diff --git a/.github/scripts/common/lib.sh b/.github/scripts/common/lib.sh index 56d0371d678ec42182e6cb84940303759ff38984..e3dd6224f29b2d7c4a0a1300e844ba45a4e7ed98 100755 --- a/.github/scripts/common/lib.sh +++ b/.github/scripts/common/lib.sh @@ -306,9 +306,10 @@ function import_gpg_keys() { EGOR="E6FC4D4782EB0FA64A4903CCDB7D3555DD3932D3" MORGAN="2E92A9D8B15D7891363D1AE8AF9E6C43F7F8C4CF" PARITY_RELEASES="90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE" + PARITY_RELEASES_SIGN_COMMITS="D8018FBB3F534D866A45998293C5FB5F6A367B51" echo "Importing GPG keys from $GPG_KEYSERVER" - for key in $SEC $EGOR $MORGAN $PARITY_RELEASES; do + for key in $SEC $EGOR $MORGAN $PARITY_RELEASES $PARITY_RELEASES_SIGN_COMMITS; do ( echo "Importing GPG key $key" gpg --no-tty --quiet --keyserver $GPG_KEYSERVER --recv-keys $key diff --git a/.github/scripts/release/build-deb.sh b/.github/scripts/release/build-deb.sh index 6cb833f98a4e48e70f297cc2be82994917f78123..8dce621bb4def00a749615d4f62ee1916d69a00c 100755 --- a/.github/scripts/release/build-deb.sh +++ b/.github/scripts/release/build-deb.sh @@ -9,8 +9,7 @@ cargo install --version 2.7.0 cargo-deb --locked -q echo "Using cargo-deb v$(cargo-deb --version)" echo "Building a Debian package for '$PRODUCT' in '$PROFILE' profile" -# we need to start the custom version with a didgit as requires it cargo-deb -cargo deb --profile $PROFILE --no-strip --no-build -p $PRODUCT --deb-version 1-$VERSION +cargo deb --profile $PROFILE --no-strip --no-build -p $PRODUCT --deb-version $VERSION deb=target/debian/$PRODUCT_*_amd64.deb diff --git a/.github/workflows/build-publish-eth-rpc.yml b/.github/workflows/build-publish-eth-rpc.yml index 9dc575cf1be20cd7c2d45673ca4864f755dff571..3aa1624096dfb848bd59a806946b02e70b13bf95 100644 --- a/.github/workflows/build-publish-eth-rpc.yml +++ b/.github/workflows/build-publish-eth-rpc.yml @@ -44,7 +44,7 @@ jobs: uses: actions/checkout@v4 - name: Build Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./substrate/frame/revive/rpc/Dockerfile @@ -70,7 +70,7 @@ jobs: password: ${{ secrets.PARITYPR_DOCKERHUB_PASSWORD }} - name: Build Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./substrate/frame/revive/rpc/Dockerfile diff --git a/.github/workflows/check-licenses.yml b/.github/workflows/check-licenses.yml index 8bd87118201ac87f34ae8c948970b5fce609741c..21e2756e8b7669f60f7615fdb4ffbfb77dcc8d08 100644 --- a/.github/workflows/check-licenses.yml +++ b/.github/workflows/check-licenses.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - - uses: actions/setup-node@v4.0.4 + - uses: actions/setup-node@v4.1.0 with: node-version: "18.x" registry-url: "https://npm.pkg.github.com" @@ -56,7 +56,7 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - - uses: actions/setup-node@v4.0.4 + - uses: actions/setup-node@v4.1.0 with: node-version: "18.x" registry-url: "https://npm.pkg.github.com" diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index 3bbb9baba46eb7c19b19c9855a70745fe63183a8..dd9d3eaf824fc9550d2851622a9705843ac8d04f 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.0 (22. Sep 2023) - name: Lychee link checker - uses: lycheeverse/lychee-action@2bb232618be239862e31382c5c0eaeba12e5e966 # for v1.9.1 (10. Jan 2024) + uses: lycheeverse/lychee-action@7cd0af4c74a61395d455af97419279d86aafaede # for v1.9.1 (10. Jan 2024) with: args: >- --config .config/lychee.toml diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index 65f3339b7ac71ad3790b69734bc0540d98b0bbc8..78602410cdf6570b1cd9b58eb08fa392a0f52c41 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -4,6 +4,7 @@ on: pull_request: types: [opened, synchronize, reopened, ready_for_review] workflow_dispatch: + merge_group: concurrency: group: check-semver-${{ github.event.pull_request.number || github.ref }} diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml index eefe36d9791d34b8f6feff3d6712dc245a785263..36deba7dfb78c56529f986e3936ace74d0baa09e 100644 --- a/.github/workflows/checks-quick.yml +++ b/.github/workflows/checks-quick.yml @@ -102,7 +102,7 @@ jobs: - name: Checkout sources uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Setup Node.js - uses: actions/setup-node@v4.0.4 + uses: actions/setup-node@v4.1.0 with: node-version: "18.x" registry-url: "https://npm.pkg.github.com" diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 0793c31dbb87c01f95abd93f624453b03b9128dd..8ec3660307d42772298e07004c51f77d71e00c33 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -32,8 +32,8 @@ jobs: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: script run: | - forklift cargo clippy --all-targets --locked --workspace --quiet - forklift cargo clippy --all-targets --all-features --locked --workspace --quiet + cargo clippy --all-targets --locked --workspace --quiet + cargo clippy --all-targets --all-features --locked --workspace --quiet check-try-runtime: runs-on: ${{ needs.preflight.outputs.RUNNER }} needs: [preflight] diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index b5db0538569b06fb5c47e6c10eadbb85118d82db..7ff0705fe249aa9899add3baad1b148091b432ff 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -83,6 +83,12 @@ jobs: homepage = "https://paritytech.github.io/polkadot-sdk/" [workspace] + EOF + + [ ${{ matrix.template }} != "solochain" ] && echo "# Leave out the node compilation from regular template usage." \ + && echo "\"default-members\" = [\"pallets/template\", \"runtime\"]" >> Cargo.toml + [ ${{ matrix.template }} == "solochain" ] && echo "# The node isn't yet replaceable by Omni Node." + cat << EOF >> Cargo.toml members = [ "node", "pallets/template", diff --git a/.github/workflows/release-10_rc-automation.yml b/.github/workflows/release-10_rc-automation.yml index 41783f6cc721b10cf69d66210223e2e746d2ab40..0be671185c70c49209517f19490b4d22b2b2d1b4 100644 --- a/.github/workflows/release-10_rc-automation.yml +++ b/.github/workflows/release-10_rc-automation.yml @@ -23,12 +23,46 @@ jobs: - name: "RelEng: Polkadot Release Coordination" room: '!cqAmzdIcbOFwrdrubV:parity.io' environment: release + env: + PGP_KMS_KEY: ${{ secrets.PGP_KMS_SIGN_COMMITS_KEY }} + PGP_KMS_HASH: ${{ secrets.PGP_KMS_HASH }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} steps: + - name: Install pgpkkms + run: | + # Install pgpkms that is used to sign commits + pip install git+https://github.com/paritytech-release/pgpkms.git@5a8f82fbb607ea102d8c178e761659de54c7af69 + + - name: Generate content write token for the release automation + id: generate_write_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.RELEASE_AUTOMATION_APP_ID }} + private-key: ${{ secrets.RELEASE_AUTOMATION_APP_PRIVATE_KEY }} + owner: paritytech + - name: Checkout sources uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 with: fetch-depth: 0 + token: ${{ steps.generate_write_token.outputs.token }} + + - name: Import gpg keys + run: | + . ./.github/scripts/common/lib.sh + + import_gpg_keys + + - name: Config git + run: | + git config --global commit.gpgsign true + git config --global gpg.program /home/runner/.local/bin/pgpkms-git + git config --global user.name "ParityReleases" + git config --global user.email "release-team@parity.io" + git config --global user.signingKey "D8018FBB3F534D866A45998293C5FB5F6A367B51" - name: Compute next rc tag # if: ${{ steps.get_rel_product.outputs.product == 'polkadot' }} @@ -58,13 +92,12 @@ jobs: fi - name: Apply new tag - uses: tvdias/github-tagger@ed7350546e3e503b5e942dffd65bc8751a95e49d # v0.0.2 - with: - # We can't use the normal GITHUB_TOKEN for the following reason: - # https://docs.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token - # RELEASE_BRANCH_TOKEN requires public_repo OAuth scope - repo-token: "${{ secrets.RELEASE_BRANCH_TOKEN }}" - tag: ${{ steps.compute_tag.outputs.new_tag }} + env: + GH_TOKEN: ${{ steps.generate_write_token.outputs.token }} + RC_TAG: ${{ steps.compute_tag.outputs.new_tag }} + run: | + git tag -s $RC_TAG -m "new rc tag $RC_TAG" + git push origin $RC_TAG - name: Send Matrix message to ${{ matrix.channel.name }} uses: s3krit/matrix-message-action@70ad3fb812ee0e45ff8999d6af11cafad11a6ecf # v0.0.3 diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index 211fa000f8f5ab313528af325b7475309622ef67..627e53bacd88ad01bef4b1b2bd49508047db166d 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -347,7 +347,7 @@ jobs: - name: Build and push id: docker_build - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 + uses: docker/build-push-action@5e99dacf67635c4f273e532b9266ddb609b3025a # v6.9.0 with: push: true file: docker/dockerfiles/polkadot/polkadot_injected_debian.Dockerfile diff --git a/.github/workflows/release-branchoff-stable.yml b/.github/workflows/release-branchoff-stable.yml index 3086c0d21f42fe3006d10bd4265be5e366278215..adce1b261b71f7f7ee6795403c4651cfab889f23 100644 --- a/.github/workflows/release-branchoff-stable.yml +++ b/.github/workflows/release-branchoff-stable.yml @@ -13,13 +13,7 @@ on: required: true jobs: - check-workflow-can-run: - uses: paritytech-release/sync-workflows/.github/workflows/check-syncronization.yml@main - - prepare-tooling: - needs: [check-workflow-can-run] - if: needs.check-workflow-can-run.outputs.checks_passed == 'true' runs-on: ubuntu-latest outputs: node_version: ${{ steps.validate_inputs.outputs.node_version }} @@ -45,7 +39,7 @@ jobs: runs-on: ubuntu-latest environment: release env: - PGP_KMS_KEY: ${{ secrets.PGP_KMS_KEY }} + PGP_KMS_KEY: ${{ secrets.PGP_KMS_SIGN_COMMITS_KEY }} PGP_KMS_HASH: ${{ secrets.PGP_KMS_HASH }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} @@ -58,10 +52,19 @@ jobs: # Install pgpkms that is used to sign commits pip install git+https://github.com/paritytech-release/pgpkms.git@5a8f82fbb607ea102d8c178e761659de54c7af69 + - name: Generate content write token for the release automation + id: generate_write_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.RELEASE_AUTOMATION_APP_ID }} + private-key: ${{ secrets.RELEASE_AUTOMATION_APP_PRIVATE_KEY }} + owner: paritytech + - name: Checkout sources uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 with: ref: master + token: ${{ steps.generate_write_token.outputs.token }} - name: Import gpg keys run: | @@ -69,14 +72,13 @@ jobs: import_gpg_keys - - name: Config git run: | git config --global commit.gpgsign true git config --global gpg.program /home/runner/.local/bin/pgpkms-git git config --global user.name "ParityReleases" git config --global user.email "release-team@parity.io" - git config --global user.signingKey "90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE" + git config --global user.signingKey "D8018FBB3F534D866A45998293C5FB5F6A367B51" - name: Create stable branch run: | @@ -84,6 +86,8 @@ jobs: git show-ref "$STABLE_BRANCH_NAME" - name: Bump versions, reorder prdocs and push stable branch + env: + GH_TOKEN: ${{ steps.generate_write_token.outputs.token }} run: | . ./.github/scripts/release/release_lib.sh @@ -101,4 +105,6 @@ jobs: reorder_prdocs $STABLE_BRANCH_NAME + gh auth setup-git + git push origin "$STABLE_BRANCH_NAME" diff --git a/.github/workflows/release-build-rc.yml b/.github/workflows/release-build-rc.yml index 5c25e3c749b8b60c72634588fbf0e0d91e99b262..94bacf320898a2a39e4f3461442572fc75fd6715 100644 --- a/.github/workflows/release-build-rc.yml +++ b/.github/workflows/release-build-rc.yml @@ -55,6 +55,10 @@ jobs: AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} AWS_RELEASE_ACCESS_KEY_ID: ${{ secrets.AWS_RELEASE_ACCESS_KEY_ID }} AWS_RELEASE_SECRET_ACCESS_KEY: ${{ secrets.AWS_RELEASE_SECRET_ACCESS_KEY }} + permissions: + id-token: write + attestations: write + contents: read build-polkadot-parachain-binary: needs: [validate-inputs] @@ -72,3 +76,7 @@ jobs: AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} AWS_RELEASE_ACCESS_KEY_ID: ${{ secrets.AWS_RELEASE_ACCESS_KEY_ID }} AWS_RELEASE_SECRET_ACCESS_KEY: ${{ secrets.AWS_RELEASE_SECRET_ACCESS_KEY }} + permissions: + id-token: write + attestations: write + contents: read diff --git a/.github/workflows/release-reusable-rc-buid.yml b/.github/workflows/release-reusable-rc-buid.yml index ae6c430b6d376d6d5e57d577ad77123ae156050e..d925839fb84a06f96216b0e498078bdfde5a9613 100644 --- a/.github/workflows/release-reusable-rc-buid.yml +++ b/.github/workflows/release-reusable-rc-buid.yml @@ -58,7 +58,7 @@ jobs: build-rc: needs: [set-image] - runs-on: ubuntu-latest + runs-on: ubuntu-latest-m environment: release container: image: ${{ needs.set-image.outputs.IMAGE }} @@ -151,7 +151,9 @@ jobs: - name: Build polkadot deb package shell: bash run: | - . "${GITHUB_WORKSPACE}"/.github/scripts/release/build-deb.sh ${{ inputs.package }} ${{ inputs.release_tag }} + . "${GITHUB_WORKSPACE}"/.github/scripts/common/lib.sh + VERSION=$(get_polkadot_node_version_from_code) + . "${GITHUB_WORKSPACE}"/.github/scripts/release/build-deb.sh ${{ inputs.package }} ${VERSION} - name: Generate artifact attestation uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3 diff --git a/.gitignore b/.gitignore index afa9ed33f4a0369b47c7a5c519965249cb3f6859..d4828765708579155865b6fe7615c19fa6d300cc 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,6 @@ artifacts bin/node-template/Cargo.lock nohup.out polkadot_argument_parsing -polkadot.* !docs/sdk/src/polkadot_sdk/polkadot.rs pwasm-alloc/Cargo.lock pwasm-libc/Cargo.lock diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index 9a907d8d99462705730f5f1ccb91e5264479e357..3dab49a118e5bb03543a5beeafddd198cab8586a 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -225,7 +225,7 @@ zombienet-polkadot-functional-0015-coretime-shared-core: --local-dir="${LOCAL_DIR}/functional" --test="0015-coretime-shared-core.zndsl" -zombienet-polkadot-functional-0016-approval-voting-parallel: +.zombienet-polkadot-functional-0016-approval-voting-parallel: extends: - .zombienet-polkadot-common script: diff --git a/Cargo.lock b/Cargo.lock index 1f171ad756c031a9daa0fdf25aafd92f87846539..1e1c902df0e1beb26e37140c98207a94a4d32db5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,7 +168,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", "syn-solidity", "tiny-keccak", ] @@ -295,7 +295,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -753,7 +753,7 @@ checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", "synstructure 0.13.1", ] @@ -776,7 +776,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -973,6 +973,7 @@ dependencies = [ "sp-core 28.0.0", "sp-runtime 31.0.1", "staging-xcm", + "staging-xcm-builder", "staging-xcm-executor", "westend-system-emulated-network", "xcm-runtime-apis", @@ -1385,7 +1386,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -1402,7 +1403,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -1617,7 +1618,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3059,7 +3060,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3077,6 +3078,32 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "cmd_lib" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "371c15a3c178d0117091bd84414545309ca979555b1aad573ef591ad58818d41" +dependencies = [ + "cmd_lib_macros", + "env_logger 0.10.1", + "faccess", + "lazy_static", + "log", + "os_pipe", +] + +[[package]] +name = "cmd_lib_macros" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb844bd05be34d91eb67101329aeba9d3337094c04fd8507d821db7ebb488eaf" +dependencies = [ + "proc-macro-error2", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.87", +] + [[package]] name = "coarsetime" version = "0.1.23" @@ -4417,7 +4444,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -4973,7 +5000,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5013,7 +5040,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "scratch", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5030,7 +5057,7 @@ checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5078,7 +5105,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "strsim 0.11.1", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5100,7 +5127,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5217,7 +5244,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5228,7 +5255,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5239,7 +5266,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5347,7 +5374,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5389,18 +5416,18 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "docify" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2f138ad521dc4a2ced1a4576148a6a610b4c5923933b062a263130a6802ce" +checksum = "a772b62b1837c8f060432ddcc10b17aae1453ef17617a99bc07789252d2a5896" dependencies = [ "docify_macros", ] [[package]] name = "docify_macros" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a081e51fb188742f5a7a1164ad752121abcb22874b21e2c3b0dd040c515fdad" +checksum = "60e6be249b0a462a14784a99b19bf35a667bb5e09de611738bb7362fa4c95ff7" dependencies = [ "common-path", "derive-syn-parse", @@ -5408,7 +5435,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "regex", - "syn 2.0.82", + "syn 2.0.87", "termcolor", "toml 0.8.12", "walkdir", @@ -5640,7 +5667,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5660,7 +5687,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5671,7 +5698,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5886,7 +5913,7 @@ dependencies = [ "prettyplease", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -5899,6 +5926,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "faccess" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ae66425802d6a903e268ae1a08b8c38ba143520f227a205edf4e9c7e3e26d5" +dependencies = [ + "bitflags 1.3.2", + "libc", + "winapi", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -5958,7 +5996,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -6317,7 +6355,7 @@ dependencies = [ "quote 1.0.37", "scale-info", "sp-arithmetic 23.0.0", - "syn 2.0.82", + "syn 2.0.87", "trybuild", ] @@ -6539,7 +6577,7 @@ dependencies = [ "sp-metadata-ir 0.6.0", "sp-runtime 31.0.1", "static_assertions", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -6550,7 +6588,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -6559,7 +6597,7 @@ version = "11.0.0" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -6720,9 +6758,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -6745,9 +6783,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -6755,15 +6793,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -6773,9 +6811,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -6807,13 +6845,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -6828,15 +6866,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -6850,9 +6888,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -8348,7 +8386,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -9102,7 +9140,7 @@ dependencies = [ "proc-macro-warning 0.4.2", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -9363,9 +9401,9 @@ dependencies = [ [[package]] name = "litep2p" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ab2528b02b6dbbc3e6ec4b55ccde885647c622a315b7da45081ed2dfe4b813" +checksum = "7286b1971f85d1d60be40ef49e81c1f3b5a0d8b83cfa02ab53591cdacae22901" dependencies = [ "async-trait", "bs58", @@ -9510,7 +9548,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -9524,7 +9562,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -9535,7 +9573,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -9546,7 +9584,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -9883,7 +9921,7 @@ dependencies = [ "cfg-if", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -10488,7 +10526,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -10664,7 +10702,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -10733,6 +10771,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_pipe" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "os_str_bytes" version = "6.5.1" @@ -11436,7 +11484,7 @@ version = "18.0.0" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -12515,6 +12563,7 @@ dependencies = [ "rlp 0.6.1", "sc-cli", "sc-rpc", + "sc-rpc-api", "sc-service", "scale-info", "secp256k1", @@ -12589,7 +12638,7 @@ version = "0.1.0" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -12834,7 +12883,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "sp-runtime 31.0.1", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -13594,6 +13643,7 @@ dependencies = [ "primitive-types 0.12.2", "scale-info", "smallvec", + "snowbridge-router-primitives", "sp-api 26.0.0", "sp-block-builder", "sp-consensus-aura", @@ -13851,7 +13901,7 @@ dependencies = [ "pest_meta", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -13892,7 +13942,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -14299,6 +14349,7 @@ dependencies = [ "polkadot-primitives", "polkadot-primitives-test-helpers", "rstest", + "schnellru", "sp-core 28.0.0", "sp-keyring", "sp-maybe-compressed-blob 11.0.0", @@ -14646,6 +14697,7 @@ dependencies = [ "polkadot-node-metrics", "polkadot-node-primitives", "polkadot-node-subsystem", + "polkadot-node-subsystem-test-helpers", "polkadot-parachain-primitives", "polkadot-primitives", "procfs", @@ -14909,7 +14961,6 @@ dependencies = [ "sp-blockchain", "sp-consensus-babe", "sp-runtime 31.0.1", - "strum 0.26.3", "substrate-prometheus-endpoint", "thiserror", ] @@ -15139,6 +15190,7 @@ dependencies = [ "sp-runtime 31.0.1", "sp-staking", "sp-std 14.0.0", + "thiserror", ] [[package]] @@ -15942,7 +15994,6 @@ dependencies = [ "sc-transaction-pool-api", "serde", "serde_json", - "serial_test", "sp-api 26.0.0", "sp-authority-discovery", "sp-block-builder", @@ -15997,6 +16048,7 @@ dependencies = [ "polkadot-primitives-test-helpers", "polkadot-subsystem-bench", "rand_chacha", + "rstest", "sc-keystore", "sc-network", "sp-application-crypto 30.0.0", @@ -16407,7 +16459,7 @@ dependencies = [ "polkavm-common 0.8.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16419,7 +16471,7 @@ dependencies = [ "polkavm-common 0.9.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16431,7 +16483,7 @@ dependencies = [ "polkavm-common 0.14.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16441,7 +16493,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15e85319a0d5129dc9f021c62607e0804f5fb777a05cdda44d750ac0732def66" dependencies = [ "polkavm-derive-impl 0.8.0", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16451,7 +16503,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl 0.9.0", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16461,7 +16513,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b569754b15060d03000c09e3bf11509d527f60b75d79b4c30c3625b5071d9702" dependencies = [ "polkavm-derive-impl 0.14.0", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16678,7 +16730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2 1.0.86", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16769,6 +16821,28 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.87", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -16783,7 +16857,7 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16794,7 +16868,7 @@ checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16875,7 +16949,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16957,7 +17031,7 @@ dependencies = [ "prost 0.13.2", "prost-types", "regex", - "syn 2.0.82", + "syn 2.0.87", "tempfile", ] @@ -16984,7 +17058,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -16997,7 +17071,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -17457,7 +17531,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -18077,7 +18151,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.0", - "syn 2.0.82", + "syn 2.0.87", "unicode-ident", ] @@ -18618,7 +18692,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -19319,6 +19393,7 @@ dependencies = [ "asynchronous-codec", "bytes", "cid 0.9.0", + "criterion", "either", "fnv", "futures", @@ -19340,6 +19415,7 @@ dependencies = [ "rand", "sc-block-builder", "sc-client-api", + "sc-consensus", "sc-network-common", "sc-network-light", "sc-network-sync", @@ -19983,7 +20059,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -20166,7 +20242,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "scale-info", - "syn 2.0.82", + "syn 2.0.87", "thiserror", ] @@ -20445,9 +20521,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] @@ -20482,13 +20558,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -20568,31 +20644,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serial_test" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" -dependencies = [ - "dashmap", - "futures", - "lazy_static", - "log", - "parking_lot 0.12.3", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" -dependencies = [ - "proc-macro2 1.0.86", - "quote 1.0.37", - "syn 2.0.82", -] - [[package]] name = "sha-1" version = "0.9.8" @@ -21561,7 +21612,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -21576,7 +21627,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -22186,7 +22237,7 @@ version = "0.1.0" dependencies = [ "quote 1.0.37", "sp-crypto-hashing 0.1.0", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -22197,7 +22248,7 @@ checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b" dependencies = [ "quote 1.0.37", "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -22215,7 +22266,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf5 dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -22224,7 +22275,7 @@ version = "14.0.0" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -22235,7 +22286,7 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -22797,7 +22848,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -22809,7 +22860,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -22823,7 +22874,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -23298,7 +23349,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "sp-version 29.0.0", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -23310,7 +23361,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -23491,6 +23542,8 @@ name = "staging-chain-spec-builder" version = "1.6.1" dependencies = [ "clap 4.5.13", + "cmd_lib", + "docify", "log", "sc-chain-spec", "serde", @@ -23579,6 +23632,7 @@ dependencies = [ "bounded-collections", "derivative", "environmental", + "frame-support", "hex", "hex-literal", "impl-trait-for-tuples", @@ -23777,7 +23831,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "rustversion", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -23790,7 +23844,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "rustversion", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -24279,7 +24333,7 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.82", + "syn 2.0.87", "thiserror", "tokio", ] @@ -24342,7 +24396,7 @@ dependencies = [ "quote 1.0.37", "scale-typegen", "subxt-codegen", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -24497,9 +24551,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.82" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", @@ -24515,7 +24569,7 @@ dependencies = [ "paste", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -24544,7 +24598,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -24674,7 +24728,7 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -24849,7 +24903,7 @@ checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -25011,7 +25065,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -25287,7 +25341,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -25329,7 +25383,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -25981,7 +26035,7 @@ dependencies = [ "once_cell", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -26015,7 +26069,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -26742,7 +26796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core 0.52.0", - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -26760,7 +26814,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -26787,7 +26841,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -26822,17 +26885,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -26849,9 +26913,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -26867,9 +26931,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -26885,9 +26949,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -26903,9 +26973,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -26921,9 +26991,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -26939,9 +27009,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -26957,9 +27027,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -27151,7 +27221,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "staging-xcm", - "syn 2.0.82", + "syn 2.0.87", "trybuild", ] @@ -27322,7 +27392,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -27342,7 +27412,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e451529431ba74f0938eccd19f1522b2000799cf..c00276724333330b8401558d2669427f32b60a9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -556,7 +556,13 @@ default-members = [ [workspace.lints.rust] suspicious_double_ref_op = { level = "allow", priority = 2 } # `substrate_runtime` is a common `cfg` condition name used in the repo. -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(build_opt_level, values("3"))', 'cfg(build_profile, values("debug", "release"))', 'cfg(enable_alloc_error_handler)', 'cfg(fuzzing)', 'cfg(substrate_runtime)'] } +unexpected_cfgs = { level = "warn", check-cfg = [ + 'cfg(build_opt_level, values("3"))', + 'cfg(build_profile, values("debug", "release"))', + 'cfg(enable_alloc_error_handler)', + 'cfg(fuzzing)', + 'cfg(substrate_runtime)', +] } [workspace.lints.clippy] all = { level = "allow", priority = 0 } @@ -677,6 +683,7 @@ cid = { version = "0.9.0" } clap = { version = "4.5.13" } clap-num = { version = "1.0.2" } clap_complete = { version = "4.5.13" } +cmd_lib = { version = "1.9.5" } coarsetime = { version = "0.1.22" } codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } collectives-westend-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend" } @@ -735,7 +742,7 @@ derive_more = { version = "0.99.17", default-features = false } digest = { version = "0.10.3", default-features = false } directories = { version = "5.0.1" } dlmalloc = { version = "0.2.4" } -docify = { version = "0.2.8" } +docify = { version = "0.2.9" } dyn-clonable = { version = "0.9.0" } dyn-clone = { version = "1.0.16" } ed25519-dalek = { version = "2.1", default-features = false } @@ -785,7 +792,7 @@ frame-system-rpc-runtime-api = { path = "substrate/frame/system/rpc/runtime-api" frame-try-runtime = { path = "substrate/frame/try-runtime", default-features = false } fs4 = { version = "0.7.0" } fs_extra = { version = "1.3.0" } -futures = { version = "0.3.30" } +futures = { version = "0.3.31" } futures-channel = { version = "0.3.23" } futures-timer = { version = "3.0.2" } futures-util = { version = "0.3.30", default-features = false } @@ -841,7 +848,7 @@ linked-hash-map = { version = "0.5.4" } linked_hash_set = { version = "0.1.4" } linregress = { version = "0.5.1" } lite-json = { version = "0.2.0", default-features = false } -litep2p = { version = "0.7.0", features = ["websocket"] } +litep2p = { version = "0.8.0", features = ["websocket"] } log = { version = "0.4.22", default-features = false } macro_magic = { version = "0.5.1" } maplit = { version = "1.0.2" } @@ -1087,7 +1094,9 @@ polkavm-derive = "0.9.1" polkavm-linker = "0.9.2" portpicker = { version = "0.1.1" } pretty_assertions = { version = "1.3.0" } -primitive-types = { version = "0.13.1", default-features = false, features = ["num-traits"] } +primitive-types = { version = "0.13.1", default-features = false, features = [ + "num-traits", +] } proc-macro-crate = { version = "3.0.0" } proc-macro-warning = { version = "1.0.0", default-features = false } proc-macro2 = { version = "1.0.86" } @@ -1196,12 +1205,11 @@ seccompiler = { version = "0.4.0" } secp256k1 = { version = "0.28.0", default-features = false } secrecy = { version = "0.8.0", default-features = false } separator = { version = "0.4.1" } -serde = { version = "1.0.210", default-features = false } +serde = { version = "1.0.214", default-features = false } serde-big-array = { version = "0.3.2" } serde_derive = { version = "1.0.117" } serde_json = { version = "1.0.132", default-features = false } serde_yaml = { version = "0.9" } -serial_test = { version = "2.0.0" } sha1 = { version = "0.10.6" } sha2 = { version = "0.10.7", default-features = false } sha3 = { version = "0.10.0", default-features = false } @@ -1310,7 +1318,7 @@ substrate-test-utils = { path = "substrate/test-utils" } substrate-wasm-builder = { path = "substrate/utils/wasm-builder", default-features = false } subxt = { version = "0.37", default-features = false } subxt-signer = { version = "0.37" } -syn = { version = "2.0.82" } +syn = { version = "2.0.87" } sysinfo = { version = "0.30" } tar = { version = "0.4" } tempfile = { version = "3.8.1" } diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index bb265e1925a20fbfb7b4f6ea480b2145ca75cc4f..095572883920fce371536d8575df77b514a4b148 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/mock.rs @@ -141,8 +141,8 @@ impl InspectMessageQueues for TestToBridgeHubSender { .iter() .map(|(location, message)| { ( - VersionedLocation::V4(location.clone()), - vec![VersionedXcm::V4(message.clone())], + VersionedLocation::from(location.clone()), + vec![VersionedXcm::from(message.clone())], ) }) .collect() diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index 6511b9fc5b04e6da2052853c49df716ed3b13ac9..9f06b99ef6d56c20afa298291e6764e5a422fd10 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -38,7 +38,7 @@ use sp_runtime::{ AccountId32, BuildStorage, StateVersion, }; use sp_std::cell::RefCell; -use xcm::prelude::*; +use xcm::{latest::ROCOCO_GENESIS_HASH, prelude::*}; use xcm_builder::{ AllowUnpaidExecutionFrom, DispatchBlob, DispatchBlobError, FixedWeightBounds, InspectMessageQueues, NetworkExportTable, NetworkExportTableItem, ParentIsPreset, @@ -160,7 +160,7 @@ parameter_types! { pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into(); pub BridgedRelativeDestination: InteriorLocation = [Parachain(BRIDGED_ASSET_HUB_ID)].into(); pub BridgedUniversalDestination: InteriorLocation = [GlobalConsensus(BridgedRelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)].into(); - pub const NonBridgedRelayNetwork: NetworkId = NetworkId::Rococo; + pub const NonBridgedRelayNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); pub const BridgeDeposit: Balance = 100_000; diff --git a/bridges/primitives/messages/src/lane.rs b/bridges/primitives/messages/src/lane.rs index 0f14ce93e11425ff8d2dd69165159ed203e72f03..75237a44d5385ace12db087a3258818f763496cd 100644 --- a/bridges/primitives/messages/src/lane.rs +++ b/bridges/primitives/messages/src/lane.rs @@ -108,8 +108,8 @@ impl TypeId for LegacyLaneId { /// concatenation (separated by some binary data). I.e.: /// /// ```nocompile -/// let endpoint1 = X2(GlobalConsensus(NetworkId::Rococo), Parachain(42)); -/// let endpoint2 = X2(GlobalConsensus(NetworkId::Wococo), Parachain(777)); +/// let endpoint1 = X2(GlobalConsensus(NetworkId::Polkadot), Parachain(42)); +/// let endpoint2 = X2(GlobalConsensus(NetworkId::Kusama), Parachain(777)); /// /// let final_lane_key = if endpoint1 < endpoint2 { /// (endpoint1, VALUES_SEPARATOR, endpoint2) diff --git a/bridges/primitives/xcm-bridge-hub/src/lib.rs b/bridges/primitives/xcm-bridge-hub/src/lib.rs index 061e7a27506329029da1e62f7ddd56f516b5dd80..63beb1bc30410c4ba84334953b0e302d4e2e1406 100644 --- a/bridges/primitives/xcm-bridge-hub/src/lib.rs +++ b/bridges/primitives/xcm-bridge-hub/src/lib.rs @@ -359,10 +359,11 @@ impl BridgeLocations { #[cfg(test)] mod tests { use super::*; + use xcm::latest::ROCOCO_GENESIS_HASH; const LOCAL_NETWORK: NetworkId = Kusama; const REMOTE_NETWORK: NetworkId = Polkadot; - const UNREACHABLE_NETWORK: NetworkId = Rococo; + const UNREACHABLE_NETWORK: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); const SIBLING_PARACHAIN: u32 = 1000; const LOCAL_BRIDGE_HUB: u32 = 1001; const REMOTE_PARACHAIN: u32 = 2000; diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index 3e67d5ab738b1a1a492667ceb955c5508292dac3..675d4b6915937ff7129dcbb5c8264b155edd31f2 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -19,7 +19,10 @@ use sp_runtime::{ BuildStorage, FixedU128, MultiSignature, }; use sp_std::{convert::From, default::Default}; -use xcm::{latest::SendXcm, prelude::*}; +use xcm::{ + latest::{SendXcm, WESTEND_GENESIS_HASH}, + prelude::*, +}; use xcm_executor::AssetsInHolding; use crate::{self as inbound_queue}; @@ -113,8 +116,8 @@ parameter_types! { pub const InitialFund: u128 = 1_000_000_000_000; pub const InboundQueuePalletInstance: u8 = 80; pub UniversalLocation: InteriorLocation = - [GlobalConsensus(Westend), Parachain(1002)].into(); - pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); + [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1002)].into(); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)),Parachain(1000)]); } #[cfg(feature = "runtime-benchmarks")] diff --git a/bridges/snowbridge/pallets/inbound-queue/src/test.rs b/bridges/snowbridge/pallets/inbound-queue/src/test.rs index 41c38460aabf1c796d743f0ee9de135d3c9e9ef0..76d0b98e9eb4632f50b72821642f8b57cb189832 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/test.rs @@ -40,8 +40,8 @@ fn test_submit_happy_path() { .into(), nonce: 1, message_id: [ - 255, 125, 48, 71, 174, 185, 100, 26, 159, 43, 108, 6, 116, 218, 55, 155, 223, 143, - 141, 22, 124, 110, 241, 18, 122, 217, 130, 29, 139, 76, 97, 201, + 11, 25, 133, 51, 23, 68, 111, 211, 132, 94, 254, 17, 194, 252, 198, 233, 10, 193, + 156, 93, 72, 140, 65, 69, 79, 155, 154, 28, 141, 166, 171, 255, ], fee_burned: 110000000000, } diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index 20798b7c349389815b96eecc3d3f9887337f5fce..939de9d40d131efc17cf322d90d072913718553f 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -169,7 +169,7 @@ mod benchmarks { T::Token::mint_into(&caller, amount)?; let relay_token_asset_id: Location = Location::parent(); - let asset = Box::new(VersionedLocation::V4(relay_token_asset_id)); + let asset = Box::new(VersionedLocation::from(relay_token_asset_id)); let asset_metadata = AssetMetadata { name: "wnd".as_bytes().to_vec().try_into().unwrap(), symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 1e8a788b7a5a8979e66f9e0003a1fc169f6c398d..eb3da095fe85560164ca81ea2a97d0b88d0c92c4 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -269,12 +269,12 @@ pub mod pallet { /// Lookup table for foreign token ID to native location relative to ethereum #[pallet::storage] pub type ForeignToNativeId = - StorageMap<_, Blake2_128Concat, TokenId, xcm::v4::Location, OptionQuery>; + StorageMap<_, Blake2_128Concat, TokenId, xcm::v5::Location, OptionQuery>; /// Lookup table for native location relative to ethereum to foreign token ID #[pallet::storage] pub type NativeToForeignId = - StorageMap<_, Blake2_128Concat, xcm::v4::Location, TokenId, OptionQuery>; + StorageMap<_, Blake2_128Concat, xcm::v5::Location, TokenId, OptionQuery>; #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index aad1c9ece05c3f7097a08ee243bad0ccec60010d..f49a245c4126f410994390908a81b6d55e613b15 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -97,9 +97,12 @@ impl DescribeLocation for DescribeTokenTerminal { #[cfg(test)] mod tests { use crate::TokenIdOf; - use xcm::prelude::{ - GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::*, - PalletInstance, Parachain, + use xcm::{ + latest::WESTEND_GENESIS_HASH, + prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::ByGenesis, + PalletInstance, Parachain, + }, }; use xcm_executor::traits::ConvertLocation; @@ -108,17 +111,24 @@ mod tests { let token_locations = [ // Relay Chain cases // Relay Chain relative to Ethereum - Location::new(1, [GlobalConsensus(Westend)]), + Location::new(1, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))]), // Parachain cases // Parachain relative to Ethereum - Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]), + Location::new(1, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(2000)]), // Parachain general index - Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]), + Location::new( + 1, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(2000), + GeneralIndex(1), + ], + ), // Parachain general key Location::new( 1, [ - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(2000), GeneralKey { length: 32, data: [0; 32] }, ], @@ -127,7 +137,7 @@ mod tests { Location::new( 1, [ - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(2000), AccountKey20 { network: None, key: [0; 20] }, ], @@ -136,24 +146,36 @@ mod tests { Location::new( 1, [ - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(2000), AccountId32 { network: None, id: [0; 32] }, ], ), // Parchain Pallet instance cases // Parachain pallet instance - Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]), + Location::new( + 1, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(2000), + PalletInstance(8), + ], + ), // Parachain Pallet general index Location::new( 1, - [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8), GeneralIndex(1)], + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(2000), + PalletInstance(8), + GeneralIndex(1), + ], ), // Parachain Pallet general key Location::new( 1, [ - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(2000), PalletInstance(8), GeneralKey { length: 32, data: [0; 32] }, @@ -163,7 +185,7 @@ mod tests { Location::new( 1, [ - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(2000), PalletInstance(8), AccountKey20 { network: None, key: [0; 20] }, @@ -173,7 +195,7 @@ mod tests { Location::new( 1, [ - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(2000), PalletInstance(8), AccountId32 { network: None, id: [0; 32] }, diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index a9324ac42470bf627e586e22c5f8984b8e3bfc5d..e03560f66e244b3d939ae3088e82866662082fa6 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -7,7 +7,7 @@ mod tests; use codec::{Decode, Encode}; use core::marker::PhantomData; -use frame_support::{traits::tokens::Balance as BalanceT, weights::Weight, PalletError}; +use frame_support::{traits::tokens::Balance as BalanceT, PalletError}; use scale_info::TypeInfo; use snowbridge_core::TokenId; use sp_core::{Get, RuntimeDebug, H160, H256}; @@ -253,7 +253,7 @@ where let bridge_location = Location::new(2, GlobalConsensus(network)); - let owner = GlobalConsensusEthereumConvertsFor::<[u8; 32]>::from_chain_id(&chain_id); + let owner = EthereumLocationsConverterFor::<[u8; 32]>::from_chain_id(&chain_id); let asset_id = Self::convert_token_address(network, token); let create_call_index: [u8; 2] = CreateAssetCall::get(); let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); @@ -279,7 +279,6 @@ where // Call create_asset on foreign assets pallet. Transact { origin_kind: OriginKind::Xcm, - require_weight_at_most: Weight::from_parts(400_000_000, 8_000), call: ( create_call_index, asset_id, @@ -454,22 +453,27 @@ where } } -pub struct GlobalConsensusEthereumConvertsFor(PhantomData); -impl ConvertLocation for GlobalConsensusEthereumConvertsFor +pub struct EthereumLocationsConverterFor(PhantomData); +impl ConvertLocation for EthereumLocationsConverterFor where AccountId: From<[u8; 32]> + Clone, { fn convert_location(location: &Location) -> Option { match location.unpack() { - (_, [GlobalConsensus(Ethereum { chain_id })]) => + (2, [GlobalConsensus(Ethereum { chain_id })]) => Some(Self::from_chain_id(chain_id).into()), + (2, [GlobalConsensus(Ethereum { chain_id }), AccountKey20 { network: _, key }]) => + Some(Self::from_chain_id_with_key(chain_id, *key).into()), _ => None, } } } -impl GlobalConsensusEthereumConvertsFor { +impl EthereumLocationsConverterFor { pub fn from_chain_id(chain_id: &u64) -> [u8; 32] { (b"ethereum-chain", chain_id).using_encoded(blake2_256) } + pub fn from_chain_id_with_key(chain_id: &u64, key: [u8; 20]) -> [u8; 32] { + (b"ethereum-chain", chain_id, key).using_encoded(blake2_256) + } } diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index e0e90e516be166b0dadd06b158c035e1f12e9d30..786aa594f653eec5e160a0a15ed4df638da8e728 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -1,4 +1,4 @@ -use super::GlobalConsensusEthereumConvertsFor; +use super::EthereumLocationsConverterFor; use crate::inbound::CallIndex; use frame_support::{assert_ok, parameter_types}; use hex_literal::hex; @@ -17,14 +17,28 @@ parameter_types! { } #[test] -fn test_contract_location_with_network_converts_successfully() { +fn test_ethereum_network_converts_successfully() { let expected_account: [u8; 32] = hex!("ce796ae65569a670d0c1cc1ac12515a3ce21b5fbf729d63d7b289baad070139d"); let contract_location = Location::new(2, [GlobalConsensus(NETWORK)]); let account = - GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&contract_location) - .unwrap(); + EthereumLocationsConverterFor::<[u8; 32]>::convert_location(&contract_location).unwrap(); + + assert_eq!(account, expected_account); +} + +#[test] +fn test_contract_location_with_network_converts_successfully() { + let expected_account: [u8; 32] = + hex!("9038d35aba0e78e072d29b2d65be9df5bb4d7d94b4609c9cf98ea8e66e544052"); + let contract_location = Location::new( + 2, + [GlobalConsensus(NETWORK), AccountKey20 { network: None, key: [123u8; 20] }], + ); + + let account = + EthereumLocationsConverterFor::<[u8; 32]>::convert_location(&contract_location).unwrap(); assert_eq!(account, expected_account); } @@ -34,7 +48,7 @@ fn test_contract_location_with_incorrect_location_fails_convert() { let contract_location = Location::new(2, [GlobalConsensus(Polkadot), Parachain(1000)]); assert_eq!( - GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&contract_location), + EthereumLocationsConverterFor::<[u8; 32]>::convert_location(&contract_location), None, ); } diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 8bd3fa24df5bff9290b4a7a87afaf90f1947d2f8..44f81ce31b3a8f4761a68fd5ca2496a5d79320bf 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -5,7 +5,10 @@ use snowbridge_core::{ AgentIdOf, }; use sp_std::default::Default; -use xcm::prelude::SendError as XcmSendError; +use xcm::{ + latest::{ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}, + prelude::SendError as XcmSendError, +}; use super::*; @@ -61,7 +64,7 @@ impl SendMessageFeeProvider for MockErrOutboundQueue { pub struct MockTokenIdConvert; impl MaybeEquivalence for MockTokenIdConvert { fn convert(_id: &TokenId) -> Option { - Some(Location::new(1, [GlobalConsensus(Westend)])) + Some(Location::new(1, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))])) } fn convert_back(_loc: &Location) -> Option { None @@ -1109,7 +1112,7 @@ fn xcm_converter_transfer_native_token_success() { let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); let amount = 1000000; - let asset_location = Location::new(1, [GlobalConsensus(Westend)]); + let asset_location = Location::new(1, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))]); let token_id = TokenIdOf::convert_location(&asset_location).unwrap(); let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); @@ -1142,7 +1145,8 @@ fn xcm_converter_transfer_native_token_with_invalid_location_will_fail() { let amount = 1000000; // Invalid asset location from a different consensus - let asset_location = Location { parents: 2, interior: [GlobalConsensus(Rococo)].into() }; + let asset_location = + Location { parents: 2, interior: [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH))].into() }; let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); let filter: AssetFilter = assets.clone().into(); @@ -1221,7 +1225,8 @@ fn exporter_validate_with_invalid_universal_source_does_not_alter_universal_sour let network = BridgedNetwork::get(); let destination: InteriorLocation = Here.into(); - let universal_source: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + let universal_source: InteriorLocation = + [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000)].into(); let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); diff --git a/bridges/snowbridge/runtime/test-common/src/lib.rs b/bridges/snowbridge/runtime/test-common/src/lib.rs index b157ad4356bdf5769119dd52b0fe027d72fab2e1..dca5062ab31094d0ab9a7ea060fce58c13f9bb98 100644 --- a/bridges/snowbridge/runtime/test-common/src/lib.rs +++ b/bridges/snowbridge/runtime/test-common/src/lib.rs @@ -15,10 +15,7 @@ use snowbridge_pallet_ethereum_client_fixtures::*; use sp_core::{Get, H160, U256}; use sp_keyring::AccountKeyring::*; use sp_runtime::{traits::Header, AccountId32, DigestItem, SaturatedConversion, Saturating}; -use xcm::{ - latest::prelude::*, - v3::Error::{self, Barrier}, -}; +use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; type RuntimeHelper = @@ -374,7 +371,7 @@ pub fn send_unpaid_transfer_token_message( Weight::zero(), ); // check error is barrier - assert_err!(outcome.ensure_complete(), Barrier); + assert_err!(outcome.ensure_complete(), XcmError::Barrier); }); } @@ -388,7 +385,7 @@ pub fn send_transfer_token_message_failure( weth_contract_address: H160, destination_address: H160, fee_amount: u128, - expected_error: Error, + expected_error: XcmError, ) where Runtime: frame_system::Config + pallet_balances::Config diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs index 8ac43fbd116e5f14f9c872408cb95bf7604987cd..2dbcf5eb58e96b42fa988380cb3ebe6a1654edee 100644 --- a/cumulus/client/consensus/aura/src/collators/lookahead.rs +++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs @@ -36,17 +36,15 @@ use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterfa use cumulus_client_consensus_common::{self as consensus_common, ParachainBlockImportMarker}; use cumulus_client_consensus_proposer::ProposerInterface; use cumulus_primitives_aura::AuraUnincludedSegmentApi; -use cumulus_primitives_core::{ - ClaimQueueOffset, CollectCollationInfo, PersistedValidationData, DEFAULT_CLAIM_QUEUE_OFFSET, -}; +use cumulus_primitives_core::{ClaimQueueOffset, CollectCollationInfo, PersistedValidationData}; use cumulus_relay_chain_interface::RelayChainInterface; use polkadot_node_primitives::{PoV, SubmitCollationParams}; use polkadot_node_subsystem::messages::CollationGenerationMessage; use polkadot_overseer::Handle as OverseerHandle; use polkadot_primitives::{ - BlockNumber as RBlockNumber, CollatorPair, Hash as RHash, HeadData, Id as ParaId, - OccupiedCoreAssumption, + vstaging::DEFAULT_CLAIM_QUEUE_OFFSET, BlockNumber as RBlockNumber, CollatorPair, Hash as RHash, + HeadData, Id as ParaId, OccupiedCoreAssumption, }; use futures::prelude::*; diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index e75b52aeebd34c93b26217425e0aad39a7673682..42515123070468cc4c4355c30aa8a2e1bdec0e6c 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -20,13 +20,11 @@ use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterfa use cumulus_client_consensus_common::{self as consensus_common, ParachainBlockImportMarker}; use cumulus_client_consensus_proposer::ProposerInterface; use cumulus_primitives_aura::AuraUnincludedSegmentApi; -use cumulus_primitives_core::{ - GetCoreSelectorApi, PersistedValidationData, DEFAULT_CLAIM_QUEUE_OFFSET, -}; +use cumulus_primitives_core::{GetCoreSelectorApi, PersistedValidationData}; use cumulus_relay_chain_interface::RelayChainInterface; use polkadot_primitives::{ - vstaging::{ClaimQueueOffset, CoreSelector}, + vstaging::{ClaimQueueOffset, CoreSelector, DEFAULT_CLAIM_QUEUE_OFFSET}, BlockId, CoreIndex, Hash as RelayHash, Header as RelayHeader, Id as ParaId, OccupiedCoreAssumption, }; diff --git a/cumulus/client/network/src/tests.rs b/cumulus/client/network/src/tests.rs index 009f922008b1204bebed6c5a68ad8dbb1daf6efb..cccb710bf18f1122d8675ee0bfca23e2075b3d47 100644 --- a/cumulus/client/network/src/tests.rs +++ b/cumulus/client/network/src/tests.rs @@ -321,8 +321,8 @@ impl RelayChainInterface for DummyRelayChainInterface { .to_vec(); Ok(RuntimeVersion { - spec_name: sp_version::create_runtime_str!("test"), - impl_name: sp_version::create_runtime_str!("test"), + spec_name: Cow::Borrowed("test"), + impl_name: Cow::Borrowed("test"), authoring_version: 1, spec_version: 1, impl_version: 0, diff --git a/cumulus/client/pov-recovery/src/tests.rs b/cumulus/client/pov-recovery/src/tests.rs index d528a92a52a80a033052bf0cb5b80c841aba2f21..91b462e06bf87bf9fb264e638daa77547b8ce660 100644 --- a/cumulus/client/pov-recovery/src/tests.rs +++ b/cumulus/client/pov-recovery/src/tests.rs @@ -322,8 +322,8 @@ impl RelayChainInterface for Relaychain { .to_vec(); Ok(RuntimeVersion { - spec_name: sp_version::create_runtime_str!("test"), - impl_name: sp_version::create_runtime_str!("test"), + spec_name: Cow::Borrowed("test"), + impl_name: Cow::Borrowed("test"), authoring_version: 1, spec_version: 1, impl_version: 0, diff --git a/cumulus/client/service/src/lib.rs b/cumulus/client/service/src/lib.rs index 25b8ee10a931eff6a190586ec2959e4ad53a3de2..ae83f2ade3f6adbedf82bb881db309208fcafce0 100644 --- a/cumulus/client/service/src/lib.rs +++ b/cumulus/client/service/src/lib.rs @@ -40,10 +40,7 @@ use sc_consensus::{ use sc_network::{config::SyncMode, service::traits::NetworkService, NetworkBackend}; use sc_network_sync::SyncingService; use sc_network_transactions::TransactionsHandlerController; -use sc_service::{ - build_polkadot_syncing_strategy, Configuration, NetworkStarter, SpawnTaskHandle, TaskManager, - WarpSyncConfig, -}; +use sc_service::{Configuration, NetworkStarter, SpawnTaskHandle, TaskManager, WarpSyncConfig}; use sc_telemetry::{log, TelemetryWorkerHandle}; use sc_utils::mpsc::TracingUnboundedSender; use sp_api::ProvideRuntimeApi; @@ -429,7 +426,7 @@ pub struct BuildNetworkParams< pub async fn build_network<'a, Block, Client, RCInterface, IQ, Network>( BuildNetworkParams { parachain_config, - mut net_config, + net_config, client, transaction_pool, para_id, @@ -500,16 +497,6 @@ where parachain_config.prometheus_config.as_ref().map(|config| &config.registry), ); - let syncing_strategy = build_polkadot_syncing_strategy( - parachain_config.protocol_id(), - parachain_config.chain_spec.fork_id(), - &mut net_config, - warp_sync_config, - client.clone(), - &spawn_handle, - parachain_config.prometheus_config.as_ref().map(|config| &config.registry), - )?; - sc_service::build_network(sc_service::BuildNetworkParams { config: parachain_config, net_config, @@ -518,7 +505,7 @@ where spawn_handle, import_queue, block_announce_validator_builder: Some(Box::new(move |_| block_announce_validator)), - syncing_strategy, + warp_sync_config, block_relay: None, metrics, }) diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index 98989a852b8dc5ad806564187ad93fe2d290dc83..39fc8321a072ea9380e728ce19a94a064921aa82 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -35,12 +35,12 @@ use core::{cmp, marker::PhantomData}; use cumulus_primitives_core::{ relay_chain::{ self, - vstaging::{ClaimQueueOffset, CoreSelector}, + vstaging::{ClaimQueueOffset, CoreSelector, DEFAULT_CLAIM_QUEUE_OFFSET}, }, AbridgedHostConfiguration, ChannelInfo, ChannelStatus, CollationInfo, GetChannelInfo, InboundDownwardMessage, InboundHrmpMessage, ListChannelInfos, MessageSendError, OutboundHrmpMessage, ParaId, PersistedValidationData, UpwardMessage, UpwardMessageSender, - XcmpMessageHandler, XcmpMessageSource, DEFAULT_CLAIM_QUEUE_OFFSET, + XcmpMessageHandler, XcmpMessageSource, }; use cumulus_primitives_parachain_inherent::{MessageQueueChain, ParachainInherentData}; use frame_support::{ diff --git a/cumulus/pallets/parachain-system/src/mock.rs b/cumulus/pallets/parachain-system/src/mock.rs index 1f5e4f4dbcf3c07a0e620f2a638916cd2928591b..5b59be0482e7396cddf161889ace0ca726da9fd5 100644 --- a/cumulus/pallets/parachain-system/src/mock.rs +++ b/cumulus/pallets/parachain-system/src/mock.rs @@ -57,8 +57,8 @@ frame_support::construct_runtime!( parameter_types! { pub Version: RuntimeVersion = RuntimeVersion { - spec_name: sp_version::create_runtime_str!("test"), - impl_name: sp_version::create_runtime_str!("system-test"), + spec_name: alloc::borrow::Cow::Borrowed("test"), + impl_name: alloc::borrow::Cow::Borrowed("system-test"), authoring_version: 1, spec_version: 1, impl_version: 1, diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 6bb7395f6553b2984ee2e36590dc42f6eb88785f..91f71558b54a211563015b73d634011c896f442d 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -1036,7 +1036,7 @@ impl InspectMessageQueues for Pallet { } ( - VersionedLocation::V4((Parent, Parachain(para_id.into())).into()), + VersionedLocation::from(Location::new(1, Parachain(para_id.into()))), decoded_messages, ) }) diff --git a/cumulus/pallets/xcmp-queue/src/tests.rs b/cumulus/pallets/xcmp-queue/src/tests.rs index 5b02baf2310a368c3b49269e4c594a81d47a3a01..bf042f15ccc04ba96045767e7035d32f8424fd2e 100644 --- a/cumulus/pallets/xcmp-queue/src/tests.rs +++ b/cumulus/pallets/xcmp-queue/src/tests.rs @@ -456,7 +456,7 @@ fn send_xcm_nested_works() { XcmpQueue::take_outbound_messages(usize::MAX), vec![( HRMP_PARA_ID.into(), - (XcmpMessageFormat::ConcatenatedVersionedXcm, VersionedXcm::V4(good.clone())) + (XcmpMessageFormat::ConcatenatedVersionedXcm, VersionedXcm::from(good.clone())) .encode(), )] ); @@ -512,7 +512,7 @@ fn hrmp_signals_are_prioritized() { // Without a signal we get the messages in order: let mut expected_msg = XcmpMessageFormat::ConcatenatedVersionedXcm.encode(); for _ in 0..31 { - expected_msg.extend(VersionedXcm::V4(message.clone()).encode()); + expected_msg.extend(VersionedXcm::from(message.clone()).encode()); } hypothetically!({ @@ -539,6 +539,7 @@ fn maybe_double_encoded_versioned_xcm_works() { // pre conditions assert_eq!(VersionedXcm::<()>::V3(Default::default()).encode(), &[3, 0]); assert_eq!(VersionedXcm::<()>::V4(Default::default()).encode(), &[4, 0]); + assert_eq!(VersionedXcm::<()>::V5(Default::default()).encode(), &[5, 0]); } // Now also testing a page instead of just concat messages. @@ -597,7 +598,7 @@ fn take_first_concatenated_xcm_good_recursion_depth_works() { for _ in 0..MAX_XCM_DECODE_DEPTH - 1 { good = Xcm(vec![SetAppendix(good)]); } - let good = VersionedXcm::V4(good); + let good = VersionedXcm::from(good); let page = good.encode(); assert_ok!(XcmpQueue::take_first_concatenated_xcm(&mut &page[..], &mut WeightMeter::new())); @@ -610,7 +611,7 @@ fn take_first_concatenated_xcm_good_bad_depth_errors() { for _ in 0..MAX_XCM_DECODE_DEPTH { bad = Xcm(vec![SetAppendix(bad)]); } - let bad = VersionedXcm::V4(bad); + let bad = VersionedXcm::from(bad); let page = bad.encode(); assert_err!( @@ -872,18 +873,18 @@ fn get_messages_works() { queued_messages, vec![ ( - VersionedLocation::V4(other_destination), + VersionedLocation::from(other_destination), vec![ - VersionedXcm::V4(Xcm(vec![ClearOrigin])), - VersionedXcm::V4(Xcm(vec![ClearOrigin])), + VersionedXcm::from(Xcm(vec![ClearOrigin])), + VersionedXcm::from(Xcm(vec![ClearOrigin])), ], ), ( - VersionedLocation::V4(destination), + VersionedLocation::from(destination), vec![ - VersionedXcm::V4(Xcm(vec![ClearOrigin])), - VersionedXcm::V4(Xcm(vec![ClearOrigin])), - VersionedXcm::V4(Xcm(vec![ClearOrigin])), + VersionedXcm::from(Xcm(vec![ClearOrigin])), + VersionedXcm::from(Xcm(vec![ClearOrigin])), + VersionedXcm::from(Xcm(vec![ClearOrigin])), ], ), ], diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs index 606d04060b6b4548b5bc3660c36d05448419e36a..3ffb9a704b4649c481d0537cd3eacfc0034c4365 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs @@ -20,8 +20,9 @@ use sp_keyring::Sr25519Keyring as Keyring; // Cumulus use emulated_integration_tests_common::{ - accounts, build_genesis_storage, collators, PenpalSiblingSovereignAccount, - PenpalTeleportableAssetLocation, RESERVABLE_ASSET_ID, SAFE_XCM_VERSION, USDT_ID, + accounts, build_genesis_storage, collators, PenpalASiblingSovereignAccount, + PenpalATeleportableAssetLocation, PenpalBSiblingSovereignAccount, + PenpalBTeleportableAssetLocation, RESERVABLE_ASSET_ID, SAFE_XCM_VERSION, USDT_ID, }; use parachains_common::{AccountId, Balance}; @@ -77,10 +78,17 @@ pub fn genesis() -> Storage { }, foreign_assets: asset_hub_rococo_runtime::ForeignAssetsConfig { assets: vec![ - // Penpal's teleportable asset representation + // PenpalA's teleportable asset representation ( - PenpalTeleportableAssetLocation::get(), - PenpalSiblingSovereignAccount::get(), + PenpalATeleportableAssetLocation::get(), + PenpalASiblingSovereignAccount::get(), + false, + ED, + ), + // PenpalB's teleportable asset representation + ( + PenpalBTeleportableAssetLocation::get(), + PenpalBSiblingSovereignAccount::get(), false, ED, ), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs index 75b61d6a4cd7ae2be4222874a0e7cc586c0bf98e..1a075b9fe6bea9a931f1a40c06ada4095a3205b6 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs @@ -59,7 +59,7 @@ impl_accounts_helpers_for_parachain!(AssetHubRococo); impl_assert_events_helpers_for_parachain!(AssetHubRococo); impl_assets_helpers_for_system_parachain!(AssetHubRococo, Rococo); impl_assets_helpers_for_parachain!(AssetHubRococo); -impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, xcm::v4::Location); +impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, xcm::v5::Location); impl_xcm_helpers_for_parachain!(AssetHubRococo); impl_bridge_helpers_for_chain!( AssetHubRococo, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index 30e7279a383f457fe9e27a2ec0bf96b9d9e5d03f..ef7997322da732e7fe55648c6f8c64e470cb7e82 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -20,9 +20,9 @@ use sp_keyring::Sr25519Keyring as Keyring; // Cumulus use emulated_integration_tests_common::{ - accounts, build_genesis_storage, collators, PenpalBSiblingSovereignAccount, - PenpalBTeleportableAssetLocation, PenpalSiblingSovereignAccount, - PenpalTeleportableAssetLocation, RESERVABLE_ASSET_ID, SAFE_XCM_VERSION, USDT_ID, + accounts, build_genesis_storage, collators, PenpalASiblingSovereignAccount, + PenpalATeleportableAssetLocation, PenpalBSiblingSovereignAccount, + PenpalBTeleportableAssetLocation, RESERVABLE_ASSET_ID, SAFE_XCM_VERSION, USDT_ID, }; use parachains_common::{AccountId, Balance}; @@ -75,10 +75,10 @@ pub fn genesis() -> Storage { }, foreign_assets: asset_hub_westend_runtime::ForeignAssetsConfig { assets: vec![ - // Penpal's teleportable asset representation + // PenpalA's teleportable asset representation ( - PenpalTeleportableAssetLocation::get(), - PenpalSiblingSovereignAccount::get(), + PenpalATeleportableAssetLocation::get(), + PenpalASiblingSovereignAccount::get(), false, ED, ), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs index c44f4b010c0ac9ac7d8fc9b547946c7dfd8376e5..3e240ed6748278f768f4143fe6a93db25d094cd2 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs @@ -59,7 +59,7 @@ impl_accounts_helpers_for_parachain!(AssetHubWestend); impl_assert_events_helpers_for_parachain!(AssetHubWestend); impl_assets_helpers_for_system_parachain!(AssetHubWestend, Westend); impl_assets_helpers_for_parachain!(AssetHubWestend); -impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, xcm::v4::Location); +impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, xcm::v5::Location); impl_xcm_helpers_for_parachain!(AssetHubWestend); impl_bridge_helpers_for_chain!( AssetHubWestend, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs index 0268a6a7a1b30e8b045335514fcd34972ada0411..575017f88bb590159fe7df9f7e6d5e3ea096f269 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs @@ -22,7 +22,7 @@ use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, SAFE_XCM_VERSION, }; use parachains_common::Balance; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; pub const ASSETHUB_PARA_ID: u32 = 1000; pub const PARA_ID: u32 = 1013; @@ -73,7 +73,7 @@ pub fn genesis() -> Storage { // open AHR -> AHW bridge ( Location::new(1, [Parachain(1000)]), - Junctions::from([Westend.into(), Parachain(1000)]), + Junctions::from([ByGenesis(WESTEND_GENESIS_HASH).into(), Parachain(1000)]), Some(bp_messages::LegacyLaneId([0, 0, 0, 2])), ), ], diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs index f72eaa30026db442b8b338cf04a5de314e91fd96..eb4623084f85ed016716efbeb655b1147f57e020 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs @@ -22,7 +22,7 @@ use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, SAFE_XCM_VERSION, }; use parachains_common::Balance; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH}; pub const PARA_ID: u32 = 1002; pub const ASSETHUB_PARA_ID: u32 = 1000; @@ -73,7 +73,10 @@ pub fn genesis() -> Storage { // open AHW -> AHR bridge ( Location::new(1, [Parachain(1000)]), - Junctions::from([Rococo.into(), Parachain(1000)]), + Junctions::from([ + NetworkId::ByGenesis(ROCOCO_GENESIS_HASH).into(), + Parachain(1000), + ]), Some(bp_messages::LegacyLaneId([0, 0, 0, 2])), ), ], diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs index e7a28ebf4a4690dcb0930e371433e8e35fd6fc8d..b548e3b7e64c3932cfcc654abdf52308ea120306 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs @@ -16,7 +16,7 @@ pub mod genesis; pub use bridge_hub_westend_runtime::{ - xcm_config::XcmConfig as BridgeHubWestendXcmConfig, + self, xcm_config::XcmConfig as BridgeHubWestendXcmConfig, ExistentialDeposit as BridgeHubWestendExistentialDeposit, }; diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs index 92dfa30f2e839074157b2fe9a29220ba7d8fd53e..f5642dbb0daa4d5345f5e8e6b0c28ae972a8fa13 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs @@ -31,6 +31,9 @@ use emulated_integration_tests_common::{ xcm_emulator::decl_test_parachains, }; +// Polkadot +use xcm::latest::{ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}; + // Penpal Parachain declaration decl_test_parachains! { pub struct PenpalA { @@ -39,7 +42,7 @@ decl_test_parachains! { penpal_runtime::AuraExt::on_initialize(1); frame_support::assert_ok!(penpal_runtime::System::set_storage( penpal_runtime::RuntimeOrigin::root(), - vec![(PenpalRelayNetworkId::key().to_vec(), NetworkId::Rococo.encode())], + vec![(PenpalRelayNetworkId::key().to_vec(), NetworkId::ByGenesis(ROCOCO_GENESIS_HASH).encode())], )); }, runtime = penpal_runtime, @@ -63,7 +66,7 @@ decl_test_parachains! { penpal_runtime::AuraExt::on_initialize(1); frame_support::assert_ok!(penpal_runtime::System::set_storage( penpal_runtime::RuntimeOrigin::root(), - vec![(PenpalRelayNetworkId::key().to_vec(), NetworkId::Westend.encode())], + vec![(PenpalRelayNetworkId::key().to_vec(), NetworkId::ByGenesis(WESTEND_GENESIS_HASH).encode())], )); }, runtime = penpal_runtime, diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index 07fde111d3dc9df67e79c7a11499059e3a5dc33b..e2757f8b9a35b7ba07067ac708fe37babdd16af4 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -41,6 +41,7 @@ use polkadot_primitives::{AssignmentId, ValidatorId}; pub const XCM_V2: u32 = 2; pub const XCM_V3: u32 = 3; pub const XCM_V4: u32 = 4; +pub const XCM_V5: u32 = 5; pub const REF_TIME_THRESHOLD: u64 = 33; pub const PROOF_SIZE_THRESHOLD: u64 = 33; @@ -55,26 +56,26 @@ pub const TELEPORTABLE_ASSET_ID: u32 = 2; // USDT registered on AH as (trust-backed) Asset and reserve-transferred between Parachain and AH pub const USDT_ID: u32 = 1984; -pub const PENPAL_ID: u32 = 2000; +pub const PENPAL_A_ID: u32 = 2000; pub const PENPAL_B_ID: u32 = 2001; pub const ASSETS_PALLET_ID: u8 = 50; parameter_types! { - pub PenpalTeleportableAssetLocation: xcm::v4::Location - = xcm::v4::Location::new(1, [ - xcm::v4::Junction::Parachain(PENPAL_ID), - xcm::v4::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v4::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + pub PenpalATeleportableAssetLocation: xcm::v5::Location + = xcm::v5::Location::new(1, [ + xcm::v5::Junction::Parachain(PENPAL_A_ID), + xcm::v5::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v5::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), ] ); - pub PenpalSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_ID).into_account_truncating(); - pub PenpalBTeleportableAssetLocation: xcm::v4::Location - = xcm::v4::Location::new(1, [ - xcm::v4::Junction::Parachain(PENPAL_B_ID), - xcm::v4::Junction::PalletInstance(ASSETS_PALLET_ID), - xcm::v4::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + pub PenpalBTeleportableAssetLocation: xcm::v5::Location + = xcm::v5::Location::new(1, [ + xcm::v5::Junction::Parachain(PENPAL_B_ID), + xcm::v5::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v5::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), ] ); + pub PenpalASiblingSovereignAccount: AccountId = Sibling::from(PENPAL_A_ID).into_account_truncating(); pub PenpalBSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_B_ID).into_account_truncating(); } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index 3ff5ed388a39d93cd088cca879b8b24914184497..b776cafb2545e8e25b7e137fbf448ee35293a66a 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -404,6 +404,230 @@ macro_rules! test_chain_can_claim_assets { }; } +#[macro_export] +macro_rules! test_can_estimate_and_pay_exact_fees { + ( $sender_para:ty, $asset_hub:ty, $receiver_para:ty, ($asset_id:expr, $amount:expr), $owner_prefix:ty ) => { + $crate::macros::paste::paste! { + // We first define the call we'll use throughout the test. + fn get_call( + estimated_local_fees: impl Into, + estimated_intermediate_fees: impl Into, + estimated_remote_fees: impl Into, + ) -> <$sender_para as Chain>::RuntimeCall { + type RuntimeCall = <$sender_para as Chain>::RuntimeCall; + + let beneficiary = [<$receiver_para Receiver>]::get(); + let xcm_in_destination = Xcm::<()>::builder_unsafe() + .pay_fees(estimated_remote_fees) + .deposit_asset(AllCounted(1), beneficiary) + .build(); + let ah_to_receiver = $asset_hub::sibling_location_of($receiver_para::para_id()); + let xcm_in_reserve = Xcm::<()>::builder_unsafe() + .pay_fees(estimated_intermediate_fees) + .deposit_reserve_asset( + AllCounted(1), + ah_to_receiver, + xcm_in_destination, + ) + .build(); + let sender_to_ah = $sender_para::sibling_location_of($asset_hub::para_id()); + let local_xcm = Xcm::<<$sender_para as Chain>::RuntimeCall>::builder() + .withdraw_asset(($asset_id, $amount)) + .pay_fees(estimated_local_fees) + .initiate_reserve_withdraw(AllCounted(1), sender_to_ah, xcm_in_reserve) + .build(); + + RuntimeCall::PolkadotXcm(pallet_xcm::Call::execute { + message: bx!(VersionedXcm::from(local_xcm)), + max_weight: Weight::from_parts(10_000_000_000, 500_000), + }) + } + + let destination = $sender_para::sibling_location_of($receiver_para::para_id()); + let sender = [<$sender_para Sender>]::get(); + let sender_as_seen_by_ah = $asset_hub::sibling_location_of($sender_para::para_id()); + let sov_of_sender_on_ah = $asset_hub::sovereign_account_id_of(sender_as_seen_by_ah.clone()); + let asset_owner = [<$owner_prefix AssetOwner>]::get(); + + // Fund parachain's sender account. + $sender_para::mint_foreign_asset( + <$sender_para as Chain>::RuntimeOrigin::signed(asset_owner.clone()), + $asset_id.clone().into(), + sender.clone(), + $amount * 2, + ); + + // Fund the parachain origin's SA on Asset Hub with the native tokens. + $asset_hub::fund_accounts(vec![(sov_of_sender_on_ah.clone(), $amount * 2)]); + + let beneficiary_id = [<$receiver_para Receiver>]::get(); + + let test_args = TestContext { + sender: sender.clone(), + receiver: beneficiary_id.clone(), + args: TestArgs::new_para( + destination, + beneficiary_id.clone(), + $amount, + ($asset_id, $amount).into(), + None, + 0, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // We get these from the closure. + let mut local_execution_fees = 0; + let mut local_delivery_fees = 0; + let mut remote_message = VersionedXcm::from(Xcm::<()>(Vec::new())); + <$sender_para as TestExt>::execute_with(|| { + type Runtime = <$sender_para as Chain>::Runtime; + type OriginCaller = <$sender_para as Chain>::OriginCaller; + + let call = get_call( + (Parent, 100_000_000_000u128), + (Parent, 100_000_000_000u128), + (Parent, 100_000_000_000u128), + ); + let origin = OriginCaller::system(RawOrigin::Signed(sender.clone())); + let result = Runtime::dry_run_call(origin, call).unwrap(); + let local_xcm = result.local_xcm.unwrap().clone(); + let local_xcm_weight = Runtime::query_xcm_weight(local_xcm).unwrap(); + local_execution_fees = Runtime::query_weight_to_asset_fee( + local_xcm_weight, + VersionedAssetId::from(AssetId(Location::parent())), + ) + .unwrap(); + // We filter the result to get only the messages we are interested in. + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::from(Location::new(1, [Parachain(1000)])) + }) + .unwrap(); + assert_eq!(messages_to_query.len(), 1); + remote_message = messages_to_query[0].clone(); + let delivery_fees = + Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) + .unwrap(); + local_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees); + }); + + // These are set in the AssetHub closure. + let mut intermediate_execution_fees = 0; + let mut intermediate_delivery_fees = 0; + let mut intermediate_remote_message = VersionedXcm::from(Xcm::<()>(Vec::new())); + <$asset_hub as TestExt>::execute_with(|| { + type Runtime = <$asset_hub as Chain>::Runtime; + type RuntimeCall = <$asset_hub as Chain>::RuntimeCall; + + // First we get the execution fees. + let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); + intermediate_execution_fees = Runtime::query_weight_to_asset_fee( + weight, + VersionedAssetId::from(AssetId(Location::new(1, []))), + ) + .unwrap(); + + // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. + let xcm_program = + VersionedXcm::from(Xcm::::from(remote_message.clone().try_into().unwrap())); + + // Now we get the delivery fees to the final destination. + let result = + Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap(); + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::from(Location::new(1, [Parachain(2001)])) + }) + .unwrap(); + // There's actually two messages here. + // One created when the message we sent from `$sender_para` arrived and was executed. + // The second one when we dry-run the xcm. + // We could've gotten the message from the queue without having to dry-run, but + // offchain applications would have to dry-run, so we do it here as well. + intermediate_remote_message = messages_to_query[0].clone(); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + intermediate_remote_message.clone(), + ) + .unwrap(); + intermediate_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees); + }); + + // Get the final execution fees in the destination. + let mut final_execution_fees = 0; + <$receiver_para as TestExt>::execute_with(|| { + type Runtime = <$sender_para as Chain>::Runtime; + + let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); + final_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(Location::parent()))) + .unwrap(); + }); + + // Dry-running is done. + $sender_para::reset_ext(); + $asset_hub::reset_ext(); + $receiver_para::reset_ext(); + + // Fund accounts again. + $sender_para::mint_foreign_asset( + <$sender_para as Chain>::RuntimeOrigin::signed(asset_owner), + $asset_id.clone().into(), + sender.clone(), + $amount * 2, + ); + $asset_hub::fund_accounts(vec![(sov_of_sender_on_ah, $amount * 2)]); + + // Actually run the extrinsic. + let sender_assets_before = $sender_para::execute_with(|| { + type ForeignAssets = <$sender_para as [<$sender_para Pallet>]>::ForeignAssets; + >::balance($asset_id.clone().into(), &sender) + }); + let receiver_assets_before = $receiver_para::execute_with(|| { + type ForeignAssets = <$receiver_para as [<$receiver_para Pallet>]>::ForeignAssets; + >::balance($asset_id.clone().into(), &beneficiary_id) + }); + + test.set_assertion::<$sender_para>(sender_assertions); + test.set_assertion::<$asset_hub>(hop_assertions); + test.set_assertion::<$receiver_para>(receiver_assertions); + let call = get_call( + (Parent, local_execution_fees + local_delivery_fees), + (Parent, intermediate_execution_fees + intermediate_delivery_fees), + (Parent, final_execution_fees), + ); + test.set_call(call); + test.assert(); + + let sender_assets_after = $sender_para::execute_with(|| { + type ForeignAssets = <$sender_para as [<$sender_para Pallet>]>::ForeignAssets; + >::balance($asset_id.clone().into(), &sender) + }); + let receiver_assets_after = $receiver_para::execute_with(|| { + type ForeignAssets = <$receiver_para as [<$receiver_para Pallet>]>::ForeignAssets; + >::balance($asset_id.into(), &beneficiary_id) + }); + + // We know the exact fees on every hop. + assert_eq!(sender_assets_after, sender_assets_before - $amount); + assert_eq!( + receiver_assets_after, + receiver_assets_before + $amount - + local_execution_fees - + local_delivery_fees - + intermediate_execution_fees - + intermediate_delivery_fees - + final_execution_fees + ); + } + }; +} + #[macro_export] macro_rules! test_dry_run_transfer_across_pk_bridge { ( $sender_asset_hub:ty, $sender_bridge_hub:ty, $destination:expr ) => { @@ -474,7 +698,7 @@ macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub { )); type Runtime = <$asset_hub as Chain>::Runtime; - let acceptable_payment_assets = Runtime::query_acceptable_payment_assets(4).unwrap(); + let acceptable_payment_assets = Runtime::query_acceptable_payment_assets(XCM_VERSION).unwrap(); assert_eq!(acceptable_payment_assets, vec![ VersionedAssetId::from(AssetId(wnd.clone())), VersionedAssetId::from(AssetId(usdt.clone())), diff --git a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs index 7a289a3f1ac62383e834645a57080ffa04c00710..9125c976525eea932800088017d5245a1b3b64f0 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs @@ -27,12 +27,11 @@ pub fn xcm_transact_paid_execution( beneficiary: AccountId, ) -> VersionedXcm<()> { let weight_limit = WeightLimit::Unlimited; - let require_weight_at_most = Weight::from_parts(1000000000, 200000); VersionedXcm::from(Xcm(vec![ WithdrawAsset(fees.clone().into()), BuyExecution { fees, weight_limit }, - Transact { require_weight_at_most, origin_kind, call }, + Transact { origin_kind, call }, RefundSurplus, DepositAsset { assets: All.into(), @@ -50,12 +49,11 @@ pub fn xcm_transact_unpaid_execution( origin_kind: OriginKind, ) -> VersionedXcm<()> { let weight_limit = WeightLimit::Unlimited; - let require_weight_at_most = Weight::from_parts(1000000000, 200000); let check_origin = None; VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit, check_origin }, - Transact { require_weight_at_most, origin_kind, call }, + Transact { origin_kind, call }, ])) } 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 1184b5fd7c4a4ba7909ca4c349777d3fbaaf0108..f3a1b3f5bfa282b21aba49bbe79a81780508f975 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 @@ -27,8 +27,8 @@ mod imports { // Polkadot pub use xcm::{ + latest::{ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}, prelude::{AccountId32 as AccountId32Junction, *}, - v3, }; pub use xcm_executor::traits::TransferType; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/claim_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/claim_assets.rs index 99b31aba4be010f48f0551cc1115b97caa63f409..52a20c00c2774c2455f268cd56e45b8902030b51 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/claim_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/claim_assets.rs @@ -25,5 +25,11 @@ fn assets_can_be_claimed() { let amount = AssetHubRococoExistentialDeposit::get(); let assets: Assets = (Parent, amount).into(); - test_chain_can_claim_assets!(AssetHubRococo, RuntimeCall, NetworkId::Rococo, assets, amount); + test_chain_can_claim_assets!( + AssetHubRococo, + RuntimeCall, + NetworkId::ByGenesis(ROCOCO_GENESIS_HASH), + assets, + amount + ); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/hybrid_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/hybrid_transfers.rs index 7bb25d7cec623641005402442f98642be60a2b57..baec7d20f4156657134ba8963adc61208c29266f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/hybrid_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/hybrid_transfers.rs @@ -163,7 +163,7 @@ fn transfer_foreign_assets_from_asset_hub_to_para() { // Foreign asset used: bridged WND let foreign_amount_to_send = ASSET_HUB_ROCOCO_ED * 10_000_000; let wnd_at_rococo_parachains = - Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); + Location::new(2, [Junction::GlobalConsensus(NetworkId::ByGenesis(WESTEND_GENESIS_HASH))]); // Configure destination chain to trust AH as reserve of WND PenpalA::execute_with(|| { @@ -171,7 +171,7 @@ fn transfer_foreign_assets_from_asset_hub_to_para() { ::RuntimeOrigin::root(), vec![( PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), - Location::new(2, [GlobalConsensus(Westend)]).encode(), + Location::new(2, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))]).encode(), )], )); }); @@ -293,7 +293,7 @@ fn transfer_foreign_assets_from_para_to_asset_hub() { // Foreign asset used: bridged WND let foreign_amount_to_send = ASSET_HUB_ROCOCO_ED * 10_000_000; let wnd_at_rococo_parachains = - Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); + Location::new(2, [Junction::GlobalConsensus(NetworkId::ByGenesis(WESTEND_GENESIS_HASH))]); // Configure destination chain to trust AH as reserve of WND PenpalA::execute_with(|| { @@ -301,7 +301,7 @@ fn transfer_foreign_assets_from_para_to_asset_hub() { ::RuntimeOrigin::root(), vec![( PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), - Location::new(2, [GlobalConsensus(Westend)]).encode(), + Location::new(2, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))]).encode(), )], )); }); @@ -455,7 +455,7 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { ::RuntimeOrigin::root(), vec![( PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), - Location::new(2, [GlobalConsensus(Westend)]).encode(), + Location::new(2, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))]).encode(), )], )); }); @@ -464,14 +464,14 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { ::RuntimeOrigin::root(), vec![( PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), - Location::new(2, [GlobalConsensus(Westend)]).encode(), + Location::new(2, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))]).encode(), )], )); }); // Register WND as foreign asset and transfer it around the Rococo ecosystem let wnd_at_rococo_parachains = - Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); + Location::new(2, [Junction::GlobalConsensus(NetworkId::ByGenesis(WESTEND_GENESIS_HASH))]); AssetHubRococo::force_create_foreign_asset( wnd_at_rococo_parachains.clone().try_into().unwrap(), assets_owner.clone(), diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index 302f71f89f838992934cf091cb3315e6958e01ae..698ef2c9e792887800a77a9cc5a61503be819a4d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -1599,7 +1599,7 @@ fn reserve_withdraw_from_untrusted_reserve_fails() { ]); let result = ::PolkadotXcm::execute( signed_origin, - bx!(xcm::VersionedXcm::V4(xcm)), + bx!(xcm::VersionedXcm::from(xcm)), Weight::MAX, ); assert!(result.is_err()); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs index 29eaa96946436fcafd189adcee9ec0bde6dc3548..ea8f6c1defba40b5169e05800df223d21cc0a0f1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs @@ -24,7 +24,7 @@ fn send_transact_as_superuser_from_relay_to_asset_hub_works() { ASSET_MIN_BALANCE, true, AssetHubRococoSender::get().into(), - Some(Weight::from_parts(1_019_445_000, 200_000)), + Some(Weight::from_parts(144_933_000, 3675)), ) } @@ -121,7 +121,7 @@ fn send_xcm_from_para_to_asset_hub_paying_fee_with_sufficient_asset() { ASSET_MIN_BALANCE, true, para_sovereign_account.clone(), - Some(Weight::from_parts(1_019_445_000, 200_000)), + Some(Weight::from_parts(144_933_000, 3675)), ASSET_MIN_BALANCE * 1000000000, ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/set_xcm_versions.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/set_xcm_versions.rs index 5662a78ab67f555c4a0a331fda79cbf984526cc1..8da1e56de219f0f75add56168fac5c0900265091 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/set_xcm_versions.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/set_xcm_versions.rs @@ -67,7 +67,7 @@ fn system_para_sets_relay_xcm_supported_version() { AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - AssetHubRococo::assert_dmp_queue_complete(Some(Weight::from_parts(1_019_210_000, 200_000))); + AssetHubRococo::assert_dmp_queue_complete(Some(Weight::from_parts(115_294_000, 0))); assert_expected_events!( AssetHubRococo, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs index 470b4d0f389e8cfd49dfd9fb930cfd6898ec9fd0..7fde929c0dcbe0c731b3371cba5a17c671826074 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs @@ -574,7 +574,7 @@ fn teleport_to_untrusted_chain_fails() { ]); let result = ::PolkadotXcm::execute( signed_origin, - bx!(xcm::VersionedXcm::V4(xcm)), + bx!(xcm::VersionedXcm::from(xcm)), Weight::MAX, ); assert!(result.is_err()); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs index 3320392b495d281bf9c127ab299793535362ddcd..69111d38bcac3b8d7278be81170d8158ba0bf4ea 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs @@ -71,11 +71,12 @@ fn spend_roc_on_asset_hub() { let teleport_call = RuntimeCall::Utility(pallet_utility::Call::::dispatch_as { as_origin: bx!(RococoOriginCaller::system(RawOrigin::Signed(treasury_account))), call: bx!(RuntimeCall::XcmPallet(pallet_xcm::Call::::teleport_assets { - dest: bx!(VersionedLocation::V4(asset_hub_location.clone())), - beneficiary: bx!(VersionedLocation::V4(treasury_location)), - assets: bx!(VersionedAssets::V4( - Asset { id: native_asset.clone().into(), fun: treasury_balance.into() }.into() - )), + dest: bx!(VersionedLocation::from(asset_hub_location.clone())), + beneficiary: bx!(VersionedLocation::from(treasury_location)), + assets: bx!(VersionedAssets::from(Assets::from(Asset { + id: native_asset.clone().into(), + fun: treasury_balance.into() + }))), fee_asset_item: 0, })), }); @@ -110,12 +111,12 @@ fn spend_roc_on_asset_hub() { let native_asset = Location::parent(); let treasury_spend_call = RuntimeCall::Treasury(pallet_treasury::Call::::spend { - asset_kind: bx!(VersionedLocatableAsset::V4 { - location: asset_hub_location.clone(), - asset_id: native_asset.into(), - }), + asset_kind: bx!(VersionedLocatableAsset::from(( + asset_hub_location.clone(), + native_asset.into() + ))), amount: treasury_spend_balance, - beneficiary: bx!(VersionedLocation::V4(alice_location)), + beneficiary: bx!(VersionedLocation::from(alice_location)), valid_from: None, }); @@ -170,16 +171,12 @@ fn create_and_claim_treasury_spend_in_usdt() { // treasury account on a sibling parachain. let treasury_account = ahr_xcm_config::LocationToAccountId::convert_location(&treasury_location).unwrap(); - let asset_hub_location = - v3::Location::new(0, v3::Junction::Parachain(AssetHubRococo::para_id().into())); + let asset_hub_location = Location::new(0, Parachain(AssetHubRococo::para_id().into())); let root = ::RuntimeOrigin::root(); - // asset kind to be spend from the treasury. - let asset_kind = VersionedLocatableAsset::V3 { - location: asset_hub_location, - asset_id: v3::AssetId::Concrete( - (v3::Junction::PalletInstance(50), v3::Junction::GeneralIndex(USDT_ID.into())).into(), - ), - }; + // asset kind to be spent from the treasury. + let asset_kind: VersionedLocatableAsset = + (asset_hub_location, AssetId((PalletInstance(50), GeneralIndex(USDT_ID.into())).into())) + .into(); // treasury spend beneficiary. let alice: AccountId = Rococo::account_id_of(ALICE); let bob: AccountId = Rococo::account_id_of(BOB); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs index aa0e183ecddaf1a07c1f91d04253138b2625df46..ea210d4f3b65e1568f785901f46db63babcef31b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs @@ -16,10 +16,8 @@ //! Tests for XCM fee estimation in the runtime. use crate::imports::*; -use frame_support::{ - dispatch::RawOrigin, - sp_runtime::{traits::Dispatchable, DispatchResult}, -}; +use emulated_integration_tests_common::test_can_estimate_and_pay_exact_fees; +use frame_support::dispatch::RawOrigin; use xcm_runtime_apis::{ dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, @@ -76,16 +74,6 @@ fn receiver_assertions(test: ParaToParaThroughAHTest) { ); } -fn transfer_assets_para_to_para_through_ah_dispatchable( - test: ParaToParaThroughAHTest, -) -> DispatchResult { - let call = transfer_assets_para_to_para_through_ah_call(test.clone()); - match call.dispatch(test.signed_origin) { - Ok(_) => Ok(()), - Err(error_with_post_info) => Err(error_with_post_info.error), - } -} - fn transfer_assets_para_to_para_through_ah_call( test: ParaToParaThroughAHTest, ) -> ::RuntimeCall { @@ -100,7 +88,7 @@ fn transfer_assets_para_to_para_through_ah_call( dest: bx!(test.args.dest.into()), assets: bx!(test.args.assets.clone().into()), assets_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())), - remote_fees_id: bx!(VersionedAssetId::V4(AssetId(Location::new(1, [])))), + remote_fees_id: bx!(VersionedAssetId::from(AssetId(Location::new(1, [])))), fees_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.into())), custom_xcm_on_dest: bx!(VersionedXcm::from(custom_xcm_on_dest)), weight_limit: test.args.weight_limit, @@ -151,7 +139,7 @@ fn multi_hop_works() { // We get them from the PenpalA closure. let mut delivery_fees_amount = 0; - let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + let mut remote_message = VersionedXcm::from(Xcm(Vec::new())); ::execute_with(|| { type Runtime = ::Runtime; type OriginCaller = ::OriginCaller; @@ -164,7 +152,7 @@ fn multi_hop_works() { .forwarded_xcms .iter() .find(|(destination, _)| { - *destination == VersionedLocation::V4(Location::new(1, [Parachain(1000)])) + *destination == VersionedLocation::from(Location::new(1, [Parachain(1000)])) }) .unwrap(); assert_eq!(messages_to_query.len(), 1); @@ -178,7 +166,7 @@ fn multi_hop_works() { // These are set in the AssetHub closure. let mut intermediate_execution_fees = 0; let mut intermediate_delivery_fees_amount = 0; - let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new())); + let mut intermediate_remote_message = VersionedXcm::from(Xcm::<()>(Vec::new())); ::execute_with(|| { type Runtime = ::Runtime; type RuntimeCall = ::RuntimeCall; @@ -187,13 +175,14 @@ fn multi_hop_works() { let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); intermediate_execution_fees = Runtime::query_weight_to_asset_fee( weight, - VersionedAssetId::V4(Location::new(1, []).into()), + VersionedAssetId::from(AssetId(Location::new(1, []))), ) .unwrap(); // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. - let xcm_program = - VersionedXcm::V4(Xcm::::from(remote_message.clone().try_into().unwrap())); + let xcm_program = VersionedXcm::from(Xcm::::from( + remote_message.clone().try_into().unwrap(), + )); // Now we get the delivery fees to the final destination. let result = @@ -202,7 +191,7 @@ fn multi_hop_works() { .forwarded_xcms .iter() .find(|(destination, _)| { - *destination == VersionedLocation::V4(Location::new(1, [Parachain(2001)])) + *destination == VersionedLocation::from(Location::new(1, [Parachain(2001)])) }) .unwrap(); // There's actually two messages here. @@ -225,9 +214,11 @@ fn multi_hop_works() { type Runtime = ::Runtime; let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); - final_execution_fees = - Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) - .unwrap(); + final_execution_fees = Runtime::query_weight_to_asset_fee( + weight, + VersionedAssetId::from(AssetId(Location::parent())), + ) + .unwrap(); }); // Dry-running is done. @@ -257,7 +248,8 @@ fn multi_hop_works() { test.set_assertion::(sender_assertions); test.set_assertion::(hop_assertions); test.set_assertion::(receiver_assertions); - test.set_dispatchable::(transfer_assets_para_to_para_through_ah_dispatchable); + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + test.set_call(call); test.assert(); let sender_assets_after = PenpalA::execute_with(|| { @@ -284,3 +276,14 @@ fn multi_hop_works() { final_execution_fees ); } + +#[test] +fn multi_hop_pay_fees_works() { + test_can_estimate_and_pay_exact_fees!( + PenpalA, + AssetHubRococo, + PenpalB, + (Parent, 1_000_000_000_000u128), + Penpal + ); +} 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 872a8ffa6a8a5a409d07ad7a4e486cc9060662b3..71e44e5cee7d4156b0a8cb68166ff4650a56f84a 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 @@ -31,6 +31,7 @@ pallet-asset-tx-payment = { workspace = true } # Polkadot polkadot-runtime-common = { workspace = true, default-features = true } xcm = { workspace = true } +xcm-builder = { workspace = true } xcm-executor = { workspace = true } pallet-xcm = { workspace = true } xcm-runtime-apis = { 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 179a44e14aa1c4818ac39360a7209f60feee7afd..3cca99fbfe5cfaee1f4254742b4a27f870984d16 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 @@ -26,7 +26,10 @@ mod imports { }; // Polkadot - pub use xcm::prelude::{AccountId32 as AccountId32Junction, *}; + pub use xcm::{ + latest::{AssetTransferFilter, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}, + prelude::{AccountId32 as AccountId32Junction, *}, + }; pub use xcm_executor::traits::TransferType; // Cumulus @@ -42,7 +45,7 @@ mod imports { xcm_helpers::{ get_amount_from_versioned_assets, non_fee_asset, xcm_transact_paid_execution, }, - ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, XCM_V3, + ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, USDT_ID, XCM_V3, }; pub use parachains_common::{AccountId, Balance}; pub use westend_system_emulated_network::{ @@ -59,12 +62,16 @@ mod imports { genesis::{AssetHubWestendAssetOwner, ED as ASSET_HUB_WESTEND_ED}, AssetHubWestendParaPallet as AssetHubWestendPallet, }, + bridge_hub_westend_emulated_chain::bridge_hub_westend_runtime::xcm_config::{ + self as bhw_xcm_config, + }, collectives_westend_emulated_chain::CollectivesWestendParaPallet as CollectivesWestendPallet, penpal_emulated_chain::{ penpal_runtime::xcm_config::{ CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + UniversalLocation as PenpalUniversalLocation, UsdtFromAssetHub as PenpalUsdtFromAssetHub, }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/claim_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/claim_assets.rs index de58839634f1b438c50d369bb025fc8750bda396..90af907654f90e7f704aa67e1c99ec163f68573d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/claim_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/claim_assets.rs @@ -25,5 +25,11 @@ fn assets_can_be_claimed() { let amount = AssetHubWestendExistentialDeposit::get(); let assets: Assets = (Parent, amount).into(); - test_chain_can_claim_assets!(AssetHubWestend, RuntimeCall, NetworkId::Westend, assets, amount); + test_chain_can_claim_assets!( + AssetHubWestend, + RuntimeCall, + NetworkId::ByGenesis(WESTEND_GENESIS_HASH), + assets, + amount + ); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs index 9520659712fc625ff479da489ffdae7acbb7d185..124ec2ec1f66e17b70b242acfb37c14616661237 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs @@ -34,10 +34,9 @@ fn create_and_claim_treasury_spend() { let asset_hub_location = Location::new(1, [Parachain(AssetHubWestend::para_id().into())]); let root = ::RuntimeOrigin::root(); // asset kind to be spent from the treasury. - let asset_kind = VersionedLocatableAsset::V4 { - location: asset_hub_location, - asset_id: AssetId((PalletInstance(50), GeneralIndex(USDT_ID.into())).into()), - }; + let asset_kind: VersionedLocatableAsset = + (asset_hub_location, AssetId((PalletInstance(50), GeneralIndex(USDT_ID.into())).into())) + .into(); // treasury spend beneficiary. let alice: AccountId = Westend::account_id_of(ALICE); let bob: AccountId = CollectivesWestend::account_id_of(BOB); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs index 4d6cdd9a94d6b2b37d74c1586dd66986e84fa13a..a0fc82fba6ef5ddc59921f2fa744d47c81dd834e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs @@ -163,7 +163,7 @@ fn transfer_foreign_assets_from_asset_hub_to_para() { // Foreign asset used: bridged ROC let foreign_amount_to_send = ASSET_HUB_WESTEND_ED * 10_000_000; let roc_at_westend_parachains = - Location::new(2, [Junction::GlobalConsensus(NetworkId::Rococo)]); + Location::new(2, [Junction::GlobalConsensus(NetworkId::ByGenesis(ROCOCO_GENESIS_HASH))]); // Configure destination chain to trust AH as reserve of ROC PenpalA::execute_with(|| { @@ -171,7 +171,7 @@ fn transfer_foreign_assets_from_asset_hub_to_para() { ::RuntimeOrigin::root(), vec![( PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), - Location::new(2, [GlobalConsensus(Rococo)]).encode(), + Location::new(2, [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH))]).encode(), )], )); }); @@ -293,7 +293,7 @@ fn transfer_foreign_assets_from_para_to_asset_hub() { // Foreign asset used: bridged ROC let foreign_amount_to_send = ASSET_HUB_WESTEND_ED * 10_000_000; let roc_at_westend_parachains = - Location::new(2, [Junction::GlobalConsensus(NetworkId::Rococo)]); + Location::new(2, [Junction::GlobalConsensus(NetworkId::ByGenesis(ROCOCO_GENESIS_HASH))]); // Configure destination chain to trust AH as reserve of ROC PenpalA::execute_with(|| { @@ -301,7 +301,7 @@ fn transfer_foreign_assets_from_para_to_asset_hub() { ::RuntimeOrigin::root(), vec![( PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), - Location::new(2, [GlobalConsensus(Rococo)]).encode(), + Location::new(2, [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH))]).encode(), )], )); }); @@ -456,7 +456,7 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { ::RuntimeOrigin::root(), vec![( PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), - Location::new(2, [GlobalConsensus(Rococo)]).encode(), + Location::new(2, [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH))]).encode(), )], )); }); @@ -465,14 +465,14 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { ::RuntimeOrigin::root(), vec![( PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), - Location::new(2, [GlobalConsensus(Rococo)]).encode(), + Location::new(2, [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH))]).encode(), )], )); }); // Register ROC as foreign asset and transfer it around the Westend ecosystem let roc_at_westend_parachains = - Location::new(2, [Junction::GlobalConsensus(NetworkId::Rococo)]); + Location::new(2, [Junction::GlobalConsensus(NetworkId::ByGenesis(ROCOCO_GENESIS_HASH))]); AssetHubWestend::force_create_foreign_asset( roc_at_westend_parachains.clone().try_into().unwrap(), assets_owner.clone(), @@ -819,3 +819,88 @@ fn transfer_native_asset_from_relay_to_para_through_asset_hub() { // should be non-zero assert!(receiver_assets_after < receiver_assets_before + amount_to_send); } + +// ============================================================================================== +// ==== Bidirectional Transfer - Native + Teleportable Foreign Assets - Parachain<->AssetHub ==== +// ============================================================================================== +/// Transfers of native asset plus teleportable foreign asset from Parachain to AssetHub and back +/// with fees paid using native asset. +#[test] +fn bidirectional_transfer_multiple_assets_between_penpal_and_asset_hub() { + fn execute_xcm_penpal_to_asset_hub(t: ParaToSystemParaTest) -> DispatchResult { + let all_assets = t.args.assets.clone().into_inner(); + let mut assets = all_assets.clone(); + let mut fees = assets.remove(t.args.fee_asset_item as usize); + // TODO(https://github.com/paritytech/polkadot-sdk/issues/6197): dry-run to get exact fees. + // For now just use half the fees locally, half on dest + if let Fungible(fees_amount) = fees.fun { + fees.fun = Fungible(fees_amount / 2); + } + // xcm to be executed at dest + let xcm_on_dest = Xcm(vec![ + // since this is the last hop, we don't need to further use any assets previously + // reserved for fees (there are no further hops to cover transport fees for); we + // RefundSurplus to get back any unspent fees + RefundSurplus, + DepositAsset { assets: Wild(All), beneficiary: t.args.beneficiary }, + ]); + let xcm = Xcm::<()>(vec![ + WithdrawAsset(all_assets.into()), + PayFees { asset: fees.clone() }, + InitiateTransfer { + destination: t.args.dest, + remote_fees: Some(AssetTransferFilter::ReserveWithdraw(fees.into())), + preserve_origin: false, + assets: vec![AssetTransferFilter::Teleport(assets.into())], + remote_xcm: xcm_on_dest, + }, + ]); + ::PolkadotXcm::execute( + t.signed_origin, + bx!(xcm::VersionedXcm::from(xcm.into())), + Weight::MAX, + ) + .unwrap(); + Ok(()) + } + fn execute_xcm_asset_hub_to_penpal(t: SystemParaToParaTest) -> DispatchResult { + let all_assets = t.args.assets.clone().into_inner(); + let mut assets = all_assets.clone(); + let mut fees = assets.remove(t.args.fee_asset_item as usize); + // TODO(https://github.com/paritytech/polkadot-sdk/issues/6197): dry-run to get exact fees. + // For now just use half the fees locally, half on dest + if let Fungible(fees_amount) = fees.fun { + fees.fun = Fungible(fees_amount / 2); + } + // xcm to be executed at dest + let xcm_on_dest = Xcm(vec![ + // since this is the last hop, we don't need to further use any assets previously + // reserved for fees (there are no further hops to cover transport fees for); we + // RefundSurplus to get back any unspent fees + RefundSurplus, + DepositAsset { assets: Wild(All), beneficiary: t.args.beneficiary }, + ]); + let xcm = Xcm::<()>(vec![ + WithdrawAsset(all_assets.into()), + PayFees { asset: fees.clone() }, + InitiateTransfer { + destination: t.args.dest, + remote_fees: Some(AssetTransferFilter::ReserveDeposit(fees.into())), + preserve_origin: false, + assets: vec![AssetTransferFilter::Teleport(assets.into())], + remote_xcm: xcm_on_dest, + }, + ]); + ::PolkadotXcm::execute( + t.signed_origin, + bx!(xcm::VersionedXcm::from(xcm.into())), + Weight::MAX, + ) + .unwrap(); + Ok(()) + } + do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( + execute_xcm_penpal_to_asset_hub, + execute_xcm_asset_hub_to_penpal, + ); +} 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 73b73b239a1bb54e818cad575084ae775c6f9db1..0dfe7a85f4c2a08ecb7d7701cabe64d0045a13f2 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 @@ -18,8 +18,86 @@ mod fellowship_treasury; mod hybrid_transfers; mod reserve_transfer; mod send; +mod set_asset_claimer; mod set_xcm_versions; mod swap; mod teleport; +mod transact; mod treasury; mod xcm_fee_estimation; + +#[macro_export] +macro_rules! foreign_balance_on { + ( $chain:ident, $id:expr, $who:expr ) => { + emulated_integration_tests_common::impls::paste::paste! { + <$chain>::execute_with(|| { + type ForeignAssets = <$chain as [<$chain Pallet>]>::ForeignAssets; + >::balance($id, $who) + }) + } + }; +} + +#[macro_export] +macro_rules! create_pool_with_wnd_on { + ( $chain:ident, $asset_id:expr, $is_foreign:expr, $asset_owner:expr ) => { + emulated_integration_tests_common::impls::paste::paste! { + <$chain>::execute_with(|| { + type RuntimeEvent = <$chain as Chain>::RuntimeEvent; + let owner = $asset_owner; + let signed_owner = <$chain as Chain>::RuntimeOrigin::signed(owner.clone()); + let wnd_location: Location = Parent.into(); + if $is_foreign { + assert_ok!(<$chain as [<$chain Pallet>]>::ForeignAssets::mint( + signed_owner.clone(), + $asset_id.clone().into(), + owner.clone().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + } else { + let asset_id = match $asset_id.interior.last() { + Some(GeneralIndex(id)) => *id as u32, + _ => unreachable!(), + }; + assert_ok!(<$chain as [<$chain Pallet>]>::Assets::mint( + signed_owner.clone(), + asset_id.into(), + owner.clone().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + } + + assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::create_pool( + signed_owner.clone(), + Box::new(wnd_location.clone()), + Box::new($asset_id.clone()), + )); + + assert_expected_events!( + $chain, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::add_liquidity( + signed_owner, + Box::new(wnd_location), + Box::new($asset_id), + 1_000_000_000_000, + 2_000_000_000_000, // $asset_id is worth half of wnd + 0, + 0, + owner.into() + )); + + assert_expected_events!( + $chain, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + } + }; +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index 10c27c338ec7f69ac63d9498e62852a6a83dca61..558eab13e5c7cd945d350ed9b9e6ee88e6ea29a2 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::imports::*; +use crate::{create_pool_with_wnd_on, foreign_balance_on, imports::*}; use sp_core::{crypto::get_public_from_string_or_panic, sr25519}; fn relay_to_para_sender_assertions(t: RelayToParaTest) { @@ -651,10 +651,8 @@ fn reserve_transfer_native_asset_from_relay_to_para() { // Query initial balances let sender_balance_before = test.sender.balance; - let receiver_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(relay_native_asset_location.clone(), &receiver) - }); + let receiver_assets_before = + foreign_balance_on!(PenpalA, relay_native_asset_location.clone(), &receiver); // Set assertions and dispatchables test.set_assertion::(relay_to_para_sender_assertions); @@ -664,10 +662,8 @@ fn reserve_transfer_native_asset_from_relay_to_para() { // Query final balances let sender_balance_after = test.sender.balance; - let receiver_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(relay_native_asset_location, &receiver) - }); + let receiver_assets_after = + foreign_balance_on!(PenpalA, relay_native_asset_location, &receiver); // Sender's balance is reduced by amount sent plus delivery fees assert!(sender_balance_after < sender_balance_before - amount_to_send); @@ -722,10 +718,8 @@ fn reserve_transfer_native_asset_from_para_to_relay() { let mut test = ParaToRelayTest::new(test_args); // Query initial balances - let sender_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(relay_native_asset_location.clone(), &sender) - }); + let sender_assets_before = + foreign_balance_on!(PenpalA, relay_native_asset_location.clone(), &sender); let receiver_balance_before = test.receiver.balance; // Set assertions and dispatchables @@ -735,10 +729,7 @@ fn reserve_transfer_native_asset_from_para_to_relay() { test.assert(); // Query final balances - let sender_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(relay_native_asset_location, &sender) - }); + let sender_assets_after = foreign_balance_on!(PenpalA, relay_native_asset_location, &sender); let receiver_balance_after = test.receiver.balance; // Sender's balance is reduced by amount sent plus delivery fees @@ -784,10 +775,8 @@ fn reserve_transfer_native_asset_from_asset_hub_to_para() { // Query initial balances let sender_balance_before = test.sender.balance; - let receiver_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &receiver) - }); + let receiver_assets_before = + foreign_balance_on!(PenpalA, system_para_native_asset_location.clone(), &receiver); // Set assertions and dispatchables test.set_assertion::(system_para_to_para_sender_assertions); @@ -797,10 +786,8 @@ fn reserve_transfer_native_asset_from_asset_hub_to_para() { // Query final balances let sender_balance_after = test.sender.balance; - let receiver_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location, &receiver) - }); + let receiver_assets_after = + foreign_balance_on!(PenpalA, system_para_native_asset_location, &receiver); // Sender's balance is reduced by amount sent plus delivery fees assert!(sender_balance_after < sender_balance_before - amount_to_send); @@ -856,10 +843,8 @@ fn reserve_transfer_native_asset_from_para_to_asset_hub() { let mut test = ParaToSystemParaTest::new(test_args); // Query initial balances - let sender_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) - }); + let sender_assets_before = + foreign_balance_on!(PenpalA, system_para_native_asset_location.clone(), &sender); let receiver_balance_before = test.receiver.balance; // Set assertions and dispatchables @@ -869,10 +854,8 @@ fn reserve_transfer_native_asset_from_para_to_asset_hub() { test.assert(); // Query final balances - let sender_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location, &sender) - }); + let sender_assets_after = + foreign_balance_on!(PenpalA, system_para_native_asset_location, &sender); let receiver_balance_after = test.receiver.balance; // Sender's balance is reduced by amount sent plus delivery fees @@ -950,17 +933,10 @@ fn reserve_transfer_multiple_assets_from_asset_hub_to_para() { type Assets = ::Assets; >::balance(RESERVABLE_ASSET_ID, &sender) }); - let receiver_system_native_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &receiver) - }); - let receiver_foreign_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance( - system_para_foreign_asset_location.clone(), - &receiver, - ) - }); + let receiver_system_native_assets_before = + foreign_balance_on!(PenpalA, system_para_native_asset_location.clone(), &receiver); + let receiver_foreign_assets_before = + foreign_balance_on!(PenpalA, system_para_foreign_asset_location.clone(), &receiver); // Set assertions and dispatchables test.set_assertion::(system_para_to_para_assets_sender_assertions); @@ -974,14 +950,10 @@ fn reserve_transfer_multiple_assets_from_asset_hub_to_para() { type Assets = ::Assets; >::balance(RESERVABLE_ASSET_ID, &sender) }); - let receiver_system_native_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location, &receiver) - }); - let receiver_foreign_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location, &receiver) - }); + let receiver_system_native_assets_after = + foreign_balance_on!(PenpalA, system_para_native_asset_location, &receiver); + let receiver_foreign_assets_after = + foreign_balance_on!(PenpalA, system_para_foreign_asset_location.clone(), &receiver); // Sender's balance is reduced assert!(sender_balance_after < sender_balance_before); // Receiver's foreign asset balance is increased @@ -1082,14 +1054,10 @@ fn reserve_transfer_multiple_assets_from_para_to_asset_hub() { let mut test = ParaToSystemParaTest::new(para_test_args); // Query initial balances - let sender_system_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(system_asset_location_on_penpal.clone(), &sender) - }); - let sender_foreign_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(asset_location_on_penpal.clone(), &sender) - }); + let sender_system_assets_before = + foreign_balance_on!(PenpalA, system_asset_location_on_penpal.clone(), &sender); + let sender_foreign_assets_before = + foreign_balance_on!(PenpalA, asset_location_on_penpal.clone(), &sender); let receiver_balance_before = test.receiver.balance; let receiver_assets_before = AssetHubWestend::execute_with(|| { type Assets = ::Assets; @@ -1103,14 +1071,10 @@ fn reserve_transfer_multiple_assets_from_para_to_asset_hub() { test.assert(); // Query final balances - let sender_system_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(system_asset_location_on_penpal, &sender) - }); - let sender_foreign_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(asset_location_on_penpal, &sender) - }); + let sender_system_assets_after = + foreign_balance_on!(PenpalA, system_asset_location_on_penpal, &sender); + let sender_foreign_assets_after = + foreign_balance_on!(PenpalA, asset_location_on_penpal, &sender); let receiver_balance_after = test.receiver.balance; let receiver_assets_after = AssetHubWestend::execute_with(|| { type Assets = ::Assets; @@ -1171,14 +1135,10 @@ fn reserve_transfer_native_asset_from_para_to_para_through_relay() { let mut test = ParaToParaThroughRelayTest::new(test_args); // Query initial balances - let sender_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(relay_native_asset_location.clone(), &sender) - }); - let receiver_assets_before = PenpalB::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(relay_native_asset_location.clone(), &receiver) - }); + let sender_assets_before = + foreign_balance_on!(PenpalA, relay_native_asset_location.clone(), &sender); + let receiver_assets_before = + foreign_balance_on!(PenpalB, relay_native_asset_location.clone(), &receiver); // Set assertions and dispatchables test.set_assertion::(para_to_para_through_hop_sender_assertions); @@ -1188,14 +1148,10 @@ fn reserve_transfer_native_asset_from_para_to_para_through_relay() { test.assert(); // Query final balances - let sender_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(relay_native_asset_location.clone(), &sender) - }); - let receiver_assets_after = PenpalB::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(relay_native_asset_location, &receiver) - }); + let sender_assets_after = + foreign_balance_on!(PenpalA, relay_native_asset_location.clone(), &sender); + let receiver_assets_after = + foreign_balance_on!(PenpalB, relay_native_asset_location, &receiver); // Sender's balance is reduced by amount sent plus delivery fees. assert!(sender_assets_after < sender_assets_before - amount_to_send); @@ -1231,55 +1187,11 @@ fn reserve_transfer_usdt_from_asset_hub_to_para() { )); }); - let relay_asset_penpal_pov = RelayLocation::get(); - let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); - // Setup the pool between `relay_asset_penpal_pov` and `usdt_from_asset_hub` on PenpalA. // So we can swap the custom asset that comes from AssetHubWestend for native asset to pay for // fees. - PenpalA::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - assert_ok!(::ForeignAssets::mint( - ::RuntimeOrigin::signed(PenpalAssetOwner::get()), - usdt_from_asset_hub.clone().into(), - PenpalASender::get().into(), - 10_000_000_000_000, // For it to have more than enough. - )); - - assert_ok!(::AssetConversion::create_pool( - ::RuntimeOrigin::signed(PenpalASender::get()), - Box::new(relay_asset_penpal_pov.clone()), - Box::new(usdt_from_asset_hub.clone()), - )); - - assert_expected_events!( - PenpalA, - vec![ - RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, - ] - ); - - assert_ok!(::AssetConversion::add_liquidity( - ::RuntimeOrigin::signed(PenpalASender::get()), - Box::new(relay_asset_penpal_pov), - Box::new(usdt_from_asset_hub.clone()), - // `usdt_from_asset_hub` is worth a third of `relay_asset_penpal_pov` - 1_000_000_000_000, - 3_000_000_000_000, - 0, - 0, - PenpalASender::get().into() - )); - - assert_expected_events!( - PenpalA, - vec![ - RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, - ] - ); - }); + create_pool_with_wnd_on!(PenpalA, PenpalUsdtFromAssetHub::get(), true, PenpalAssetOwner::get()); let assets: Assets = vec![( [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(usdt_id.into())], @@ -1310,10 +1222,8 @@ fn reserve_transfer_usdt_from_asset_hub_to_para() { type Balances = ::Balances; Balances::free_balance(&sender) }); - let receiver_initial_balance = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(usdt_from_asset_hub.clone(), &receiver) - }); + let receiver_initial_balance = + foreign_balance_on!(PenpalA, usdt_from_asset_hub.clone(), &receiver); test.set_assertion::(system_para_to_para_sender_assertions); test.set_assertion::(system_para_to_para_receiver_assertions); @@ -1328,10 +1238,7 @@ fn reserve_transfer_usdt_from_asset_hub_to_para() { type Balances = ::Balances; Balances::free_balance(&sender) }); - let receiver_after_balance = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(usdt_from_asset_hub, &receiver) - }); + let receiver_after_balance = foreign_balance_on!(PenpalA, usdt_from_asset_hub, &receiver); // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we allow payment with different assets locally, this should be the same, since // they aren't used for fees. @@ -1371,7 +1278,7 @@ fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { ]); // Give USDT to sov account of sender. - let usdt_id = 1984; + let usdt_id: u32 = 1984; AssetHubWestend::execute_with(|| { use frame_support::traits::tokens::fungibles::Mutate; type Assets = ::Assets; @@ -1383,101 +1290,15 @@ fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { }); // We create a pool between WND and USDT in AssetHub. - let native_asset: Location = Parent.into(); let usdt = Location::new( 0, [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(usdt_id.into())], ); - - // set up pool with USDT <> native pair - AssetHubWestend::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - assert_ok!(::Assets::mint( - ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - usdt_id.into(), - AssetHubWestendSender::get().into(), - 10_000_000_000_000, // For it to have more than enough. - )); - - assert_ok!(::AssetConversion::create_pool( - ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(native_asset.clone()), - Box::new(usdt.clone()), - )); - - assert_expected_events!( - AssetHubWestend, - vec![ - RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, - ] - ); - - assert_ok!(::AssetConversion::add_liquidity( - ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(native_asset), - Box::new(usdt), - 1_000_000_000_000, - 2_000_000_000_000, // usdt is worth half of `native_asset` - 0, - 0, - AssetHubWestendSender::get().into() - )); - - assert_expected_events!( - AssetHubWestend, - vec![ - RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, - ] - ); - }); - - let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); - + create_pool_with_wnd_on!(AssetHubWestend, usdt, false, AssetHubWestendSender::get()); // We also need a pool between WND and USDT on PenpalB. - PenpalB::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - let relay_asset = RelayLocation::get(); - - assert_ok!(::ForeignAssets::mint( - ::RuntimeOrigin::signed(PenpalAssetOwner::get()), - usdt_from_asset_hub.clone().into(), - PenpalBReceiver::get().into(), - 10_000_000_000_000, // For it to have more than enough. - )); - - assert_ok!(::AssetConversion::create_pool( - ::RuntimeOrigin::signed(PenpalBReceiver::get()), - Box::new(relay_asset.clone()), - Box::new(usdt_from_asset_hub.clone()), - )); - - assert_expected_events!( - PenpalB, - vec![ - RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, - ] - ); - - assert_ok!(::AssetConversion::add_liquidity( - ::RuntimeOrigin::signed(PenpalBReceiver::get()), - Box::new(relay_asset), - Box::new(usdt_from_asset_hub.clone()), - 1_000_000_000_000, - 2_000_000_000_000, // `usdt_from_asset_hub` is worth half of `relay_asset` - 0, - 0, - PenpalBReceiver::get().into() - )); - - assert_expected_events!( - PenpalB, - vec![ - RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, - ] - ); - }); + create_pool_with_wnd_on!(PenpalB, PenpalUsdtFromAssetHub::get(), true, PenpalAssetOwner::get()); + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); PenpalA::execute_with(|| { use frame_support::traits::tokens::fungibles::Mutate; type ForeignAssets = ::ForeignAssets; @@ -1523,14 +1344,9 @@ fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { let mut test = ParaToParaThroughAHTest::new(test_args); // Query initial balances - let sender_assets_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(usdt_from_asset_hub.clone(), &sender) - }); - let receiver_assets_before = PenpalB::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(usdt_from_asset_hub.clone(), &receiver) - }); + let sender_assets_before = foreign_balance_on!(PenpalA, usdt_from_asset_hub.clone(), &sender); + let receiver_assets_before = + foreign_balance_on!(PenpalB, usdt_from_asset_hub.clone(), &receiver); test.set_assertion::(para_to_para_through_hop_sender_assertions); test.set_assertion::(para_to_para_asset_hub_hop_assertions); test.set_assertion::(para_to_para_through_hop_receiver_assertions); @@ -1540,14 +1356,8 @@ fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { test.assert(); // Query final balances - let sender_assets_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(usdt_from_asset_hub.clone(), &sender) - }); - let receiver_assets_after = PenpalB::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance(usdt_from_asset_hub, &receiver) - }); + let sender_assets_after = foreign_balance_on!(PenpalA, usdt_from_asset_hub.clone(), &sender); + let receiver_assets_after = foreign_balance_on!(PenpalB, usdt_from_asset_hub, &receiver); // Sender's balance is reduced by amount assert!(sender_assets_after < sender_assets_before - asset_amount_to_send); @@ -1603,7 +1413,7 @@ fn reserve_withdraw_from_untrusted_reserve_fails() { ]); let result = ::PolkadotXcm::execute( signed_origin, - bx!(xcm::VersionedXcm::V4(xcm)), + bx!(xcm::VersionedXcm::from(xcm)), Weight::MAX, ); assert!(result.is_err()); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs index 761c7c12255c5890fd99e53a20b81060e2e2b8c2..d4f239df48774dc723afded1f91f186b53a249a8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs @@ -24,7 +24,7 @@ fn send_transact_as_superuser_from_relay_to_asset_hub_works() { ASSET_MIN_BALANCE, true, AssetHubWestendSender::get().into(), - Some(Weight::from_parts(1_019_445_000, 200_000)), + Some(Weight::from_parts(144_759_000, 3675)), ) } @@ -121,7 +121,7 @@ fn send_xcm_from_para_to_asset_hub_paying_fee_with_sufficient_asset() { ASSET_MIN_BALANCE, true, para_sovereign_account.clone(), - Some(Weight::from_parts(1_019_445_000, 200_000)), + Some(Weight::from_parts(144_759_000, 3675)), ASSET_MIN_BALANCE * 1000000000, ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_asset_claimer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_asset_claimer.rs new file mode 100644 index 0000000000000000000000000000000000000000..544b05360521e50b2f99c4b47ab7282eb91187bd --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_asset_claimer.rs @@ -0,0 +1,154 @@ +// 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. + +//! Tests related to claiming assets trapped during XCM execution. + +use crate::imports::{bhw_xcm_config::LocationToAccountId, *}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + impls::AccountId32, +}; +use frame_support::{assert_ok, sp_runtime::traits::Dispatchable}; +use westend_system_emulated_network::{ + asset_hub_westend_emulated_chain::asset_hub_westend_runtime::RuntimeOrigin as AssetHubRuntimeOrigin, + bridge_hub_westend_emulated_chain::bridge_hub_westend_runtime::RuntimeOrigin as BridgeHubRuntimeOrigin, +}; +use xcm_executor::traits::ConvertLocation; + +#[test] +fn test_set_asset_claimer_within_a_chain() { + let (alice_account, _) = account_and_location(ALICE); + let (bob_account, bob_location) = account_and_location(BOB); + + let trap_amount = 16_000_000_000_000; + let assets: Assets = (Parent, trap_amount).into(); + + let alice_balance_before = + ::account_data_of(alice_account.clone()).free; + AssetHubWestend::fund_accounts(vec![(alice_account.clone(), trap_amount * 2)]); + let alice_balance_after = + ::account_data_of(alice_account.clone()).free; + assert_eq!(alice_balance_after - alice_balance_before, trap_amount * 2); + + type RuntimeCall = ::RuntimeCall; + let asset_trap_xcm = Xcm::::builder_unsafe() + .set_asset_claimer(bob_location.clone()) + .withdraw_asset(assets.clone()) + .clear_origin() + .build(); + + AssetHubWestend::execute_with(|| { + assert_ok!(RuntimeCall::PolkadotXcm(pallet_xcm::Call::execute { + message: bx!(VersionedXcm::from(asset_trap_xcm)), + max_weight: Weight::from_parts(4_000_000_000_000, 300_000), + }) + .dispatch(AssetHubRuntimeOrigin::signed(alice_account.clone()))); + }); + + let balance_after_trap = + ::account_data_of(alice_account.clone()).free; + assert_eq!(alice_balance_after - balance_after_trap, trap_amount); + + let bob_balance_before = ::account_data_of(bob_account.clone()).free; + let claim_xcm = Xcm::::builder_unsafe() + .claim_asset(assets.clone(), Here) + .deposit_asset(AllCounted(assets.len() as u32), bob_location.clone()) + .build(); + + AssetHubWestend::execute_with(|| { + assert_ok!(RuntimeCall::PolkadotXcm(pallet_xcm::Call::execute { + message: bx!(VersionedXcm::from(claim_xcm)), + max_weight: Weight::from_parts(4_000_000_000_000, 300_000), + }) + .dispatch(AssetHubRuntimeOrigin::signed(bob_account.clone()))); + }); + + let bob_balance_after = ::account_data_of(bob_account.clone()).free; + assert_eq!(bob_balance_after - bob_balance_before, trap_amount); +} + +fn account_and_location(account: &str) -> (AccountId32, Location) { + let account_id = AssetHubWestend::account_id_of(account); + let account_clone = account_id.clone(); + let location: Location = [Junction::AccountId32 { + network: Some(ByGenesis(WESTEND_GENESIS_HASH)), + id: account_id.into(), + }] + .into(); + (account_clone, location) +} + +// The test: +// 1. Funds Bob account on BridgeHub, withdraws the funds, sets asset claimer to +// sibling-account-of(AssetHub/Alice) and traps the funds. +// 2. Alice on AssetHub sends an XCM to BridgeHub to claim assets, pay fees and deposit +// remaining to her sibling account on BridgeHub. +#[test] +fn test_set_asset_claimer_between_the_chains() { + let alice = AssetHubWestend::account_id_of(ALICE); + let alice_bh_sibling = Location::new( + 1, + [ + Parachain(AssetHubWestend::para_id().into()), + Junction::AccountId32 { + network: Some(ByGenesis(WESTEND_GENESIS_HASH)), + id: alice.clone().into(), + }, + ], + ); + + let bob = BridgeHubWestend::account_id_of(BOB); + let trap_amount = 16_000_000_000_000u128; + BridgeHubWestend::fund_accounts(vec![(bob.clone(), trap_amount * 2)]); + + let assets: Assets = (Parent, trap_amount).into(); + type RuntimeCall = ::RuntimeCall; + let trap_xcm = Xcm::::builder_unsafe() + .set_asset_claimer(alice_bh_sibling.clone()) + .withdraw_asset(assets.clone()) + .clear_origin() + .build(); + + BridgeHubWestend::execute_with(|| { + assert_ok!(RuntimeCall::PolkadotXcm(pallet_xcm::Call::execute { + message: bx!(VersionedXcm::from(trap_xcm)), + max_weight: Weight::from_parts(4_000_000_000_000, 700_000), + }) + .dispatch(BridgeHubRuntimeOrigin::signed(bob.clone()))); + }); + + let alice_bh_acc = LocationToAccountId::convert_location(&alice_bh_sibling).unwrap(); + let balance = ::account_data_of(alice_bh_acc.clone()).free; + assert_eq!(balance, 0); + + let pay_fees = 6_000_000_000_000u128; + let xcm_on_bh = Xcm::<()>::builder_unsafe() + .claim_asset(assets.clone(), Here) + .pay_fees((Parent, pay_fees)) + .deposit_asset(All, alice_bh_sibling.clone()) + .build(); + let bh_on_ah = AssetHubWestend::sibling_location_of(BridgeHubWestend::para_id()).into(); + AssetHubWestend::execute_with(|| { + assert_ok!(::PolkadotXcm::send( + AssetHubRuntimeOrigin::signed(alice.clone()), + bx!(bh_on_ah), + bx!(VersionedXcm::from(xcm_on_bh)), + )); + }); + + let alice_bh_acc = LocationToAccountId::convert_location(&alice_bh_sibling).unwrap(); + let balance = ::account_data_of(alice_bh_acc).free; + assert_eq!(balance, trap_amount - pay_fees); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_xcm_versions.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_xcm_versions.rs index 474e9a86ccc22777bc4f2ac574ef70ec39843a45..4405ed2988a9703c857d08b78e34578bbc8c47d7 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_xcm_versions.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/set_xcm_versions.rs @@ -67,10 +67,7 @@ fn system_para_sets_relay_xcm_supported_version() { AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - AssetHubWestend::assert_dmp_queue_complete(Some(Weight::from_parts( - 1_019_210_000, - 200_000, - ))); + AssetHubWestend::assert_dmp_queue_complete(Some(Weight::from_parts(115_688_000, 0))); assert_expected_events!( AssetHubWestend, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs index ee0f297792f8ca9f09818aa50520a964bcfd0d61..0897c187e7cbc1a382db5f494ff880f4bda4b41c 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::imports::*; +use crate::{foreign_balance_on, imports::*}; fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { Westend::assert_ump_queue_processed( @@ -112,16 +112,6 @@ fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) { assert_expected_events!( AssetHubWestend, vec![ - // native asset used for fees is transferred to Parachain's Sovereign account as reserve - RuntimeEvent::Balances( - pallet_balances::Event::Transfer { from, to, amount } - ) => { - from: *from == t.sender.account_id, - to: *to == AssetHubWestend::sovereign_account_id_of( - t.args.dest.clone() - ), - amount: *amount == t.args.amount, - }, // foreign asset is burned locally as part of teleportation RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { asset_id: *asset_id == expected_foreign_asset_id, @@ -283,13 +273,13 @@ pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using ah_to_para_dispatchable: fn(SystemParaToParaTest) -> DispatchResult, ) { // Init values for Parachain - let fee_amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 100; + let fee_amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; let asset_location_on_penpal = PenpalLocalTeleportableToAssetHub::get(); let asset_id_on_penpal = match asset_location_on_penpal.last() { Some(Junction::GeneralIndex(id)) => *id as u32, _ => unreachable!(), }; - let asset_amount_to_send = ASSET_HUB_WESTEND_ED * 100; + let asset_amount_to_send = ASSET_HUB_WESTEND_ED * 1000; let asset_owner = PenpalAssetOwner::get(); let system_para_native_asset_location = RelayLocation::get(); let sender = PenpalASender::get(); @@ -318,7 +308,7 @@ pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using ::RuntimeOrigin::signed(asset_owner.clone()), asset_id_on_penpal, sender.clone(), - asset_amount_to_send, + asset_amount_to_send * 2, ); // fund Parachain's check account to be able to teleport PenpalA::fund_accounts(vec![( @@ -335,7 +325,7 @@ pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using )]); // Init values for System Parachain - let foreign_asset_at_asset_hub_westend = + let foreign_asset_at_asset_hub = Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); @@ -355,13 +345,11 @@ pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using ), }; let mut penpal_to_ah = ParaToSystemParaTest::new(penpal_to_ah_test_args); - let penpal_sender_balance_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance( - system_para_native_asset_location.clone(), - &PenpalASender::get(), - ) - }); + let penpal_sender_balance_before = foreign_balance_on!( + PenpalA, + system_para_native_asset_location.clone(), + &PenpalASender::get() + ); let ah_receiver_balance_before = penpal_to_ah.receiver.balance; @@ -369,26 +357,22 @@ pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using type Assets = ::Assets; >::balance(asset_id_on_penpal, &PenpalASender::get()) }); - let ah_receiver_assets_before = AssetHubWestend::execute_with(|| { - type Assets = ::ForeignAssets; - >::balance( - foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), - &AssetHubWestendReceiver::get(), - ) - }); + let ah_receiver_assets_before = foreign_balance_on!( + AssetHubWestend, + foreign_asset_at_asset_hub.clone(), + &AssetHubWestendReceiver::get() + ); penpal_to_ah.set_assertion::(penpal_to_ah_foreign_assets_sender_assertions); penpal_to_ah.set_assertion::(penpal_to_ah_foreign_assets_receiver_assertions); penpal_to_ah.set_dispatchable::(para_to_ah_dispatchable); penpal_to_ah.assert(); - let penpal_sender_balance_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance( - system_para_native_asset_location.clone(), - &PenpalASender::get(), - ) - }); + let penpal_sender_balance_after = foreign_balance_on!( + PenpalA, + system_para_native_asset_location.clone(), + &PenpalASender::get() + ); let ah_receiver_balance_after = penpal_to_ah.receiver.balance; @@ -396,13 +380,11 @@ pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using type Assets = ::Assets; >::balance(asset_id_on_penpal, &PenpalASender::get()) }); - let ah_receiver_assets_after = AssetHubWestend::execute_with(|| { - type Assets = ::ForeignAssets; - >::balance( - foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), - &AssetHubWestendReceiver::get(), - ) - }); + let ah_receiver_assets_after = foreign_balance_on!( + AssetHubWestend, + foreign_asset_at_asset_hub.clone(), + &AssetHubWestendReceiver::get() + ); // Sender's balance is reduced assert!(penpal_sender_balance_after < penpal_sender_balance_before); @@ -427,17 +409,21 @@ pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using type ForeignAssets = ::ForeignAssets; assert_ok!(ForeignAssets::transfer( ::RuntimeOrigin::signed(AssetHubWestendReceiver::get()), - foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), + foreign_asset_at_asset_hub.clone().try_into().unwrap(), AssetHubWestendSender::get().into(), asset_amount_to_send, )); }); + // Only send back half the amount. + let asset_amount_to_send = asset_amount_to_send / 2; + let fee_amount_to_send = fee_amount_to_send / 2; + let ah_to_penpal_beneficiary_id = PenpalAReceiver::get(); let penpal_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let ah_assets: Assets = vec![ (Parent, fee_amount_to_send).into(), - (foreign_asset_at_asset_hub_westend.clone(), asset_amount_to_send).into(), + (foreign_asset_at_asset_hub.clone(), asset_amount_to_send).into(), ] .into(); let fee_asset_index = ah_assets @@ -462,21 +448,17 @@ pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using let mut ah_to_penpal = SystemParaToParaTest::new(ah_to_penpal_test_args); let ah_sender_balance_before = ah_to_penpal.sender.balance; - let penpal_receiver_balance_before = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance( - system_para_native_asset_location.clone(), - &PenpalAReceiver::get(), - ) - }); + let penpal_receiver_balance_before = foreign_balance_on!( + PenpalA, + system_para_native_asset_location.clone(), + &PenpalAReceiver::get() + ); - let ah_sender_assets_before = AssetHubWestend::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance( - foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), - &AssetHubWestendSender::get(), - ) - }); + let ah_sender_assets_before = foreign_balance_on!( + AssetHubWestend, + foreign_asset_at_asset_hub.clone(), + &AssetHubWestendSender::get() + ); let penpal_receiver_assets_before = PenpalA::execute_with(|| { type Assets = ::Assets; >::balance(asset_id_on_penpal, &PenpalAReceiver::get()) @@ -488,21 +470,14 @@ pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using ah_to_penpal.assert(); let ah_sender_balance_after = ah_to_penpal.sender.balance; - let penpal_receiver_balance_after = PenpalA::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance( - system_para_native_asset_location, - &PenpalAReceiver::get(), - ) - }); + let penpal_receiver_balance_after = + foreign_balance_on!(PenpalA, system_para_native_asset_location, &PenpalAReceiver::get()); - let ah_sender_assets_after = AssetHubWestend::execute_with(|| { - type ForeignAssets = ::ForeignAssets; - >::balance( - foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), - &AssetHubWestendSender::get(), - ) - }); + let ah_sender_assets_after = foreign_balance_on!( + AssetHubWestend, + foreign_asset_at_asset_hub.clone(), + &AssetHubWestendSender::get() + ); let penpal_receiver_assets_after = PenpalA::execute_with(|| { type Assets = ::Assets; >::balance(asset_id_on_penpal, &PenpalAReceiver::get()) @@ -577,7 +552,7 @@ fn teleport_to_untrusted_chain_fails() { ]); let result = ::PolkadotXcm::execute( signed_origin, - bx!(xcm::VersionedXcm::V4(xcm)), + bx!(xcm::VersionedXcm::from(xcm)), Weight::MAX, ); assert!(result.is_err()); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/transact.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/transact.rs new file mode 100644 index 0000000000000000000000000000000000000000..3c53cfb261be4c2c0d09d0080e29e7df1ebdd284 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/transact.rs @@ -0,0 +1,246 @@ +// 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::{create_pool_with_wnd_on, foreign_balance_on, imports::*}; +use frame_support::traits::tokens::fungibles::Mutate; +use xcm_builder::{DescribeAllTerminal, DescribeFamily, HashedDescription}; +use xcm_executor::traits::ConvertLocation; + +/// PenpalA transacts on PenpalB, paying fees using USDT. XCM has to go through Asset Hub as the +/// reserve location of USDT. The original origin `PenpalA/PenpalASender` is proxied by Asset Hub. +fn transfer_and_transact_in_same_xcm( + destination: Location, + usdt: Asset, + beneficiary: Location, + call: xcm::DoubleEncoded<()>, +) { + let signed_origin = ::RuntimeOrigin::signed(PenpalASender::get().into()); + let context = PenpalUniversalLocation::get(); + let asset_hub_location = PenpalA::sibling_location_of(AssetHubWestend::para_id()); + + let Fungible(total_usdt) = usdt.fun else { unreachable!() }; + + // TODO(https://github.com/paritytech/polkadot-sdk/issues/6197): dry-run to get local fees, for now use hardcoded value. + let local_fees_amount = 80_000_000_000; // current exact value 69_200_786_622 + let ah_fees_amount = 90_000_000_000; // current exact value 79_948_099_299 + let usdt_to_ah_then_onward_amount = total_usdt - local_fees_amount - ah_fees_amount; + + let local_fees: Asset = (usdt.id.clone(), local_fees_amount).into(); + let fees_for_ah: Asset = (usdt.id.clone(), ah_fees_amount).into(); + let usdt_to_ah_then_onward: Asset = (usdt.id.clone(), usdt_to_ah_then_onward_amount).into(); + + // xcm to be executed at dest + let xcm_on_dest = Xcm(vec![ + Transact { origin_kind: OriginKind::Xcm, call }, + ExpectTransactStatus(MaybeErrorCode::Success), + // since this is the last hop, we don't need to further use any assets previously + // reserved for fees (there are no further hops to cover transport fees for); we + // RefundSurplus to get back any unspent fees + RefundSurplus, + DepositAsset { assets: Wild(All), beneficiary }, + ]); + let destination = destination.reanchored(&asset_hub_location, &context).unwrap(); + let xcm_on_ah = Xcm(vec![InitiateTransfer { + destination, + remote_fees: Some(AssetTransferFilter::ReserveDeposit(Wild(All))), + preserve_origin: true, + assets: vec![], + remote_xcm: xcm_on_dest, + }]); + let xcm = Xcm::<()>(vec![ + WithdrawAsset(usdt.into()), + PayFees { asset: local_fees }, + InitiateTransfer { + destination: asset_hub_location, + remote_fees: Some(AssetTransferFilter::ReserveWithdraw(fees_for_ah.into())), + preserve_origin: true, + assets: vec![AssetTransferFilter::ReserveWithdraw(usdt_to_ah_then_onward.into())], + remote_xcm: xcm_on_ah, + }, + ]); + ::PolkadotXcm::execute( + signed_origin, + bx!(xcm::VersionedXcm::from(xcm.into())), + Weight::MAX, + ) + .unwrap(); +} + +/// PenpalA transacts on PenpalB, paying fees using USDT. XCM has to go through Asset Hub as the +/// reserve location of USDT. The original origin `PenpalA/PenpalASender` is proxied by Asset Hub. +#[test] +fn transact_from_para_to_para_through_asset_hub() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let fee_amount_to_send: Balance = WESTEND_ED * 10000; + let sender_chain_as_seen_by_asset_hub = + AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_asset_hub = + AssetHubWestend::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub); + let receiver_as_seen_by_asset_hub = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_asset_hub = + AssetHubWestend::sovereign_account_id_of(receiver_as_seen_by_asset_hub); + + // Create SA-of-Penpal-on-AHW with ED. + AssetHubWestend::fund_accounts(vec![ + (sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + (sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + ]); + + // Prefund USDT to sov account of sender. + AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + assert_ok!(>::mint_into( + USDT_ID, + &sov_of_sender_on_asset_hub.clone().into(), + fee_amount_to_send, + )); + }); + + // We create a pool between WND and USDT in AssetHub. + let usdt = Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]); + create_pool_with_wnd_on!(AssetHubWestend, usdt, false, AssetHubWestendSender::get()); + // We also need a pool between WND and USDT on PenpalA. + create_pool_with_wnd_on!(PenpalA, PenpalUsdtFromAssetHub::get(), true, PenpalAssetOwner::get()); + // We also need a pool between WND and USDT on PenpalB. + create_pool_with_wnd_on!(PenpalB, PenpalUsdtFromAssetHub::get(), true, PenpalAssetOwner::get()); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + assert_ok!(>::mint_into( + usdt_from_asset_hub.clone(), + &sender, + fee_amount_to_send, + )); + }); + + // Give the sender enough Relay tokens to pay for local delivery fees. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + sender.clone(), + 10_000_000_000_000, // Large estimate to make sure it works. + ); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Query initial balances + let sender_assets_before = foreign_balance_on!(PenpalA, usdt_from_asset_hub.clone(), &sender); + let receiver_assets_before = + foreign_balance_on!(PenpalB, usdt_from_asset_hub.clone(), &receiver); + + // Now register a new asset on PenpalB from PenpalA/sender account while paying fees using USDT + // (going through Asset Hub) + + let usdt_to_send: Asset = (usdt_from_asset_hub.clone(), fee_amount_to_send).into(); + let assets: Assets = usdt_to_send.clone().into(); + let asset_location_on_penpal_a = + Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())]); + let penpal_a_as_seen_by_penpal_b = PenpalB::sibling_location_of(PenpalA::para_id()); + let sender_as_seen_by_penpal_b = + penpal_a_as_seen_by_penpal_b.clone().appended_with(sender.clone()).unwrap(); + let foreign_asset_at_penpal_b = + penpal_a_as_seen_by_penpal_b.appended_with(asset_location_on_penpal_a).unwrap(); + // Encoded `create_asset` call to be executed in PenpalB + let call = PenpalB::create_foreign_asset_call( + foreign_asset_at_penpal_b.clone(), + ASSET_MIN_BALANCE, + receiver.clone(), + ); + PenpalA::execute_with(|| { + // initiate transaction + transfer_and_transact_in_same_xcm(destination, usdt_to_send, receiver.clone().into(), call); + + // verify expected events; + PenpalA::assert_xcm_pallet_attempted_complete(None); + }); + AssetHubWestend::execute_with(|| { + let sov_penpal_a_on_ah = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalA::para_id()), + ); + let sov_penpal_b_on_ah = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalB::para_id()), + ); + asset_hub_hop_assertions(&assets, sov_penpal_a_on_ah, sov_penpal_b_on_ah); + }); + PenpalB::execute_with(|| { + let expected_creator = + HashedDescription::>::convert_location( + &sender_as_seen_by_penpal_b, + ) + .unwrap(); + penpal_b_assertions(foreign_asset_at_penpal_b, expected_creator, receiver.clone()); + }); + + // Query final balances + let sender_assets_after = foreign_balance_on!(PenpalA, usdt_from_asset_hub.clone(), &sender); + let receiver_assets_after = foreign_balance_on!(PenpalB, usdt_from_asset_hub, &receiver); + + // Sender's balance is reduced by amount + assert_eq!(sender_assets_after, sender_assets_before - fee_amount_to_send); + // Receiver's balance is increased + assert!(receiver_assets_after > receiver_assets_before); +} + +fn asset_hub_hop_assertions(assets: &Assets, sender_sa: AccountId, receiver_sa: AccountId) { + type RuntimeEvent = ::RuntimeEvent; + for asset in assets.inner() { + let amount = if let Fungible(a) = asset.fun { a } else { unreachable!() }; + assert_expected_events!( + AssetHubWestend, + vec![ + // Withdrawn from sender parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Burned { owner, balance, .. } + ) => { + owner: *owner == sender_sa, + balance: *balance == amount, + }, + // Deposited to receiver parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Deposited { who, .. } + ) => { + who: *who == receiver_sa, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + } +} + +fn penpal_b_assertions( + expected_asset: Location, + expected_creator: AccountId, + expected_owner: AccountId, +) { + type RuntimeEvent = ::RuntimeEvent; + PenpalB::assert_xcmp_queue_success(None); + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Created { asset_id, creator, owner } + ) => { + asset_id: *asset_id == expected_asset, + creator: *creator == expected_creator, + owner: *owner == expected_owner, + }, + ] + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs index b70967184387a868b4bd2b49970f8e16d418d62d..c303e6411d33b7a4485aa0132855d0237a26eb1b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs @@ -32,11 +32,10 @@ fn create_and_claim_treasury_spend() { ahw_xcm_config::LocationToAccountId::convert_location(&treasury_location).unwrap(); let asset_hub_location = Location::new(0, Parachain(AssetHubWestend::para_id().into())); let root = ::RuntimeOrigin::root(); - // asset kind to be spend from the treasury. - let asset_kind = VersionedLocatableAsset::V4 { - location: asset_hub_location, - asset_id: AssetId([PalletInstance(50), GeneralIndex(USDT_ID.into())].into()), - }; + // asset kind to be spent from the treasury. + let asset_kind: VersionedLocatableAsset = + (asset_hub_location, AssetId([PalletInstance(50), GeneralIndex(USDT_ID.into())].into())) + .into(); // treasury spend beneficiary. let alice: AccountId = Westend::account_id_of(ALICE); let bob: AccountId = Westend::account_id_of(BOB); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index 037d6604ea4d5e665928b5629dce3a52abb30a8a..ec05a074c5acfb4b176009ced7c7ff18bfb39c0b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -17,10 +17,8 @@ use crate::imports::*; -use frame_support::{ - dispatch::RawOrigin, - sp_runtime::{traits::Dispatchable, DispatchResult}, -}; +use emulated_integration_tests_common::test_can_estimate_and_pay_exact_fees; +use frame_support::dispatch::RawOrigin; use xcm_runtime_apis::{ dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, @@ -77,22 +75,12 @@ fn receiver_assertions(test: ParaToParaThroughAHTest) { ); } -fn transfer_assets_para_to_para_through_ah_dispatchable( - test: ParaToParaThroughAHTest, -) -> DispatchResult { - let call = transfer_assets_para_to_para_through_ah_call(test.clone()); - match call.dispatch(test.signed_origin) { - Ok(_) => Ok(()), - Err(error_with_post_info) => Err(error_with_post_info.error), - } -} - fn transfer_assets_para_to_para_through_ah_call( test: ParaToParaThroughAHTest, ) -> ::RuntimeCall { type RuntimeCall = ::RuntimeCall; - let asset_hub_location: Location = PenpalB::sibling_location_of(AssetHubWestend::para_id()); + let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubWestend::para_id()); let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { assets: Wild(AllCounted(test.args.assets.len() as u32)), beneficiary: test.args.beneficiary, @@ -101,7 +89,7 @@ fn transfer_assets_para_to_para_through_ah_call( dest: bx!(test.args.dest.into()), assets: bx!(test.args.assets.clone().into()), assets_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())), - remote_fees_id: bx!(VersionedAssetId::V4(AssetId(Location::new(1, [])))), + remote_fees_id: bx!(VersionedAssetId::from(AssetId(Location::parent()))), fees_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.into())), custom_xcm_on_dest: bx!(VersionedXcm::from(custom_xcm_on_dest)), weight_limit: test.args.weight_limit, @@ -153,7 +141,7 @@ fn multi_hop_works() { // We get them from the PenpalA closure. let mut delivery_fees_amount = 0; - let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + let mut remote_message = VersionedXcm::from(Xcm(Vec::new())); ::execute_with(|| { type Runtime = ::Runtime; type OriginCaller = ::OriginCaller; @@ -166,7 +154,7 @@ fn multi_hop_works() { .forwarded_xcms .iter() .find(|(destination, _)| { - *destination == VersionedLocation::V4(Location::new(1, [Parachain(1000)])) + *destination == VersionedLocation::from(Location::new(1, [Parachain(1000)])) }) .unwrap(); assert_eq!(messages_to_query.len(), 1); @@ -180,7 +168,7 @@ fn multi_hop_works() { // These are set in the AssetHub closure. let mut intermediate_execution_fees = 0; let mut intermediate_delivery_fees_amount = 0; - let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new())); + let mut intermediate_remote_message = VersionedXcm::from(Xcm::<()>(Vec::new())); ::execute_with(|| { type Runtime = ::Runtime; type RuntimeCall = ::RuntimeCall; @@ -189,13 +177,14 @@ fn multi_hop_works() { let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); intermediate_execution_fees = Runtime::query_weight_to_asset_fee( weight, - VersionedAssetId::V4(Location::new(1, []).into()), + VersionedAssetId::from(AssetId(Location::new(1, []))), ) .unwrap(); // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. - let xcm_program = - VersionedXcm::V4(Xcm::::from(remote_message.clone().try_into().unwrap())); + let xcm_program = VersionedXcm::from(Xcm::::from( + remote_message.clone().try_into().unwrap(), + )); // Now we get the delivery fees to the final destination. let result = @@ -204,7 +193,7 @@ fn multi_hop_works() { .forwarded_xcms .iter() .find(|(destination, _)| { - *destination == VersionedLocation::V4(Location::new(1, [Parachain(2001)])) + *destination == VersionedLocation::from(Location::new(1, [Parachain(2001)])) }) .unwrap(); // There's actually two messages here. @@ -228,7 +217,7 @@ fn multi_hop_works() { let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); final_execution_fees = - Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(Location::parent())) .unwrap(); }); @@ -259,7 +248,8 @@ fn multi_hop_works() { test.set_assertion::(sender_assertions); test.set_assertion::(hop_assertions); test.set_assertion::(receiver_assertions); - test.set_dispatchable::(transfer_assets_para_to_para_through_ah_dispatchable); + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + test.set_call(call); test.assert(); let sender_assets_after = PenpalA::execute_with(|| { @@ -286,3 +276,14 @@ fn multi_hop_works() { final_execution_fees ); } + +#[test] +fn multi_hop_pay_fees_works() { + test_can_estimate_and_pay_exact_fees!( + PenpalA, + AssetHubWestend, + PenpalB, + (Parent, 1_000_000_000_000u128), + Penpal + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index 77e4c8183e65fd1de4c56c7b23844f3ab732d7f1..54bc395c86f094b22350d5940c29b0b2a32cd36f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -22,9 +22,8 @@ mod imports { // Polkadot pub use xcm::{ - latest::ParentThen, + latest::{ParentThen, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}, prelude::{AccountId32 as AccountId32Junction, *}, - v4::{self, NetworkId::Westend as WestendId}, }; pub use xcm_executor::traits::TransferType; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs index 0e1cfdd82aafd336c0f46a77771eeaaf9c70199e..33ab1e70b97b95b8369457b7e72bd0bf2d205f07 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs @@ -39,7 +39,7 @@ fn send_assets_over_bridge(send_fn: F) { fn set_up_rocs_for_penpal_rococo_through_ahr_to_ahw( sender: &AccountId, amount: u128, -) -> (Location, v4::Location) { +) -> (Location, v5::Location) { let roc_at_rococo_parachains = roc_at_ah_rococo(); let roc_at_asset_hub_westend = bridged_roc_at_ah_westend(); create_foreign_on_ah_westend(roc_at_asset_hub_westend.clone(), true); @@ -70,7 +70,7 @@ fn send_assets_from_penpal_rococo_through_rococo_ah_to_westend_ah( ); let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - Westend, + ByGenesis(WESTEND_GENESIS_HASH), AssetHubWestend::para_id(), ); // send message over bridge @@ -125,7 +125,7 @@ fn send_roc_from_asset_hub_rococo_to_asset_hub_westend() { set_up_pool_with_wnd_on_ah_westend(bridged_roc_at_asset_hub_westend.clone(), true); let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - Westend, + ByGenesis(WESTEND_GENESIS_HASH), AssetHubWestend::para_id(), ); let rocs_in_reserve_on_ahr_before = @@ -199,7 +199,7 @@ fn send_back_wnds_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { // fund the AHR's SA on AHW with the WND tokens held in reserve let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), AssetHubRococo::para_id(), ); AssetHubWestend::fund_accounts(vec![(sov_ahr_on_ahw.clone(), prefund_amount)]); @@ -271,7 +271,7 @@ fn send_back_wnds_usdt_and_weth_from_asset_hub_rococo_to_asset_hub_westend() { create_foreign_on_ah_westend(bridged_weth_at_ah.clone(), true); // prefund AHR's sovereign account on AHW to be able to withdraw USDT and wETH from reserves let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), AssetHubRococo::para_id(), ); AssetHubWestend::mint_asset( @@ -357,7 +357,7 @@ fn send_rocs_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() set_up_rocs_for_penpal_rococo_through_ahr_to_ahw(&sender, amount); let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - Westend, + ByGenesis(WESTEND_GENESIS_HASH), AssetHubWestend::para_id(), ); let rocs_in_reserve_on_ahr_before = @@ -463,7 +463,7 @@ fn send_back_wnds_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_weste // fund the AHR's SA on AHW with the WND tokens held in reserve let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( - NetworkId::Rococo, + NetworkId::ByGenesis(ROCOCO_GENESIS_HASH), AssetHubRococo::para_id(), ); AssetHubWestend::fund_accounts(vec![(sov_ahr_on_ahw.clone(), amount * 2)]); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/claim_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/claim_assets.rs index e61dc35bdf8a3cecbaa1cb42582582b1b7c1183f..e678cc40a3cbbaa3b4bd1521d8b376505b0f6d31 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/claim_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/claim_assets.rs @@ -25,5 +25,11 @@ fn assets_can_be_claimed() { let amount = BridgeHubRococoExistentialDeposit::get(); let assets: Assets = (Parent, amount).into(); - test_chain_can_claim_assets!(AssetHubRococo, RuntimeCall, NetworkId::Rococo, assets, amount); + test_chain_can_claim_assets!( + AssetHubRococo, + RuntimeCall, + NetworkId::ByGenesis(ROCOCO_GENESIS_HASH), + assets, + amount + ); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs index 767f74f6ad7f488952c5156068448f055e6a1946..8aff877559616ccb24b2f3de1a3a00492404d907 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs @@ -14,6 +14,7 @@ // limitations under the License. use crate::imports::*; +use xcm::opaque::v5; mod asset_transfers; mod claim_assets; @@ -23,10 +24,22 @@ mod snowbridge; mod teleport; pub(crate) fn asset_hub_westend_location() -> Location { - Location::new(2, [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())]) + Location::new( + 2, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(AssetHubWestend::para_id().into()), + ], + ) } pub(crate) fn bridge_hub_westend_location() -> Location { - Location::new(2, [GlobalConsensus(Westend), Parachain(BridgeHubWestend::para_id().into())]) + Location::new( + 2, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(BridgeHubWestend::para_id().into()), + ], + ) } // ROC and wROC @@ -34,7 +47,7 @@ pub(crate) fn roc_at_ah_rococo() -> Location { Parent.into() } pub(crate) fn bridged_roc_at_ah_westend() -> Location { - Location::new(2, [GlobalConsensus(Rococo)]) + Location::new(2, [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH))]) } // WND and wWND @@ -42,7 +55,7 @@ pub(crate) fn wnd_at_ah_westend() -> Location { Parent.into() } pub(crate) fn bridged_wnd_at_ah_rococo() -> Location { - Location::new(2, [GlobalConsensus(Westend)]) + Location::new(2, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))]) } // USDT and wUSDT @@ -53,7 +66,7 @@ pub(crate) fn bridged_usdt_at_ah_rococo() -> Location { Location::new( 2, [ - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(AssetHubWestend::para_id().into()), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into()), @@ -73,7 +86,7 @@ pub(crate) fn weth_at_asset_hubs() -> Location { } pub(crate) fn create_foreign_on_ah_rococo( - id: v4::Location, + id: v5::Location, sufficient: bool, prefund_accounts: Vec<(AccountId, u128)>, ) { @@ -82,18 +95,18 @@ pub(crate) fn create_foreign_on_ah_rococo( AssetHubRococo::force_create_foreign_asset(id, owner, sufficient, min, prefund_accounts); } -pub(crate) fn create_foreign_on_ah_westend(id: v4::Location, sufficient: bool) { +pub(crate) fn create_foreign_on_ah_westend(id: v5::Location, sufficient: bool) { let owner = AssetHubWestend::account_id_of(ALICE); AssetHubWestend::force_create_foreign_asset(id, owner, sufficient, ASSET_MIN_BALANCE, vec![]); } -pub(crate) fn foreign_balance_on_ah_rococo(id: v4::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_rococo(id: v5::Location, who: &AccountId) -> u128 { AssetHubRococo::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) }) } -pub(crate) fn foreign_balance_on_ah_westend(id: v4::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_westend(id: v5::Location, who: &AccountId) -> u128 { AssetHubWestend::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) @@ -101,8 +114,8 @@ pub(crate) fn foreign_balance_on_ah_westend(id: v4::Location, who: &AccountId) - } // set up pool -pub(crate) fn set_up_pool_with_wnd_on_ah_westend(asset: v4::Location, is_foreign: bool) { - let wnd: v4::Location = v4::Parent.into(); +pub(crate) fn set_up_pool_with_wnd_on_ah_westend(asset: v5::Location, is_foreign: bool) { + let wnd: v5::Location = v5::Parent.into(); AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; let owner = AssetHubWestendSender::get(); @@ -117,7 +130,7 @@ pub(crate) fn set_up_pool_with_wnd_on_ah_westend(asset: v4::Location, is_foreign )); } else { let asset_id = match asset.interior.last() { - Some(v4::Junction::GeneralIndex(id)) => *id as u32, + Some(GeneralIndex(id)) => *id as u32, _ => unreachable!(), }; assert_ok!(::Assets::mint( @@ -237,7 +250,11 @@ pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), ROC * 5); AssetHubRococo::open_bridge( AssetHubRococo::sibling_location_of(BridgeHubRococo::para_id()), - [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())].into(), + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(AssetHubWestend::para_id().into()), + ] + .into(), Some(( (roc_at_ah_rococo(), ROC * 1).into(), BridgeHubRococo::sovereign_account_id_of(BridgeHubRococo::sibling_location_of( @@ -250,7 +267,11 @@ pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5); AssetHubWestend::open_bridge( AssetHubWestend::sibling_location_of(BridgeHubWestend::para_id()), - [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())].into(), + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + Parachain(AssetHubRococo::para_id().into()), + ] + .into(), Some(( (wnd_at_ah_westend(), WND * 1).into(), BridgeHubWestend::sovereign_account_id_of(BridgeHubWestend::sibling_location_of( diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/register_bridged_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/register_bridged_assets.rs index 44637670112b9e3cd2aa10e94d2dc59b41d3196f..1ae3a1b15805b5b9507da7bce3a32c278a6ac6a9 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/register_bridged_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/register_bridged_assets.rs @@ -22,7 +22,7 @@ const XCM_FEE: u128 = 4_000_000_000_000; fn register_rococo_asset_on_wah_from_rah() { let sa_of_rah_on_wah = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), AssetHubRococo::para_id(), ); @@ -30,7 +30,7 @@ fn register_rococo_asset_on_wah_from_rah() { let bridged_asset_at_wah = Location::new( 2, [ - GlobalConsensus(Rococo), + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(AssetHubRococo::para_id().into()), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into()), diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs index 12f05742a0803f73926c2a1e7d58aa724ed16015..931a3128f826997086eed4bb224e0c33edeef78b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs @@ -29,7 +29,7 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable let xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit, check_origin }, ExportMessage { - network: WestendId, + network: ByGenesis(WESTEND_GENESIS_HASH), destination: [Parachain(AssetHubWestend::para_id().into())].into(), xcm: remote_xcm, }, @@ -60,15 +60,6 @@ fn send_xcm_from_rococo_relay_to_westend_asset_hub_should_fail_on_not_applicable #[test] fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() { - // Initially set only default version on all runtimes - let newer_xcm_version = xcm::prelude::XCM_VERSION; - let older_xcm_version = newer_xcm_version - 1; - - AssetHubRococo::force_default_xcm_version(Some(older_xcm_version)); - BridgeHubRococo::force_default_xcm_version(Some(older_xcm_version)); - BridgeHubWestend::force_default_xcm_version(Some(older_xcm_version)); - AssetHubWestend::force_default_xcm_version(Some(older_xcm_version)); - // prepare data let destination = asset_hub_westend_location(); let native_token = Location::parent(); @@ -82,6 +73,14 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() { // open bridge open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // Initially set only default version on all runtimes + let newer_xcm_version = xcm::prelude::XCM_VERSION; + let older_xcm_version = newer_xcm_version - 1; + AssetHubRococo::force_default_xcm_version(Some(older_xcm_version)); + BridgeHubRococo::force_default_xcm_version(Some(older_xcm_version)); + BridgeHubWestend::force_default_xcm_version(Some(older_xcm_version)); + AssetHubWestend::force_default_xcm_version(Some(older_xcm_version)); + // send XCM from AssetHubRococo - fails - destination version not known assert_err!( send_assets_from_asset_hub_rococo( diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index d91a0c6895f98c377230f48d984d393e9773aeb9..d59553574c26a4f56628bb2fb139a184089a9d4c 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -25,7 +25,7 @@ use snowbridge_pallet_inbound_queue_fixtures::{ }; use snowbridge_pallet_system; use snowbridge_router_primitives::inbound::{ - Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, + Command, Destination, EthereumLocationsConverterFor, MessageV1, VersionedMessage, }; use sp_core::H256; use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable}; @@ -84,11 +84,7 @@ fn create_agent() { let remote_xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), - Transact { - require_weight_at_most: 3000000000.into(), - origin_kind: OriginKind::Xcm, - call: create_agent_call.encode().into(), - }, + Transact { origin_kind: OriginKind::Xcm, call: create_agent_call.encode().into() }, ])); // Rococo Global Consensus @@ -142,11 +138,7 @@ fn create_channel() { let create_agent_xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), - Transact { - require_weight_at_most: 3000000000.into(), - origin_kind: OriginKind::Xcm, - call: create_agent_call.encode().into(), - }, + Transact { origin_kind: OriginKind::Xcm, call: create_agent_call.encode().into() }, ])); let create_channel_call = @@ -155,11 +147,7 @@ fn create_channel() { let create_channel_xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(origin_para).into()), - Transact { - require_weight_at_most: 3000000000.into(), - origin_kind: OriginKind::Xcm, - call: create_channel_call.encode().into(), - }, + Transact { origin_kind: OriginKind::Xcm, call: create_channel_call.encode().into() }, ])); // Rococo Global Consensus @@ -310,24 +298,26 @@ fn send_token_from_ethereum_to_penpal() { )); }); + let ethereum_network_v5: NetworkId = EthereumNetwork::get().into(); + // The Weth asset location, identified by the contract address on Ethereum let weth_asset_location: Location = - (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); + (Parent, Parent, ethereum_network_v5, AccountKey20 { network: None, key: WETH }).into(); - let origin_location = (Parent, Parent, EthereumNetwork::get()).into(); + let origin_location = (Parent, Parent, ethereum_network_v5).into(); // Fund ethereum sovereign on AssetHub let ethereum_sovereign: AccountId = - GlobalConsensusEthereumConvertsFor::::convert_location(&origin_location) - .unwrap(); + EthereumLocationsConverterFor::::convert_location(&origin_location).unwrap(); AssetHubRococo::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); // Create asset on the Penpal parachain. PenpalA::execute_with(|| { - assert_ok!(::ForeignAssets::create( - ::RuntimeOrigin::signed(PenpalASender::get()), + assert_ok!(::ForeignAssets::force_create( + ::RuntimeOrigin::root(), weth_asset_location.clone(), asset_hub_sovereign.into(), + false, 1000, )); @@ -448,14 +438,14 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { )), fun: Fungible(WETH_AMOUNT), }]; - let multi_assets = VersionedAssets::V4(Assets::from(assets)); + let multi_assets = VersionedAssets::from(Assets::from(assets)); - let destination = VersionedLocation::V4(Location::new( + let destination = VersionedLocation::from(Location::new( 2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], )); - let beneficiary = VersionedLocation::V4(Location::new( + let beneficiary = VersionedLocation::from(Location::new( 0, [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], )); @@ -564,10 +554,9 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { } fn send_token_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u128) { - let weth_asset_location: Location = Location::new( - 2, - [EthereumNetwork::get().into(), AccountKey20 { network: None, key: WETH }], - ); + let ethereum_network_v5: NetworkId = EthereumNetwork::get().into(); + let weth_asset_location: Location = + Location::new(2, [ethereum_network_v5.into(), AccountKey20 { network: None, key: WETH }]); // Fund asset hub sovereign on bridge hub let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new( 1, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 76e8312921decb84a950814e9c236b28d5ad60e7..501ddb84d42591a0735932399600cf63e0c1c140 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -22,9 +22,9 @@ mod imports { // Polkadot pub use xcm::{ - latest::ParentThen, + latest::{ParentThen, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}, prelude::{AccountId32 as AccountId32Junction, *}, - v4::{self, NetworkId::Rococo as RococoId}, + v5, }; pub use xcm_executor::traits::TransferType; @@ -56,6 +56,7 @@ mod imports { penpal_emulated_chain::{ penpal_runtime::xcm_config::{ CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, + LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, UniversalLocation as PenpalUniversalLocation, }, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, @@ -71,8 +72,9 @@ mod imports { BridgeHubWestendPara as BridgeHubWestend, BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, BridgeHubWestendParaSender as BridgeHubWestendSender, PenpalBPara as PenpalB, - PenpalBParaSender as PenpalBSender, WestendRelay as Westend, - WestendRelayReceiver as WestendReceiver, WestendRelaySender as WestendSender, + PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender, + WestendRelay as Westend, WestendRelayReceiver as WestendReceiver, + WestendRelaySender as WestendSender, }; pub const ASSET_ID: u32 = 1; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs index 0856c95260095d6111d6ad4f18821b38bd89debd..ab09517339db71c3eeff70ebec15bcf8218f02f4 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs @@ -12,7 +12,9 @@ // 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::tests::*; + +use crate::{create_pool_with_native_on, tests::*}; +use xcm::latest::AssetTransferFilter; fn send_assets_over_bridge(send_fn: F) { // fund the AHW's SA on BHW for paying bridge transport fees @@ -38,7 +40,7 @@ fn send_assets_over_bridge(send_fn: F) { fn set_up_wnds_for_penpal_westend_through_ahw_to_ahr( sender: &AccountId, amount: u128, -) -> (Location, v4::Location) { +) -> (Location, v5::Location) { let wnd_at_westend_parachains = wnd_at_ah_westend(); let wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo(); create_foreign_on_ah_rococo(wnd_at_asset_hub_rococo.clone(), true); @@ -69,10 +71,9 @@ fn send_assets_from_penpal_westend_through_westend_ah_to_rococo_ah( ); let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), AssetHubRococo::para_id(), ); - // send message over bridge assert_ok!(PenpalB::execute_with(|| { let signed_origin = ::RuntimeOrigin::signed(PenpalBSender::get()); @@ -128,13 +129,18 @@ fn send_wnds_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { let bridged_wnd_at_asset_hub_rococo = bridged_wnd_at_ah_rococo(); create_foreign_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), true); - set_up_pool_with_roc_on_ah_rococo(bridged_wnd_at_asset_hub_rococo.clone(), true); + create_pool_with_native_on!( + AssetHubRococo, + bridged_wnd_at_asset_hub_rococo.clone(), + true, + AssetHubRococoSender::get() + ); //////////////////////////////////////////////////////////// // Let's first send over just some WNDs as a simple example //////////////////////////////////////////////////////////// let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), AssetHubRococo::para_id(), ); let wnds_in_reserve_on_ahw_before = @@ -206,7 +212,12 @@ fn send_wnds_usdt_and_weth_from_asset_hub_westend_to_asset_hub_rococo() { ); create_foreign_on_ah_rococo(bridged_weth_at_ah.clone(), true); create_foreign_on_ah_rococo(bridged_usdt_at_asset_hub_rococo.clone(), true); - set_up_pool_with_roc_on_ah_rococo(bridged_usdt_at_asset_hub_rococo.clone(), true); + create_pool_with_native_on!( + AssetHubRococo, + bridged_usdt_at_asset_hub_rococo.clone(), + true, + AssetHubRococoSender::get() + ); let receiver_usdts_before = foreign_balance_on_ah_rococo(bridged_usdt_at_asset_hub_rococo.clone(), &receiver); @@ -269,7 +280,7 @@ fn send_back_rocs_from_asset_hub_westend_to_asset_hub_rococo() { // fund the AHW's SA on AHR with the ROC tokens held in reserve let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - Westend, + ByGenesis(WESTEND_GENESIS_HASH), AssetHubWestend::para_id(), ); AssetHubRococo::fund_accounts(vec![(sov_ahw_on_ahr.clone(), prefund_amount)]); @@ -339,7 +350,7 @@ fn send_wnds_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() set_up_wnds_for_penpal_westend_through_ahw_to_ahr(&sender, amount); let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), AssetHubRococo::para_id(), ); let wnds_in_reserve_on_ahw_before = @@ -445,7 +456,7 @@ fn send_back_rocs_from_penpal_westend_through_asset_hub_westend_to_asset_hub_roc // fund the AHW's SA on AHR with the ROC tokens held in reserve let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - Westend, + ByGenesis(WESTEND_GENESIS_HASH), AssetHubWestend::para_id(), ); AssetHubRococo::fund_accounts(vec![(sov_ahw_on_ahr.clone(), amount * 2)]); @@ -543,3 +554,295 @@ fn dry_run_transfer_to_rococo_sends_xcm_to_bridge_hub() { asset_hub_rococo_location() ); } + +fn do_send_pens_and_wnds_from_penpal_westend_via_ahw_to_asset_hub_rococo( + wnds: (Location, u128), + pens: (Location, u128), +) { + let (wnds_id, wnds_amount) = wnds; + let (pens_id, pens_amount) = pens; + send_assets_over_bridge(|| { + let sov_penpal_on_ahw = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalB::para_id()), + ); + let sov_ahr_on_ahw = + AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( + ByGenesis(ROCOCO_GENESIS_HASH), + AssetHubRococo::para_id(), + ); + // send message over bridge + assert_ok!(PenpalB::execute_with(|| { + let destination = asset_hub_rococo_location(); + let local_asset_hub = PenpalB::sibling_location_of(AssetHubWestend::para_id()); + let signed_origin = ::RuntimeOrigin::signed(PenpalBSender::get()); + let beneficiary: Location = + AccountId32Junction { network: None, id: AssetHubRococoReceiver::get().into() } + .into(); + let wnds: Asset = (wnds_id.clone(), wnds_amount).into(); + let pens: Asset = (pens_id, pens_amount).into(); + let assets: Assets = vec![wnds.clone(), pens.clone()].into(); + + // TODO: dry-run to get exact fees, for now just some static value 100_000_000_000 + let penpal_fees_amount = 100_000_000_000; + // use 100_000_000_000 WNDs in fees on AHW + // (exec fees: 3_593_000_000, transpo fees: 69_021_561_290 = 72_614_561_290) + // TODO: make this exact once we have bridge dry-running + let ahw_fee_amount = 100_000_000_000; + + // XCM to be executed at dest (Rococo Asset Hub) + let xcm_on_dest = Xcm(vec![ + // since this is the last hop, we don't need to further use any assets previously + // reserved for fees (there are no further hops to cover transport fees for); we + // RefundSurplus to get back any unspent fees + RefundSurplus, + // deposit everything to final beneficiary + DepositAsset { assets: Wild(All), beneficiary: beneficiary.clone() }, + ]); + + // XCM to be executed at (intermediary) Westend Asset Hub + let context = PenpalUniversalLocation::get(); + let reanchored_dest = + destination.clone().reanchored(&local_asset_hub, &context).unwrap(); + let reanchored_pens = pens.clone().reanchored(&local_asset_hub, &context).unwrap(); + let mut onward_wnds = wnds.clone().reanchored(&local_asset_hub, &context).unwrap(); + onward_wnds.fun = Fungible(wnds_amount - ahw_fee_amount - penpal_fees_amount); + let xcm_on_ahw = Xcm(vec![ + // both WNDs and PENs are local-reserve transferred to Rococo Asset Hub + // initially, all WNDs are reserved for fees on destination, but at the end of the + // program we RefundSurplus to get back any unspent and deposit them to final + // beneficiary + InitiateTransfer { + destination: reanchored_dest, + remote_fees: Some(AssetTransferFilter::ReserveDeposit(onward_wnds.into())), + preserve_origin: false, + assets: vec![AssetTransferFilter::ReserveDeposit(reanchored_pens.into())], + remote_xcm: xcm_on_dest, + }, + ]); + + let penpal_fees = (wnds.id.clone(), Fungible(penpal_fees_amount)); + let ahw_fees: Asset = (wnds.id.clone(), Fungible(ahw_fee_amount)).into(); + let ahw_non_fees_wnds: Asset = + (wnds.id.clone(), Fungible(wnds_amount - ahw_fee_amount - penpal_fees_amount)) + .into(); + // XCM to be executed locally + let xcm = Xcm::<()>(vec![ + // Withdraw both WNDs and PENs from origin account + WithdrawAsset(assets.into()), + PayFees { asset: penpal_fees.into() }, + // Execute the transfers while paying remote fees with WNDs + InitiateTransfer { + destination: local_asset_hub, + // WNDs for fees are reserve-withdrawn at AHW and reserved for fees + remote_fees: Some(AssetTransferFilter::ReserveWithdraw(ahw_fees.into())), + preserve_origin: false, + // PENs are teleported to AHW, rest of non-fee WNDs are reserve-withdrawn at AHW + assets: vec![ + AssetTransferFilter::Teleport(pens.into()), + AssetTransferFilter::ReserveWithdraw(ahw_non_fees_wnds.into()), + ], + remote_xcm: xcm_on_ahw, + }, + ]); + + ::PolkadotXcm::execute( + signed_origin, + bx!(xcm::VersionedXcm::V5(xcm.into())), + Weight::MAX, + ) + })); + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![ + // Amount to reserve transfer is withdrawn from Penpal's sovereign account + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == sov_penpal_on_ahw.clone().into(), + amount: *amount == wnds_amount, + }, + // Amount deposited in AHR's sovereign account + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == sov_ahr_on_ahw.clone().into(), + }, + RuntimeEvent::XcmpQueue( + cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. } + ) => {}, + ] + ); + }); + }); +} + +/// Transfer "PEN"s plus "WND"s from PenpalWestend to AssetHubWestend, over bridge to +/// AssetHubRococo. PENs need to be teleported to AHW, while WNDs reserve-withdrawn, then both +/// reserve transferred further to AHR. (transfer 2 different assets with different transfer types +/// across 3 different chains) +#[test] +fn send_pens_and_wnds_from_penpal_westend_via_ahw_to_ahr() { + let penpal_check_account = ::PolkadotXcm::check_account(); + let owner: AccountId = AssetHubRococo::account_id_of(ALICE); + let sender = PenpalBSender::get(); + let amount = ASSET_HUB_WESTEND_ED * 10_000_000; + + let (wnd_at_westend_parachains, wnd_at_rococo_parachains) = + set_up_wnds_for_penpal_westend_through_ahw_to_ahr(&sender, amount); + + let pens_location_on_penpal = + Location::try_from(PenpalLocalTeleportableToAssetHub::get()).unwrap(); + let pens_id_on_penpal = match pens_location_on_penpal.last() { + Some(Junction::GeneralIndex(id)) => *id as u32, + _ => unreachable!(), + }; + + let penpal_parachain_junction = Junction::Parachain(PenpalB::para_id().into()); + let pens_at_ahw = Location::new( + 1, + pens_location_on_penpal + .interior() + .clone() + .pushed_front_with(penpal_parachain_junction) + .unwrap(), + ); + let pens_at_rococo_parachains = Location::new( + 2, + pens_at_ahw + .interior() + .clone() + .pushed_front_with(Junction::GlobalConsensus(NetworkId::ByGenesis( + WESTEND_GENESIS_HASH, + ))) + .unwrap(), + ); + let wnds_to_send = amount; + let pens_to_send = amount; + + // ---------- Set up Penpal Westend ---------- + // Fund Penpal's sender account. No need to create the asset (only mint), it exists in genesis. + PenpalB::mint_asset( + ::RuntimeOrigin::signed(owner.clone()), + pens_id_on_penpal, + sender.clone(), + pens_to_send * 2, + ); + // fund Penpal's check account to be able to teleport + PenpalB::fund_accounts(vec![(penpal_check_account.clone().into(), pens_to_send * 2)]); + + // ---------- Set up Asset Hub Rococo ---------- + // create PEN at AHR + AssetHubRococo::force_create_foreign_asset( + pens_at_rococo_parachains.clone(), + owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + + // account balances before + let sender_wnds_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + wnd_at_westend_parachains.clone().into(), + &PenpalBSender::get(), + ) + }); + let sender_pens_before = PenpalB::execute_with(|| { + type Assets = ::Assets; + >::balance(pens_id_on_penpal, &PenpalBSender::get()) + }); + let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( + ByGenesis(ROCOCO_GENESIS_HASH), + AssetHubRococo::para_id(), + ); + let wnds_in_reserve_on_ahw_before = + ::account_data_of(sov_ahr_on_ahw.clone()).free; + let pens_in_reserve_on_ahw_before = AssetHubWestend::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(pens_at_ahw.clone(), &sov_ahr_on_ahw) + }); + let receiver_wnds_before = AssetHubRococo::execute_with(|| { + type Assets = ::ForeignAssets; + >::balance( + wnd_at_rococo_parachains.clone(), + &AssetHubRococoReceiver::get(), + ) + }); + let receiver_pens_before = AssetHubRococo::execute_with(|| { + type Assets = ::ForeignAssets; + >::balance( + pens_at_rococo_parachains.clone(), + &AssetHubRococoReceiver::get(), + ) + }); + + // transfer assets + do_send_pens_and_wnds_from_penpal_westend_via_ahw_to_asset_hub_rococo( + (wnd_at_westend_parachains.clone(), wnds_to_send), + (pens_location_on_penpal.try_into().unwrap(), pens_to_send), + ); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + AssetHubRococo, + vec![ + // issue WNDs on AHR + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == wnd_at_westend_parachains.clone().try_into().unwrap(), + owner: *owner == AssetHubRococoReceiver::get(), + }, + // message processed successfully + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + }); + + // account balances after + let sender_wnds_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance( + wnd_at_westend_parachains.into(), + &PenpalBSender::get(), + ) + }); + let sender_pens_after = PenpalB::execute_with(|| { + type Assets = ::Assets; + >::balance(pens_id_on_penpal, &PenpalBSender::get()) + }); + let wnds_in_reserve_on_ahw_after = + ::account_data_of(sov_ahr_on_ahw.clone()).free; + let pens_in_reserve_on_ahw_after = AssetHubWestend::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(pens_at_ahw, &sov_ahr_on_ahw) + }); + let receiver_wnds_after = AssetHubRococo::execute_with(|| { + type Assets = ::ForeignAssets; + >::balance( + wnd_at_rococo_parachains.clone(), + &AssetHubRococoReceiver::get(), + ) + }); + let receiver_pens_after = AssetHubRococo::execute_with(|| { + type Assets = ::ForeignAssets; + >::balance(pens_at_rococo_parachains, &AssetHubRococoReceiver::get()) + }); + + // Sender's balance is reduced + assert!(sender_wnds_after < sender_wnds_before); + // Receiver's balance is increased + assert!(receiver_wnds_after > receiver_wnds_before); + // Reserve balance is increased by sent amount (less fess) + assert!(wnds_in_reserve_on_ahw_after > wnds_in_reserve_on_ahw_before); + assert!(wnds_in_reserve_on_ahw_after <= wnds_in_reserve_on_ahw_before + wnds_to_send); + + // Sender's balance is reduced by sent amount + assert_eq!(sender_pens_after, sender_pens_before - pens_to_send); + // Reserve balance is increased by sent amount + assert_eq!(pens_in_reserve_on_ahw_after, pens_in_reserve_on_ahw_before + pens_to_send); + // Receiver's balance is increased by sent amount + assert_eq!(receiver_pens_after, receiver_pens_before + pens_to_send); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/claim_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/claim_assets.rs index e62ce6843258e641ddd1e86dc0c5b006db31e4df..c111eb86501a7f05f70f47b471d789a6797406ef 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/claim_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/claim_assets.rs @@ -25,5 +25,11 @@ fn assets_can_be_claimed() { let amount = BridgeHubWestendExistentialDeposit::get(); let assets: Assets = (Parent, amount).into(); - test_chain_can_claim_assets!(AssetHubWestend, RuntimeCall, NetworkId::Westend, assets, amount); + test_chain_can_claim_assets!( + AssetHubWestend, + RuntimeCall, + NetworkId::ByGenesis(WESTEND_GENESIS_HASH), + assets, + amount + ); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs index af11f0f7ba723fd790e8c026dead84dcefab704c..6c1cdb98e8b2a69b38431d1119d554069e362e4a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -19,16 +19,28 @@ mod asset_transfers; mod claim_assets; mod register_bridged_assets; mod send_xcm; -mod teleport; - mod snowbridge; +mod teleport; +mod transact; pub(crate) fn asset_hub_rococo_location() -> Location { - Location::new(2, [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())]) + Location::new( + 2, + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + Parachain(AssetHubRococo::para_id().into()), + ], + ) } pub(crate) fn bridge_hub_rococo_location() -> Location { - Location::new(2, [GlobalConsensus(Rococo), Parachain(BridgeHubRococo::para_id().into())]) + Location::new( + 2, + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + Parachain(BridgeHubRococo::para_id().into()), + ], + ) } // WND and wWND @@ -36,7 +48,7 @@ pub(crate) fn wnd_at_ah_westend() -> Location { Parent.into() } pub(crate) fn bridged_wnd_at_ah_rococo() -> Location { - Location::new(2, [GlobalConsensus(Westend)]) + Location::new(2, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))]) } // ROC and wROC @@ -44,7 +56,7 @@ pub(crate) fn roc_at_ah_rococo() -> Location { Parent.into() } pub(crate) fn bridged_roc_at_ah_westend() -> Location { - Location::new(2, [GlobalConsensus(Rococo)]) + Location::new(2, [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH))]) } // USDT and wUSDT @@ -55,7 +67,7 @@ pub(crate) fn bridged_usdt_at_ah_rococo() -> Location { Location::new( 2, [ - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(AssetHubWestend::para_id().into()), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into()), @@ -74,13 +86,13 @@ pub(crate) fn weth_at_asset_hubs() -> Location { ) } -pub(crate) fn create_foreign_on_ah_rococo(id: v4::Location, sufficient: bool) { +pub(crate) fn create_foreign_on_ah_rococo(id: v5::Location, sufficient: bool) { let owner = AssetHubRococo::account_id_of(ALICE); AssetHubRococo::force_create_foreign_asset(id, owner, sufficient, ASSET_MIN_BALANCE, vec![]); } pub(crate) fn create_foreign_on_ah_westend( - id: v4::Location, + id: v5::Location, sufficient: bool, prefund_accounts: Vec<(AccountId, u128)>, ) { @@ -89,74 +101,83 @@ pub(crate) fn create_foreign_on_ah_westend( AssetHubWestend::force_create_foreign_asset(id, owner, sufficient, min, prefund_accounts); } -pub(crate) fn foreign_balance_on_ah_rococo(id: v4::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_rococo(id: v5::Location, who: &AccountId) -> u128 { AssetHubRococo::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) }) } -pub(crate) fn foreign_balance_on_ah_westend(id: v4::Location, who: &AccountId) -> u128 { +pub(crate) fn foreign_balance_on_ah_westend(id: v5::Location, who: &AccountId) -> u128 { AssetHubWestend::execute_with(|| { type Assets = ::ForeignAssets; >::balance(id, who) }) } -// set up pool -pub(crate) fn set_up_pool_with_roc_on_ah_rococo(asset: v4::Location, is_foreign: bool) { - let roc: v4::Location = v4::Parent.into(); - AssetHubRococo::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - let owner = AssetHubRococoSender::get(); - let signed_owner = ::RuntimeOrigin::signed(owner.clone()); +/// note: $asset needs to be prefunded outside this function +#[macro_export] +macro_rules! create_pool_with_native_on { + ( $chain:ident, $asset:expr, $is_foreign:expr, $asset_owner:expr ) => { + emulated_integration_tests_common::impls::paste::paste! { + <$chain>::execute_with(|| { + type RuntimeEvent = <$chain as Chain>::RuntimeEvent; + let owner = $asset_owner; + let signed_owner = <$chain as Chain>::RuntimeOrigin::signed(owner.clone()); + let native_asset: Location = Parent.into(); - if is_foreign { - assert_ok!(::ForeignAssets::mint( - signed_owner.clone(), - asset.clone().into(), - owner.clone().into(), - 3_000_000_000_000, - )); - } else { - let asset_id = match asset.interior.last() { - Some(v4::Junction::GeneralIndex(id)) => *id as u32, - _ => unreachable!(), - }; - assert_ok!(::Assets::mint( - signed_owner.clone(), - asset_id.into(), - owner.clone().into(), - 3_000_000_000_000, - )); + if $is_foreign { + assert_ok!(<$chain as [<$chain Pallet>]>::ForeignAssets::mint( + signed_owner.clone(), + $asset.clone().into(), + owner.clone().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + } else { + let asset_id = match $asset.interior.last() { + Some(GeneralIndex(id)) => *id as u32, + _ => unreachable!(), + }; + assert_ok!(<$chain as [<$chain Pallet>]>::Assets::mint( + signed_owner.clone(), + asset_id.into(), + owner.clone().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + } + + assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::create_pool( + signed_owner.clone(), + Box::new(native_asset.clone()), + Box::new($asset.clone()), + )); + + assert_expected_events!( + $chain, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::add_liquidity( + signed_owner, + Box::new(native_asset), + Box::new($asset), + 1_000_000_000_000, + 2_000_000_000_000, // $asset is worth half of native_asset + 0, + 0, + owner.into() + )); + + assert_expected_events!( + $chain, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); } - assert_ok!(::AssetConversion::create_pool( - signed_owner.clone(), - Box::new(roc.clone()), - Box::new(asset.clone()), - )); - assert_expected_events!( - AssetHubRococo, - vec![ - RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, - ] - ); - assert_ok!(::AssetConversion::add_liquidity( - signed_owner.clone(), - Box::new(roc), - Box::new(asset), - 1_000_000_000_000, - 2_000_000_000_000, - 1, - 1, - owner.into() - )); - assert_expected_events!( - AssetHubRococo, - vec![ - RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded {..}) => {}, - ] - ); - }); + }; } pub(crate) fn send_assets_from_asset_hub_westend( @@ -239,7 +260,11 @@ pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), ROC * 5); AssetHubRococo::open_bridge( AssetHubRococo::sibling_location_of(BridgeHubRococo::para_id()), - [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())].into(), + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(AssetHubWestend::para_id().into()), + ] + .into(), Some(( (roc_at_ah_rococo(), ROC * 1).into(), BridgeHubRococo::sovereign_account_id_of(BridgeHubRococo::sibling_location_of( @@ -252,7 +277,11 @@ pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5); AssetHubWestend::open_bridge( AssetHubWestend::sibling_location_of(BridgeHubWestend::para_id()), - [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())].into(), + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + Parachain(AssetHubRococo::para_id().into()), + ] + .into(), Some(( (wnd_at_ah_westend(), WND * 1).into(), BridgeHubWestend::sovereign_account_id_of(BridgeHubWestend::sibling_location_of( diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/register_bridged_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/register_bridged_assets.rs index 7a7ad6da2d55dd349eb250055e4ccdeb44e9c035..424f1e55956bda9cc5f87428540895abbdad3554 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/register_bridged_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/register_bridged_assets.rs @@ -30,7 +30,7 @@ fn register_westend_asset_on_rah_from_wah() { let bridged_asset_at_rah = Location::new( 2, [ - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(AssetHubWestend::para_id().into()), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into()), @@ -57,7 +57,7 @@ fn register_ethereum_asset_on_rah_from_wah() { fn register_asset_on_rah_from_wah(bridged_asset_at_rah: Location) { let sa_of_wah_on_rah = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - Westend, + ByGenesis(WESTEND_GENESIS_HASH), AssetHubWestend::para_id(), ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs index ae05e4223b073de9b8f8edb7907b15a7a4cf7ed6..787d7dc842cb27b33dd771c62b43f15361611ad6 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs @@ -29,7 +29,7 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable let xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit, check_origin }, ExportMessage { - network: RococoId, + network: ByGenesis(ROCOCO_GENESIS_HASH), destination: [Parachain(AssetHubRococo::para_id().into())].into(), xcm: remote_xcm, }, @@ -60,15 +60,6 @@ fn send_xcm_from_westend_relay_to_rococo_asset_hub_should_fail_on_not_applicable #[test] fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() { - // Initially set only default version on all runtimes - let newer_xcm_version = xcm::prelude::XCM_VERSION; - let older_xcm_version = newer_xcm_version - 1; - - AssetHubRococo::force_default_xcm_version(Some(older_xcm_version)); - BridgeHubRococo::force_default_xcm_version(Some(older_xcm_version)); - BridgeHubWestend::force_default_xcm_version(Some(older_xcm_version)); - AssetHubWestend::force_default_xcm_version(Some(older_xcm_version)); - // prepare data let destination = asset_hub_rococo_location(); let native_token = Location::parent(); @@ -82,6 +73,14 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() { // open bridge open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // Initially set only default version on all runtimes + let newer_xcm_version = xcm::prelude::XCM_VERSION; + let older_xcm_version = newer_xcm_version - 1; + AssetHubRococo::force_default_xcm_version(Some(older_xcm_version)); + BridgeHubRococo::force_default_xcm_version(Some(older_xcm_version)); + BridgeHubWestend::force_default_xcm_version(Some(older_xcm_version)); + AssetHubWestend::force_default_xcm_version(Some(older_xcm_version)); + // send XCM from AssetHubWestend - fails - destination version not known assert_err!( send_assets_from_asset_hub_westend( diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 4e9dd5a77dd7b14ffb6ce24754ceb957ef14ea56..ffa60a4f52e746668da4f76a882d5e46789ffa63 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -22,7 +22,7 @@ use hex_literal::hex; use rococo_westend_system_emulated_network::asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner; use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ - Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, + Command, Destination, EthereumLocationsConverterFor, MessageV1, VersionedMessage, }; use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; @@ -96,8 +96,10 @@ fn send_token_from_ethereum_to_asset_hub() { // Fund ethereum sovereign on AssetHub AssetHubWestend::fund_accounts(vec![(AssetHubWestendReceiver::get(), INITIAL_FUND)]); + let ethereum_network_v5: NetworkId = EthereumNetwork::get().into(); + let weth_asset_location: Location = - (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); + (Parent, Parent, ethereum_network_v5, AccountKey20 { network: None, key: WETH }).into(); AssetHubWestend::execute_with(|| { type RuntimeOrigin = ::RuntimeOrigin; @@ -156,8 +158,9 @@ fn send_token_from_ethereum_to_asset_hub() { fn send_weth_asset_from_asset_hub_to_ethereum() { let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); + let ethereum_network_v5: NetworkId = EthereumNetwork::get().into(); let weth_asset_location: Location = - (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); + (Parent, Parent, ethereum_network_v5, AccountKey20 { network: None, key: WETH }).into(); BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); @@ -218,14 +221,14 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { )), fun: Fungible(TOKEN_AMOUNT), }]; - let versioned_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::from(Assets::from(assets)); - let destination = VersionedLocation::V4(Location::new( + let destination = VersionedLocation::from(Location::new( 2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], )); - let beneficiary = VersionedLocation::V4(Location::new( + let beneficiary = VersionedLocation::from(Location::new( 0, [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], )); @@ -291,13 +294,15 @@ fn transfer_relay_token() { BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); let asset_id: Location = Location { parents: 1, interior: [].into() }; - let expected_asset_id: Location = - Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; + let expected_asset_id: Location = Location { + parents: 1, + interior: [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))].into(), + }; let expected_token_id = TokenIdOf::convert_location(&expected_asset_id).unwrap(); let ethereum_sovereign: AccountId = - GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&Location::new( + EthereumLocationsConverterFor::<[u8; 32]>::convert_location(&Location::new( 2, [GlobalConsensus(EthereumNetwork::get())], )) @@ -317,7 +322,7 @@ fn transfer_relay_token() { assert_ok!(::EthereumSystem::register_token( RuntimeOrigin::root(), - Box::new(VersionedLocation::V4(asset_id.clone())), + Box::new(VersionedLocation::from(asset_id.clone())), AssetMetadata { name: "wnd".as_bytes().to_vec().try_into().unwrap(), symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), @@ -337,14 +342,14 @@ fn transfer_relay_token() { type RuntimeEvent = ::RuntimeEvent; let assets = vec![Asset { id: AssetId(Location::parent()), fun: Fungible(TOKEN_AMOUNT) }]; - let versioned_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::from(Assets::from(assets)); - let destination = VersionedLocation::V4(Location::new( + let destination = VersionedLocation::from(Location::new( 2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], )); - let beneficiary = VersionedLocation::V4(Location::new( + let beneficiary = VersionedLocation::from(Location::new( 0, [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], )); @@ -445,7 +450,7 @@ fn transfer_ah_token() { let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); let ethereum_sovereign: AccountId = - GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) + EthereumLocationsConverterFor::<[u8; 32]>::convert_location(ðereum_destination) .unwrap() .into(); AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); @@ -462,10 +467,15 @@ fn transfer_ah_token() { ], ); - let asset_id_after_reanchored = - Location::new(1, [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())]) - .appended_with(asset_id.clone().interior) - .unwrap(); + let asset_id_after_reanchored = Location::new( + 1, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(AssetHubWestend::para_id().into()), + ], + ) + .appended_with(asset_id.clone().interior) + .unwrap(); let token_id = TokenIdOf::convert_location(&asset_id_after_reanchored).unwrap(); @@ -475,7 +485,7 @@ fn transfer_ah_token() { assert_ok!(::EthereumSystem::register_token( RuntimeOrigin::root(), - Box::new(VersionedLocation::V4(asset_id_in_bh.clone())), + Box::new(VersionedLocation::from(asset_id_in_bh.clone())), AssetMetadata { name: "ah_asset".as_bytes().to_vec().try_into().unwrap(), symbol: "ah_asset".as_bytes().to_vec().try_into().unwrap(), @@ -500,9 +510,9 @@ fn transfer_ah_token() { // Send partial of the token, will fail if send all let assets = vec![Asset { id: AssetId(asset_id.clone()), fun: Fungible(TOKEN_AMOUNT / 10) }]; - let versioned_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::from(Assets::from(assets)); - let beneficiary = VersionedLocation::V4(Location::new( + let beneficiary = VersionedLocation::from(Location::new( 0, [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], )); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/transact.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/transact.rs new file mode 100644 index 0000000000000000000000000000000000000000..db42704dae614304cfc74f044cf60cf8fd7c5bef --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/transact.rs @@ -0,0 +1,248 @@ +// 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::{ + create_pool_with_native_on, + tests::{snowbridge::CHAIN_ID, *}, +}; +use sp_core::Get; +use xcm::latest::AssetTransferFilter; + +const ETHEREUM_BOB: [u8; 20] = hex_literal::hex!("11b0b11000011b0b11000011b0b11000011b0b11"); + +/// Bob on Ethereum transacts on PenpalB, paying fees using WETH. XCM has to go through Asset Hub +/// as the reserve location of WETH. The original origin `Ethereum/Bob` is proxied by Asset Hub. +/// +/// This particular test is not testing snowbridge, but only Bridge Hub, so the tested XCM flow from +/// Ethereum starts from Bridge Hub. +// TODO(https://github.com/paritytech/polkadot-sdk/issues/6243): Once Snowbridge supports Transact, start the flow from Ethereum and test completely e2e. +fn transfer_and_transact_in_same_xcm( + sender: Location, + weth: Asset, + destination: Location, + beneficiary: Location, + call: xcm::DoubleEncoded<()>, +) { + let signed_origin = ::RuntimeOrigin::root(); + let context: InteriorLocation = [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(::ParachainInfo::get().into()), + ] + .into(); + let asset_hub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); + + // TODO(https://github.com/paritytech/polkadot-sdk/issues/6197): dry-run to get local fees, for now use hardcoded value. + let ah_fees_amount = 90_000_000_000u128; // current exact value 79_948_099_299 + let fees_for_ah: Asset = (weth.id.clone(), ah_fees_amount).into(); + + // xcm to be executed at dest + let xcm_on_dest = Xcm(vec![ + Transact { origin_kind: OriginKind::Xcm, call }, + ExpectTransactStatus(MaybeErrorCode::Success), + // since this is the last hop, we don't need to further use any assets previously + // reserved for fees (there are no further hops to cover transport fees for); we + // RefundSurplus to get back any unspent fees + RefundSurplus, + DepositAsset { assets: Wild(All), beneficiary }, + ]); + let destination = destination.reanchored(&asset_hub_location, &context).unwrap(); + let xcm_to_ah = Xcm::<()>(vec![ + UnpaidExecution { check_origin: None, weight_limit: Unlimited }, + DescendOrigin([PalletInstance(80)].into()), // snowbridge pallet + UniversalOrigin(GlobalConsensus(Ethereum { chain_id: CHAIN_ID })), + ReserveAssetDeposited(weth.clone().into()), + AliasOrigin(sender), + PayFees { asset: fees_for_ah }, + InitiateTransfer { + destination, + // on the last hop we can just put everything in fees and `RefundSurplus` to get any + // unused back + remote_fees: Some(AssetTransferFilter::ReserveDeposit(Wild(All))), + preserve_origin: true, + assets: vec![], + remote_xcm: xcm_on_dest, + }, + ]); + ::PolkadotXcm::send( + signed_origin, + bx!(asset_hub_location.into()), + bx!(xcm::VersionedXcm::from(xcm_to_ah.into())), + ) + .unwrap(); +} + +/// Bob on Ethereum transacts on PenpalB, paying fees using WETH. XCM has to go through Asset Hub +/// as the reserve location of WETH. The original origin `Ethereum/Bob` is proxied by Asset Hub. +/// +/// This particular test is not testing snowbridge, but only Bridge Hub, so the tested XCM flow from +/// Ethereum starts from Bridge Hub. +// TODO(https://github.com/paritytech/polkadot-sdk/issues/6243): Once Snowbridge supports Transact, start the flow from Ethereum and test completely e2e. +#[test] +fn transact_from_ethereum_to_penpalb_through_asset_hub() { + // Snowbridge doesn't support transact yet, we are emulating it by sending one from Bridge Hub + // as if it comes from Snowbridge. + let destination = BridgeHubWestend::sibling_location_of(PenpalB::para_id()); + let sender = Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: CHAIN_ID }), + AccountKey20 { network: None, key: ETHEREUM_BOB }, + ], + ); + + let bridged_weth = weth_at_asset_hubs(); + AssetHubWestend::force_create_foreign_asset( + bridged_weth.clone(), + PenpalAssetOwner::get(), + true, + ASSET_MIN_BALANCE, + vec![], + ); + PenpalB::force_create_foreign_asset( + bridged_weth.clone(), + PenpalAssetOwner::get(), + true, + ASSET_MIN_BALANCE, + vec![], + ); + // Configure source Penpal chain to trust local AH as reserve of bridged WETH + PenpalB::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), + bridged_weth.encode(), + )], + )); + }); + + let fee_amount_to_send: parachains_common::Balance = ASSET_HUB_WESTEND_ED * 10000; + let sender_chain_as_seen_by_asset_hub = + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + + let sov_of_sender_on_asset_hub = AssetHubWestend::execute_with(|| { + AssetHubWestend::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub) + }); + let receiver_as_seen_by_asset_hub = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_asset_hub = AssetHubWestend::execute_with(|| { + AssetHubWestend::sovereign_account_id_of(receiver_as_seen_by_asset_hub) + }); + // Create SAs of sender and receiver on AHW with ED. + AssetHubWestend::fund_accounts(vec![ + (sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + (sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + ]); + + // We create a pool between WND and WETH in AssetHub to support paying for fees with WETH. + let ahw_owner = AssetHubWestendSender::get(); + create_pool_with_native_on!(AssetHubWestend, bridged_weth.clone(), true, ahw_owner); + // We also need a pool between WND and WETH on PenpalB to support paying for fees with WETH. + create_pool_with_native_on!(PenpalB, bridged_weth.clone(), true, PenpalAssetOwner::get()); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Query initial balances + let receiver_assets_before = PenpalB::execute_with(|| { + type Assets = ::ForeignAssets; + >::balance(bridged_weth.clone(), &receiver) + }); + + // Now register a new asset on PenpalB from Ethereum/Bob account while paying fees using WETH + // (going through Asset Hub) + let weth_to_send: Asset = (bridged_weth.clone(), fee_amount_to_send).into(); + // Silly example of a Transact: Bob creates his own foreign assset on PenpalB based on his + // Ethereum address + let foreign_asset_at_penpal_b = Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: CHAIN_ID }), + AccountKey20 { network: None, key: ETHEREUM_BOB }, + ], + ); + // Encoded `create_asset` call to be executed in PenpalB + let call = PenpalB::create_foreign_asset_call( + foreign_asset_at_penpal_b.clone(), + ASSET_MIN_BALANCE, + receiver.clone(), + ); + BridgeHubWestend::execute_with(|| { + // initiate transaction + transfer_and_transact_in_same_xcm( + sender.clone(), + weth_to_send, + destination, + receiver.clone().into(), + call, + ); + }); + AssetHubWestend::execute_with(|| { + let sov_penpal_b_on_ah = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalB::para_id()), + ); + asset_hub_hop_assertions(sov_penpal_b_on_ah); + }); + PenpalB::execute_with(|| { + let expected_creator = PenpalB::sovereign_account_id_of(sender); + penpal_b_assertions(foreign_asset_at_penpal_b, expected_creator, receiver.clone()); + }); + + // Query final balances + let receiver_assets_after = PenpalB::execute_with(|| { + type Assets = ::ForeignAssets; + >::balance(bridged_weth, &receiver) + }); + // Receiver's balance is increased + assert!(receiver_assets_after > receiver_assets_before); +} + +fn asset_hub_hop_assertions(receiver_sa: AccountId) { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![ + // Deposited to receiver parachain SA + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Deposited { who, .. } + ) => { + who: *who == receiver_sa, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + +fn penpal_b_assertions( + expected_asset: Location, + expected_creator: AccountId, + expected_owner: AccountId, +) { + type RuntimeEvent = ::RuntimeEvent; + PenpalB::assert_xcmp_queue_success(None); + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Created { asset_id, creator, owner } + ) => { + asset_id: *asset_id == expected_asset, + creator: *creator == expected_creator, + owner: *owner == expected_owner, + }, + ] + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs index f97599bda7f08be29d3388cd4a0856db77a30858..80b82e0c446f79d633b725806ecad27a69c2edf8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship.rs @@ -36,7 +36,6 @@ fn fellows_whitelist_call() { UnpaidExecution { weight_limit: Unlimited, check_origin: None }, Transact { origin_kind: OriginKind::Xcm, - require_weight_at_most: Weight::from_parts(5_000_000_000, 500_000), call: WestendCall::Whitelist( pallet_whitelist::Call::::whitelist_call { call_hash } ) diff --git a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_treasury.rs index 943f8965540d5cb0c17c7a0b1aac8797cbd3ed42..8418e3da3bba0731a56c6b9dfbeb378d089adf28 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend/src/tests/fellowship_treasury.rs @@ -64,11 +64,12 @@ fn fellowship_treasury_spend() { let teleport_call = RuntimeCall::Utility(pallet_utility::Call::::dispatch_as { as_origin: bx!(WestendOriginCaller::system(RawOrigin::Signed(treasury_account))), call: bx!(RuntimeCall::XcmPallet(pallet_xcm::Call::::teleport_assets { - dest: bx!(VersionedLocation::V4(asset_hub_location.clone())), - beneficiary: bx!(VersionedLocation::V4(treasury_location)), - assets: bx!(VersionedAssets::V4( - Asset { id: native_asset.clone().into(), fun: treasury_balance.into() }.into() - )), + dest: bx!(VersionedLocation::from(asset_hub_location.clone())), + beneficiary: bx!(VersionedLocation::from(treasury_location)), + assets: bx!(VersionedAssets::from(Assets::from(Asset { + id: native_asset.clone().into(), + fun: treasury_balance.into() + }))), fee_asset_item: 0, })), }); @@ -101,12 +102,12 @@ fn fellowship_treasury_spend() { let native_asset = Location::parent(); let treasury_spend_call = RuntimeCall::Treasury(pallet_treasury::Call::::spend { - asset_kind: bx!(VersionedLocatableAsset::V4 { - location: asset_hub_location.clone(), - asset_id: native_asset.into(), - }), + asset_kind: bx!(VersionedLocatableAsset::from(( + asset_hub_location.clone(), + native_asset.into() + ))), amount: fellowship_treasury_balance, - beneficiary: bx!(VersionedLocation::V4(fellowship_treasury_location)), + beneficiary: bx!(VersionedLocation::from(fellowship_treasury_location)), valid_from: None, }); @@ -179,12 +180,12 @@ fn fellowship_treasury_spend() { let fellowship_treasury_spend_call = RuntimeCall::FellowshipTreasury(pallet_treasury::Call::::spend { - asset_kind: bx!(VersionedLocatableAsset::V4 { - location: asset_hub_location, - asset_id: native_asset.into(), - }), + asset_kind: bx!(VersionedLocatableAsset::from(( + asset_hub_location, + native_asset.into() + ))), amount: fellowship_spend_balance, - beneficiary: bx!(VersionedLocation::V4(alice_location)), + beneficiary: bx!(VersionedLocation::from(alice_location)), valid_from: None, }); diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs index 055bd50d82987926d60d6ed305670b97c7943d63..d3fec42303689fdcf9def2bc8c2c022fa47c51f4 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs @@ -20,7 +20,7 @@ mod imports { pub use frame_support::assert_ok; // Polkadot - pub use xcm::prelude::*; + pub use xcm::{latest::ROCOCO_GENESIS_HASH, prelude::*}; // Cumulus pub use emulated_integration_tests_common::xcm_emulator::{ diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/claim_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/claim_assets.rs index e37b915174d36d33d86ce0ff2c7fbef2b3c91024..bdab86f5cbf286088d0ec7b48178d87be88ccdb5 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/claim_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/claim_assets.rs @@ -25,5 +25,11 @@ fn assets_can_be_claimed() { let amount = CoretimeRococoExistentialDeposit::get(); let assets: Assets = (Parent, amount).into(); - test_chain_can_claim_assets!(CoretimeRococo, RuntimeCall, NetworkId::Rococo, assets, amount); + test_chain_can_claim_assets!( + CoretimeRococo, + RuntimeCall, + NetworkId::ByGenesis(ROCOCO_GENESIS_HASH), + assets, + amount + ); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs index ac844e0f3284e16c7e8484ce69fcde9f04ff295b..4fb619aba3d39c8a0a4b8549e65a10fb4811f86b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs @@ -20,7 +20,7 @@ mod imports { pub use frame_support::assert_ok; // Polkadot - pub use xcm::prelude::*; + pub use xcm::{latest::WESTEND_GENESIS_HASH, prelude::*}; // Cumulus pub use emulated_integration_tests_common::xcm_emulator::{ diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/claim_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/claim_assets.rs index c8d853698444c3dfc047ae1b860eee742efca004..3cabc3f8ac5167e8cbe7574b85ec960a3286d255 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/claim_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/claim_assets.rs @@ -25,5 +25,11 @@ fn assets_can_be_claimed() { let amount = CoretimeWestendExistentialDeposit::get(); let assets: Assets = (Parent, amount).into(); - test_chain_can_claim_assets!(CoretimeWestend, RuntimeCall, NetworkId::Westend, assets, amount); + test_chain_can_claim_assets!( + CoretimeWestend, + RuntimeCall, + NetworkId::ByGenesis(WESTEND_GENESIS_HASH), + assets, + amount + ); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs index 06b0b6ba600534793b83f24e6e8d9b9d730736bd..a95396d5070b30ebe9f53bf05a19da0f53794328 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs @@ -19,7 +19,7 @@ mod imports { pub use frame_support::{assert_ok, sp_runtime::DispatchResult, traits::fungibles::Inspect}; // Polkadot - pub use xcm::prelude::*; + pub use xcm::{latest::ROCOCO_GENESIS_HASH, prelude::*}; // Cumulus pub use asset_test_utils::xcm_helpers; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/claim_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/claim_assets.rs index 793200e1d06b870327942229252d56bcb46405f9..6795b1e7f39779df269499be9dc3dd0f3ec24f06 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/claim_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/claim_assets.rs @@ -25,5 +25,11 @@ fn assets_can_be_claimed() { let amount = PeopleRococoExistentialDeposit::get(); let assets: Assets = (Parent, amount).into(); - test_chain_can_claim_assets!(PeopleRococo, RuntimeCall, NetworkId::Rococo, assets, amount); + test_chain_can_claim_assets!( + PeopleRococo, + RuntimeCall, + NetworkId::ByGenesis(ROCOCO_GENESIS_HASH), + assets, + amount + ); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs index 418cfea07ddc2b7fec7e372fd7c54e62631f4df8..59d87e1ea3f032225a598195269535445b6d21f7 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs @@ -19,7 +19,7 @@ mod imports { pub use frame_support::{assert_ok, sp_runtime::DispatchResult, traits::fungibles::Inspect}; // Polkadot - pub use xcm::prelude::*; + pub use xcm::{latest::WESTEND_GENESIS_HASH, prelude::*}; // Cumulus pub use asset_test_utils::xcm_helpers; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/claim_assets.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/claim_assets.rs index 42ccc459286a2dde06c0fc96dc125a527e649973..055c713abfd8c51e1fb061eea7aa5152025d4c12 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/claim_assets.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/claim_assets.rs @@ -25,5 +25,11 @@ fn assets_can_be_claimed() { let amount = PeopleWestendExistentialDeposit::get(); let assets: Assets = (Parent, amount).into(); - test_chain_can_claim_assets!(PeopleWestend, RuntimeCall, NetworkId::Westend, assets, amount); + test_chain_can_claim_assets!( + PeopleWestend, + RuntimeCall, + NetworkId::ByGenesis(WESTEND_GENESIS_HASH), + assets, + amount + ); } diff --git a/cumulus/parachains/pallets/ping/src/lib.rs b/cumulus/parachains/pallets/ping/src/lib.rs index 729494cbd251d6ff5ea3271a040c28bb786ea849..2cf32c891fc0b88838c1ce0ce99998f0f0fdf2f4 100644 --- a/cumulus/parachains/pallets/ping/src/lib.rs +++ b/cumulus/parachains/pallets/ping/src/lib.rs @@ -108,7 +108,6 @@ pub mod pallet { (Parent, Junction::Parachain(para.into())).into(), Xcm(vec![Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(1_000, 1_000), call: ::RuntimeCall::from(Call::::ping { seq, payload: payload.clone().to_vec(), @@ -209,7 +208,6 @@ pub mod pallet { (Parent, Junction::Parachain(para.into())).into(), Xcm(vec![Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(1_000, 1_000), call: ::RuntimeCall::from(Call::::pong { seq, payload: payload.clone(), diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs index d15dd971a8194812f6120858d35b3e0261088d34..d58d2f6d5f4d68b7bbb1e38dc3457b1ea286e156 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs @@ -67,8 +67,8 @@ mod preset_names { /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &PresetId) -> Option> { use preset_names::*; - let patch = match id.try_into() { - Ok(PRESET_GENESIS) => asset_hub_rococo_genesis( + let patch = match id.as_ref() { + PRESET_GENESIS => asset_hub_rococo_genesis( // initial collators. vec![ // E8XC6rTJRsioKCp6KMy6zd24ykj4gWsusZ3AkSeyavpVBAG @@ -100,7 +100,7 @@ pub fn get_preset(id: &PresetId) -> Option> { ASSET_HUB_ROCOCO_ED * 524_288, 1000.into(), ), - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => asset_hub_rococo_genesis( + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => asset_hub_rococo_genesis( // initial collators. vec![ (Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into()), @@ -110,7 +110,7 @@ pub fn get_preset(id: &PresetId) -> Option> { testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, 1000.into(), ), - Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => asset_hub_rococo_genesis( + sp_genesis_builder::DEV_RUNTIME_PRESET => asset_hub_rococo_genesis( // initial collators. vec![(Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into())], vec![ 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 420fa67f41f3824bbf3397dec7fd0dd972237831..2f9d83bd9d0b1687804eec288676ca632d7611c7 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -42,7 +42,7 @@ use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSele use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{AccountIdConversion, BlakeTwo256, Block as BlockT, Saturating, Verify}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Permill, @@ -120,8 +120,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("statemine"), - impl_name: create_runtime_str!("statemine"), + spec_name: alloc::borrow::Cow::Borrowed("statemine"), + impl_name: alloc::borrow::Cow::Borrowed("statemine"), authoring_version: 1, spec_version: 1_016_002, impl_version: 0, @@ -330,11 +330,11 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, LocalFromLeft< - AssetIdForTrustBackedAssetsConvert, + AssetIdForTrustBackedAssetsConvert, AssetIdForTrustBackedAssets, - xcm::v4::Location, + xcm::v5::Location, >, - xcm::v4::Location, + xcm::v5::Location, AccountId, >; @@ -342,21 +342,21 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< pub type NativeAndAssets = fungible::UnionOf< Balances, LocalAndForeignAssets, - TargetFromLeft, - xcm::v4::Location, + TargetFromLeft, + xcm::v5::Location, AccountId, >; pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< AssetConversionPalletId, - (xcm::v4::Location, xcm::v4::Location), + (xcm::v5::Location, xcm::v5::Location), >; impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; - type AssetKind = xcm::v4::Location; + type AssetKind = xcm::v5::Location; type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset< @@ -381,7 +381,7 @@ impl pallet_asset_conversion::Config for Runtime { TokenLocation, parachain_info::Pallet, xcm_config::TrustBackedAssetsPalletIndex, - xcm::v4::Location, + xcm::v5::Location, >; } @@ -415,18 +415,18 @@ pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; - type AssetId = xcm::v4::Location; - type AssetIdParameter = xcm::v4::Location; + type AssetId = xcm::v5::Location; + type AssetIdParameter = xcm::v5::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< ( - FromSiblingParachain, xcm::v4::Location>, - FromNetwork, + FromSiblingParachain, xcm::v5::Location>, + FromNetwork, xcm_config::bridging::to_westend::WestendOrEthereumAssetFromAssetHubWestend, ), LocationToAccountId, AccountId, - xcm::v4::Location, + xcm::v5::Location, >; type ForceOrigin = AssetsForceOrigin; type AssetDeposit = ForeignAssetsAssetDeposit; @@ -813,7 +813,7 @@ parameter_types! { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AssetId = xcm::v4::Location; + type AssetId = xcm::v5::Location; type OnChargeAssetTransaction = SwapAssetAdapter< TokenLocation, NativeAndAssets, @@ -1308,16 +1308,16 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - xcm::v4::Location, + xcm::v5::Location, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: xcm::v5::Location, asset2: xcm::v5::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: xcm::v5::Location, asset2: xcm::v5::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: xcm::v4::Location, asset2: xcm::v4::Location) -> Option<(Balance, Balance)> { + fn get_reserves(asset1: xcm::v5::Location, asset2: xcm::v5::Location) -> Option<(Balance, Balance)> { AssetConversion::get_reserves(asset1, asset2).ok() } } @@ -1414,7 +1414,7 @@ impl_runtime_apis! { // We also accept all assets in a pool with the native token. let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::< Runtime, - xcm::v4::Location + xcm::v5::Location >(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter(); acceptable_assets.extend(assets_in_pool_with_native); PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) @@ -1431,7 +1431,7 @@ impl_runtime_apis! { Ok(asset_id) => { let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::< Runtime, - xcm::v4::Location + xcm::v5::Location >(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?; if assets_in_pool_with_this_asset .into_iter() @@ -1556,7 +1556,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; @@ -1834,7 +1834,12 @@ impl_runtime_apis! { } fn alias_origin() -> Result<(Location, Location), BenchmarkError> { - Err(BenchmarkError::Skip) + // Any location can alias to an internal location. + // Here parachain 1001 aliases to an internal account. + Ok(( + Location::new(1, [Parachain(1001)]), + Location::new(1, [Parachain(1001), AccountId32 { id: [111u8; 32], network: None }]), + )) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/mod.rs index 8c52ecd9f1b1fa66bc027be7b1d376ff6882517a..bf374fc415ced2bbfd9ba71b9b1e275e95dfd7b5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/mod.rs @@ -22,7 +22,10 @@ use alloc::vec::Vec; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use xcm::{latest::prelude::*, DoubleEncoded}; +use xcm::{ + latest::{prelude::*, AssetTransferFilter}, + DoubleEncoded, +}; trait WeighAssets { fn weigh_assets(&self, weight: Weight) -> Weight; @@ -81,11 +84,7 @@ impl XcmWeightInfo for AssetHubRococoXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::transfer_reserve_asset()) } - fn transact( - _origin_type: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_type: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -132,12 +131,35 @@ impl XcmWeightInfo for AssetHubRococoXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmFungibleWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmFungibleWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -150,6 +172,9 @@ impl XcmWeightInfo for AssetHubRococoXcmWeight { fn clear_error() -> Weight { XcmGeneric::::clear_error() } + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight { XcmGeneric::::claim_asset() } @@ -223,8 +248,7 @@ impl XcmWeightInfo for AssetHubRococoXcmWeight { XcmGeneric::::clear_topic() } fn alias_origin(_: &Location) -> Weight { - // XCM Executor does not currently support alias origin operations - Weight::MAX + XcmGeneric::::alias_origin() } fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { XcmGeneric::::unpaid_execution() diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 7478ba8893c1b609d7d950a6e0e39ed3d443a730..a2169e2ea04b1eaea17ad04295efa91e047e2edf 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wmcgzesc-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: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 34_364_000 picoseconds. - Weight::from_parts(35_040_000, 3593) + // Minimum execution time: 33_878_000 picoseconds. + Weight::from_parts(34_766_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 42_755_000 picoseconds. - Weight::from_parts(43_650_000, 6196) + // Minimum execution time: 42_776_000 picoseconds. + Weight::from_parts(43_643_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 103_037_000 picoseconds. - Weight::from_parts(105_732_000, 8799) + // Minimum execution time: 104_654_000 picoseconds. + Weight::from_parts(106_518_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -99,8 +99,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_095_000 picoseconds. - Weight::from_parts(1_220_000, 0) + // Minimum execution time: 1_183_000 picoseconds. + Weight::from_parts(1_309_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -122,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 108_117_000 picoseconds. - Weight::from_parts(110_416_000, 6196) + // Minimum execution time: 112_272_000 picoseconds. + Weight::from_parts(114_853_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -131,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_907_000 picoseconds. - Weight::from_parts(3_050_000, 0) + // Minimum execution time: 2_769_000 picoseconds. + Weight::from_parts(2_916_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -140,8 +140,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 24_965_000 picoseconds. - Weight::from_parts(25_687_000, 3593) + // Minimum execution time: 26_145_000 picoseconds. + Weight::from_parts(26_589_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -165,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 83_312_000 picoseconds. - Weight::from_parts(85_463_000, 6196) + // Minimum execution time: 85_446_000 picoseconds. + Weight::from_parts(88_146_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -190,9 +190,34 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 49_874_000 picoseconds. - Weight::from_parts(51_165_000, 3610) + // Minimum execution time: 55_060_000 picoseconds. + Weight::from_parts(56_120_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `6196` + // Minimum execution time: 90_870_000 picoseconds. + Weight::from_parts(93_455_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index f6a883c03e9dd53124a229a827d5732f4973cd1b..ef08b432e5c7862c290566808b2083006149930e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wmcgzesc-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: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 99_552_000 picoseconds. - Weight::from_parts(101_720_000, 6196) + // Minimum execution time: 103_506_000 picoseconds. + Weight::from_parts(106_039_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 659_000 picoseconds. - Weight::from_parts(706_000, 0) + // Minimum execution time: 668_000 picoseconds. + Weight::from_parts(743_000, 0) + } + pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_803_000 picoseconds. + Weight::from_parts(5_983_000, 0) + } + pub fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 644_000 picoseconds. + Weight::from_parts(684_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +100,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3568` - // Minimum execution time: 9_665_000 picoseconds. - Weight::from_parts(9_878_000, 3568) + // Minimum execution time: 9_957_000 picoseconds. + Weight::from_parts(10_163_000, 3568) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_959_000 picoseconds. - Weight::from_parts(7_111_000, 0) + // Minimum execution time: 6_663_000 picoseconds. + Weight::from_parts(7_134_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_682_000 picoseconds. - Weight::from_parts(2_799_000, 0) + // Minimum execution time: 3_067_000 picoseconds. + Weight::from_parts(3_175_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 656_000 picoseconds. - Weight::from_parts(683_000, 0) + // Minimum execution time: 650_000 picoseconds. + Weight::from_parts(691_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 687_000 picoseconds. - Weight::from_parts(719_000, 0) + // Minimum execution time: 669_000 picoseconds. + Weight::from_parts(703_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 588_000 picoseconds. - Weight::from_parts(653_000, 0) + // Minimum execution time: 649_000 picoseconds. + Weight::from_parts(691_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 690_000 picoseconds. - Weight::from_parts(714_000, 0) + Weight::from_parts(735_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 671_000 picoseconds. - Weight::from_parts(710_000, 0) + // Minimum execution time: 681_000 picoseconds. + Weight::from_parts(735_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +173,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 67_374_000 picoseconds. - Weight::from_parts(68_899_000, 6196) + // Minimum execution time: 68_877_000 picoseconds. + Weight::from_parts(69_996_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +184,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 12_896_000 picoseconds. - Weight::from_parts(13_191_000, 3625) + // Minimum execution time: 13_276_000 picoseconds. + Weight::from_parts(13_586_000, 3625) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +193,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 634_000 picoseconds. - Weight::from_parts(677_000, 0) + // Minimum execution time: 659_000 picoseconds. + Weight::from_parts(721_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +214,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 28_197_000 picoseconds. - Weight::from_parts(28_752_000, 3610) + // Minimum execution time: 28_656_000 picoseconds. + Weight::from_parts(29_175_000, 3610) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +225,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_678_000 picoseconds. - Weight::from_parts(2_803_000, 0) + // Minimum execution time: 2_608_000 picoseconds. + Weight::from_parts(2_876_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 22_806_000 picoseconds. - Weight::from_parts(23_217_000, 0) + // Minimum execution time: 24_035_000 picoseconds. + Weight::from_parts(24_315_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_221_000 picoseconds. - Weight::from_parts(6_347_000, 0) + // Minimum execution time: 6_558_000 picoseconds. + Weight::from_parts(6_711_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 653_000 picoseconds. - Weight::from_parts(676_000, 0) + // Minimum execution time: 645_000 picoseconds. + Weight::from_parts(700_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 621_000 picoseconds. - Weight::from_parts(678_000, 0) + // Minimum execution time: 653_000 picoseconds. + Weight::from_parts(696_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 770_000 picoseconds. - Weight::from_parts(829_000, 0) + // Minimum execution time: 787_000 picoseconds. + Weight::from_parts(866_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +284,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 71_654_000 picoseconds. - Weight::from_parts(73_329_000, 6196) + // Minimum execution time: 75_093_000 picoseconds. + Weight::from_parts(76_165_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +293,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_999_000 picoseconds. - Weight::from_parts(4_179_000, 0) + // Minimum execution time: 4_304_000 picoseconds. + Weight::from_parts(4_577_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +316,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 66_722_000 picoseconds. - Weight::from_parts(68_812_000, 6196) + // Minimum execution time: 68_809_000 picoseconds. + Weight::from_parts(70_037_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,22 +325,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 718_000 picoseconds. - Weight::from_parts(745_000, 0) + // Minimum execution time: 715_000 picoseconds. + Weight::from_parts(766_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 623_000 picoseconds. - Weight::from_parts(682_000, 0) + // Minimum execution time: 639_000 picoseconds. + Weight::from_parts(688_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 664_000 picoseconds. - Weight::from_parts(696_000, 0) + // Minimum execution time: 638_000 picoseconds. + Weight::from_parts(712_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -334,22 +348,29 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1489` - // Minimum execution time: 2_495_000 picoseconds. - Weight::from_parts(2_604_000, 1489) + // Minimum execution time: 2_521_000 picoseconds. + Weight::from_parts(2_715_000, 1489) .saturating_add(T::DbWeight::get().reads(1)) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 645_000 picoseconds. - Weight::from_parts(673_000, 0) + // Minimum execution time: 619_000 picoseconds. + Weight::from_parts(692_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 643_000 picoseconds. - Weight::from_parts(701_000, 0) + // Minimum execution time: 665_000 picoseconds. + Weight::from_parts(716_000, 0) + } + pub fn alias_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 668_000 picoseconds. + Weight::from_parts(726_000, 0) } } 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 637c5900f7da42030e552db9192b16a2dad1deaa..66743fa3a07ea1e598d9082f776072f9bbd2bb2c 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 @@ -28,7 +28,7 @@ use frame_support::{ parameter_types, traits::{ tokens::imbalance::{ResolveAssetTo, ResolveTo}, - ConstU32, Contains, Equals, Everything, Nothing, PalletInfoAccess, + ConstU32, Contains, Equals, Everything, PalletInfoAccess, }, }; use frame_system::EnsureRoot; @@ -42,31 +42,32 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; -use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; +use snowbridge_router_primitives::inbound::EthereumLocationsConverterFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto, TryConvertInto}; use testnet_parachains_constants::rococo::snowbridge::{ EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, }; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}; use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, - AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, - EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, - GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, - SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, - WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, + AccountId32Aliases, AliasChildLocation, AllowExplicitUnpaidExecutionFrom, + AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, + DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, + FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, + IsConcrete, LocalMint, MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, + NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, + SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithLatestLocationConverter, WithUniqueTopic, + XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; parameter_types! { pub const TokenLocation: Location = Location::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Rococo; + pub const RelayNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); @@ -104,7 +105,7 @@ pub type LocationToAccountId = ( GlobalConsensusParachainConvertsFor, // Ethereum contract sovereign account. // (Used to get convert ethereum contract locations to sovereign account) - GlobalConsensusEthereumConvertsFor, + EthereumLocationsConverterFor, ); /// Means for transacting the native currency on this chain. @@ -175,7 +176,7 @@ pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConverte StartsWithExplicitGlobalConsensus, ), Balance, - xcm::v4::Location, + xcm::v5::Location, >; /// Means for transacting foreign assets from different global consensus. @@ -335,14 +336,14 @@ pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< crate::AssetConversion, crate::NativeAndAssets, ( - TrustBackedAssetsAsLocation, + TrustBackedAssetsAsLocation, ForeignAssetsConvertedConcreteId, // `ForeignAssetsConvertedConcreteId` excludes the relay token, so we add it back here. MatchedConvertedConcreteId< - xcm::v4::Location, + xcm::v5::Location, Balance, Equals, - WithLatestLocationConverter, + WithLatestLocationConverter, TryConvertInto, >, ), @@ -389,7 +390,7 @@ impl xcm_executor::Config for XcmConfig { TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, Balance, - xcm::v4::Location, + xcm::v5::Location, >, ForeignAssetsConvertedConcreteId, ), @@ -440,7 +441,8 @@ impl xcm_executor::Config for XcmConfig { (bridging::to_westend::UniversalAliases, bridging::to_ethereum::UniversalAliases); type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; - type Aliasers = Nothing; + // We allow any origin to alias into a child sub-location (equivalent to DescendOrigin). + type Aliasers = AliasChildLocation; type TransactionalProcessor = FrameTransactionalProcessor; type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); @@ -515,9 +517,9 @@ impl cumulus_pallet_xcm::Config for Runtime { /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] -impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { - fn create_asset_id_parameter(id: u32) -> xcm::v4::Location { - xcm::v4::Location::new(1, [xcm::v4::Junction::Parachain(id)]) +impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> xcm::v5::Location { + xcm::v5::Location::new(1, [xcm::v5::Junction::Parachain(id)]) } } @@ -581,7 +583,7 @@ pub mod bridging { ] ); - pub const WestendNetwork: NetworkId = NetworkId::Westend; + pub const WestendNetwork: NetworkId = NetworkId::ByGenesis(WESTEND_GENESIS_HASH); pub const EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; pub WestendEcosystem: Location = Location::new(2, [GlobalConsensus(WestendNetwork::get())]); pub EthereumEcosystem: Location = Location::new(2, [GlobalConsensus(EthereumNetwork::get())]); @@ -652,7 +654,7 @@ pub mod bridging { /// `Option` represents static "base fee" which is used for total delivery fee calculation. pub BridgeTable: alloc::vec::Vec = alloc::vec![ NetworkExportTableItem::new( - EthereumNetwork::get(), + EthereumNetwork::get().into(), Some(alloc::vec![Junctions::Here]), SiblingBridgeHub::get(), Some(( @@ -665,7 +667,7 @@ pub mod bridging { /// Universal aliases pub UniversalAliases: BTreeSet<(Location, Junction)> = BTreeSet::from_iter( alloc::vec![ - (SiblingBridgeHubWithEthereumInboundQueueInstance::get(), GlobalConsensus(EthereumNetwork::get())), + (SiblingBridgeHubWithEthereumInboundQueueInstance::get(), GlobalConsensus(EthereumNetwork::get().into())), ] ); } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index 814323135325f4efab62b205545a76dafa577f1f..5da8b45417a3baafdccf3eab73ddb3f5224a2fb2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -1078,6 +1078,7 @@ fn limited_reserve_transfer_assets_for_native_asset_over_bridge_works( mod asset_hub_rococo_tests { use super::*; use asset_hub_rococo_runtime::PolkadotXcm; + use xcm::latest::WESTEND_GENESIS_HASH; use xcm_executor::traits::ConvertLocation; fn bridging_to_asset_hub_westend() -> TestBridgingConfig { @@ -1108,8 +1109,10 @@ mod asset_hub_rococo_tests { let block_author_account = AccountId::from(BLOCK_AUTHOR_ACCOUNT); let staking_pot = StakingPot::get(); - let foreign_asset_id_location = - Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); + let foreign_asset_id_location = Location::new( + 2, + [Junction::GlobalConsensus(NetworkId::ByGenesis(WESTEND_GENESIS_HASH))], + ); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = @@ -1143,7 +1146,7 @@ mod asset_hub_rococo_tests { }, ( [PalletInstance(bp_bridge_hub_rococo::WITH_BRIDGE_ROCOCO_TO_WESTEND_MESSAGES_PALLET_INDEX)].into(), - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), [Parachain(1000)].into() ), || { @@ -1182,8 +1185,10 @@ mod asset_hub_rococo_tests { let block_author_account = AccountId::from(BLOCK_AUTHOR_ACCOUNT); let staking_pot = StakingPot::get(); - let foreign_asset_id_location = - Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); + let foreign_asset_id_location = Location::new( + 2, + [Junction::GlobalConsensus(NetworkId::ByGenesis(WESTEND_GENESIS_HASH))], + ); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = @@ -1210,7 +1215,7 @@ mod asset_hub_rococo_tests { bridging_to_asset_hub_westend, ( [PalletInstance(bp_bridge_hub_rococo::WITH_BRIDGE_ROCOCO_TO_WESTEND_MESSAGES_PALLET_INDEX)].into(), - GlobalConsensus(Westend), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), [Parachain(1000)].into() ), || { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/genesis_config_presets.rs index 758ce3f40609fdc322807d1ac222df737a0bc3b6..824544e3b687979fa97c5644d637f576797a0781 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/genesis_config_presets.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/genesis_config_presets.rs @@ -18,6 +18,7 @@ use crate::*; use alloc::{vec, vec::Vec}; use cumulus_primitives_core::ParaId; +use frame_support::build_struct_json_patch; use hex_literal::hex; use parachains_common::{AccountId, AuraId}; use sp_core::crypto::UncheckedInto; @@ -35,15 +36,14 @@ fn asset_hub_westend_genesis( endowment: Balance, id: ParaId, ) -> serde_json::Value { - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts.iter().cloned().map(|k| (k, endowment)).collect(), }, - parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + parachain_info: ParachainInfoConfig { parachain_id: id }, collator_selection: CollatorSelectionConfig { invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), candidacy_bond: ASSET_HUB_WESTEND_ED * 16, - ..Default::default() }, session: SessionConfig { keys: invulnerables @@ -56,16 +56,9 @@ fn asset_hub_westend_genesis( ) }) .collect(), - ..Default::default() }, - polkadot_xcm: PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, + }) } /// Encapsulates names of predefined presets. @@ -76,8 +69,8 @@ mod preset_names { /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &PresetId) -> Option> { use preset_names::*; - let patch = match id.try_into() { - Ok(PRESET_GENESIS) => asset_hub_westend_genesis( + let patch = match id.as_ref() { + PRESET_GENESIS => asset_hub_westend_genesis( // initial collators. vec![ ( @@ -105,7 +98,7 @@ pub fn get_preset(id: &PresetId) -> Option> { ASSET_HUB_WESTEND_ED * 4096, 1000.into(), ), - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => asset_hub_westend_genesis( + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => asset_hub_westend_genesis( // initial collators. vec![ (Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into()), @@ -115,7 +108,7 @@ pub fn get_preset(id: &PresetId) -> Option> { WND * 1_000_000, 1000.into(), ), - Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => asset_hub_westend_genesis( + sp_genesis_builder::DEV_RUNTIME_PRESET => asset_hub_westend_genesis( // initial collators. vec![(Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into())], vec![ 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 0ed24db97df8ce7912790b194497ee4f0656341d..63175222cc26e1f6d0da063337e9ad6d43f26405 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -45,7 +45,10 @@ use frame_support::{ ord_parameter_types, parameter_types, traits::{ fungible, fungibles, - tokens::{imbalance::ResolveAssetTo, nonfungibles_v2::Inspect}, + tokens::{ + imbalance::ResolveAssetTo, nonfungibles_v2::Inspect, Fortitude::Polite, + Preservation::Expendable, + }, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, InstanceFilter, Nothing, TransformOrigin, }, @@ -67,7 +70,7 @@ use parachains_common::{ use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{AccountIdConversion, BlakeTwo256, Block as BlockT, Saturating, Verify}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Perbill, Permill, RuntimeDebug, @@ -121,10 +124,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // Note: "westmint" is the legacy name for this chain. It has been renamed to // "asset-hub-westend". Many wallets/tools depend on the `spec_name`, so it remains "westmint" // for the time being. Wallets/tools should update to treat "asset-hub-westend" equally. - spec_name: create_runtime_str!("westmint"), - impl_name: create_runtime_str!("westmint"), + spec_name: alloc::borrow::Cow::Borrowed("westmint"), + impl_name: alloc::borrow::Cow::Borrowed("westmint"), authoring_version: 1, - spec_version: 1_016_002, + spec_version: 1_016_004, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, @@ -329,11 +332,11 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, LocalFromLeft< - AssetIdForTrustBackedAssetsConvert, + AssetIdForTrustBackedAssetsConvert, AssetIdForTrustBackedAssets, - xcm::v4::Location, + xcm::v5::Location, >, - xcm::v4::Location, + xcm::v5::Location, AccountId, >; @@ -341,21 +344,21 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< pub type NativeAndAssets = fungible::UnionOf< Balances, LocalAndForeignAssets, - TargetFromLeft, - xcm::v4::Location, + TargetFromLeft, + xcm::v5::Location, AccountId, >; pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< AssetConversionPalletId, - (xcm::v4::Location, xcm::v4::Location), + (xcm::v5::Location, xcm::v5::Location), >; impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; - type AssetKind = xcm::v4::Location; + type AssetKind = xcm::v5::Location; type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset< @@ -380,7 +383,7 @@ impl pallet_asset_conversion::Config for Runtime { WestendLocation, parachain_info::Pallet, xcm_config::TrustBackedAssetsPalletIndex, - xcm::v4::Location, + xcm::v5::Location, >; } @@ -414,18 +417,18 @@ pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; - type AssetId = xcm::v4::Location; - type AssetIdParameter = xcm::v4::Location; + type AssetId = xcm::v5::Location; + type AssetIdParameter = xcm::v5::Location; type Currency = Balances; type CreateOrigin = ForeignCreators< ( - FromSiblingParachain, xcm::v4::Location>, - FromNetwork, + FromSiblingParachain, xcm::v5::Location>, + FromNetwork, xcm_config::bridging::to_rococo::RococoAssetFromAssetHubRococo, ), LocationToAccountId, AccountId, - xcm::v4::Location, + xcm::v5::Location, >; type ForceOrigin = AssetsForceOrigin; type AssetDeposit = ForeignAssetsAssetDeposit; @@ -807,7 +810,7 @@ parameter_types! { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AssetId = xcm::v4::Location; + type AssetId = xcm::v5::Location; type OnChargeAssetTransaction = SwapAssetAdapter< WestendLocation, NativeAndAssets, @@ -968,6 +971,7 @@ impl pallet_revive::Config for Runtime { 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. } impl TryFrom for pallet_revive::Call { @@ -1481,18 +1485,18 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - xcm::v4::Location, + xcm::v5::Location, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: xcm::v5::Location, asset2: xcm::v5::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: xcm::v4::Location, asset2: xcm::v4::Location, amount: Balance, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: xcm::v5::Location, asset2: xcm::v5::Location, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: xcm::v4::Location, asset2: xcm::v4::Location) -> Option<(Balance, Balance)> { + fn get_reserves(asset1: xcm::v5::Location, asset2: xcm::v5::Location) -> Option<(Balance, Balance)> { AssetConversion::get_reserves(asset1, asset2).ok() } } @@ -1526,7 +1530,7 @@ impl_runtime_apis! { // We also accept all assets in a pool with the native token. let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::< Runtime, - xcm::v4::Location + xcm::v5::Location >(&native_token).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?.into_iter(); acceptable_assets.extend(assets_in_pool_with_native); PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) @@ -1544,7 +1548,7 @@ impl_runtime_apis! { // We recognize assets in a pool with the native one. let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::< Runtime, - xcm::v4::Location + xcm::v5::Location >(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?; if assets_in_pool_with_this_asset .into_iter() @@ -1732,7 +1736,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; @@ -1996,7 +2000,7 @@ impl_runtime_apis! { fn fee_asset() -> Result { Ok(Asset { id: AssetId(WestendLocation::get()), - fun: Fungible(1_000_000 * UNITS), + fun: Fungible(1_000 * UNITS), }) } @@ -2010,7 +2014,12 @@ impl_runtime_apis! { } fn alias_origin() -> Result<(Location, Location), BenchmarkError> { - Err(BenchmarkError::Skip) + // Any location can alias to an internal location. + // Here parachain 1001 aliases to an internal account. + Ok(( + Location::new(1, [Parachain(1001)]), + Location::new(1, [Parachain(1001), AccountId32 { id: [111u8; 32], network: None }]), + )) } } @@ -2072,8 +2081,9 @@ impl_runtime_apis! { impl pallet_revive::ReviveApi for Runtime { fn balance(address: H160) -> Balance { + use frame_support::traits::fungible::Inspect; let account = ::AddressMapper::to_account_id(&address); - Balances::usable_balance(account) + Balances::reducible_balance(&account, Expendable, Polite) } fn nonce(address: H160) -> Nonce { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs index d39052c5c03b84f8c02f8257bd9f5a739f4f8624..928f1910cbd22a4d9a9d1cc120a8677cbba90688 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs @@ -21,7 +21,10 @@ use alloc::vec::Vec; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use xcm::{latest::prelude::*, DoubleEncoded}; +use xcm::{ + latest::{prelude::*, AssetTransferFilter}, + DoubleEncoded, +}; trait WeighAssets { fn weigh_assets(&self, weight: Weight) -> Weight; @@ -80,11 +83,7 @@ impl XcmWeightInfo for AssetHubWestendXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::transfer_reserve_asset()) } - fn transact( - _origin_type: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_type: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -132,12 +131,35 @@ impl XcmWeightInfo for AssetHubWestendXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmFungibleWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmFungibleWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -150,6 +172,9 @@ impl XcmWeightInfo for AssetHubWestendXcmWeight { fn clear_error() -> Weight { XcmGeneric::::clear_error() } + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight { XcmGeneric::::claim_asset() } @@ -223,8 +248,7 @@ impl XcmWeightInfo for AssetHubWestendXcmWeight { XcmGeneric::::clear_topic() } fn alias_origin(_: &Location) -> Weight { - // XCM Executor does not currently support alias origin operations - Weight::MAX + XcmGeneric::::alias_origin() } fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { XcmGeneric::::unpaid_execution() diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 0aeae3184627cc2162f779d8972202195fb583aa..97e59c24dd89f9243354e5201ae39ee439408529 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wmcgzesc-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: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 32_651_000 picoseconds. - Weight::from_parts(33_225_000, 3593) + // Minimum execution time: 32_698_000 picoseconds. + Weight::from_parts(33_530_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 41_059_000 picoseconds. - Weight::from_parts(41_730_000, 6196) + // Minimum execution time: 41_485_000 picoseconds. + Weight::from_parts(41_963_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 102_780_000 picoseconds. - Weight::from_parts(105_302_000, 8799) + // Minimum execution time: 104_952_000 picoseconds. + Weight::from_parts(108_211_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -99,8 +99,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_124_000 picoseconds. - Weight::from_parts(1_201_000, 0) + // Minimum execution time: 1_154_000 picoseconds. + Weight::from_parts(1_238_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -122,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 109_024_000 picoseconds. - Weight::from_parts(111_406_000, 6196) + // Minimum execution time: 111_509_000 picoseconds. + Weight::from_parts(114_476_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -131,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_887_000 picoseconds. - Weight::from_parts(3_081_000, 0) + // Minimum execution time: 2_572_000 picoseconds. + Weight::from_parts(2_809_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -140,8 +140,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 25_234_000 picoseconds. - Weight::from_parts(25_561_000, 3593) + // Minimum execution time: 25_570_000 picoseconds. + Weight::from_parts(25_933_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -165,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 83_416_000 picoseconds. - Weight::from_parts(85_683_000, 6196) + // Minimum execution time: 86_148_000 picoseconds. + Weight::from_parts(88_170_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -190,9 +190,34 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 49_271_000 picoseconds. - Weight::from_parts(51_019_000, 3610) + // Minimum execution time: 55_051_000 picoseconds. + Weight::from_parts(56_324_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `6196` + // Minimum execution time: 90_155_000 picoseconds. + Weight::from_parts(91_699_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 98ecd7bd3092bdb89d50f7f4c28118f55b7374a7..7098f175d42175985ff3a38134ebf184b74e9e83 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wmcgzesc-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: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 100_823_000 picoseconds. - Weight::from_parts(103_071_000, 6196) + // Minimum execution time: 103_794_000 picoseconds. + Weight::from_parts(106_697_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 600_000 picoseconds. - Weight::from_parts(686_000, 0) + // Minimum execution time: 621_000 picoseconds. + Weight::from_parts(705_000, 0) + } + pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_580_000 picoseconds. + Weight::from_parts(5_950_000, 0) + } + pub fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 598_000 picoseconds. + Weight::from_parts(700_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +100,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3568` - // Minimum execution time: 8_226_000 picoseconds. - Weight::from_parts(8_650_000, 3568) + // Minimum execution time: 8_186_000 picoseconds. + Weight::from_parts(8_753_000, 3568) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_131_000 picoseconds. - Weight::from_parts(7_600_000, 0) + // Minimum execution time: 6_924_000 picoseconds. + Weight::from_parts(7_315_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_589_000 picoseconds. - Weight::from_parts(2_705_000, 0) + // Minimum execution time: 2_731_000 picoseconds. + Weight::from_parts(2_828_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 667_000 picoseconds. - Weight::from_parts(744_000, 0) + // Minimum execution time: 655_000 picoseconds. + Weight::from_parts(723_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 646_000 picoseconds. - Weight::from_parts(720_000, 0) + // Minimum execution time: 648_000 picoseconds. + Weight::from_parts(730_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 633_000 picoseconds. - Weight::from_parts(669_000, 0) + // Minimum execution time: 628_000 picoseconds. + Weight::from_parts(697_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 671_000 picoseconds. - Weight::from_parts(726_000, 0) + // Minimum execution time: 714_000 picoseconds. + Weight::from_parts(775_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 615_000 picoseconds. - Weight::from_parts(675_000, 0) + // Minimum execution time: 666_000 picoseconds. + Weight::from_parts(717_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +173,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 67_236_000 picoseconds. - Weight::from_parts(69_899_000, 6196) + // Minimum execution time: 70_263_000 picoseconds. + Weight::from_parts(71_266_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +184,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 12_976_000 picoseconds. - Weight::from_parts(13_357_000, 3625) + // Minimum execution time: 13_079_000 picoseconds. + Weight::from_parts(13_569_000, 3625) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +193,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 633_000 picoseconds. - Weight::from_parts(685_000, 0) + // Minimum execution time: 630_000 picoseconds. + Weight::from_parts(710_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +214,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 28_707_000 picoseconds. - Weight::from_parts(31_790_000, 3610) + // Minimum execution time: 29_042_000 picoseconds. + Weight::from_parts(29_633_000, 3610) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +225,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_670_000 picoseconds. - Weight::from_parts(2_833_000, 0) + // Minimum execution time: 2_601_000 picoseconds. + Weight::from_parts(2_855_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 23_459_000 picoseconds. - Weight::from_parts(23_817_000, 0) + // Minimum execution time: 23_696_000 picoseconds. + Weight::from_parts(24_427_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_197_000 picoseconds. - Weight::from_parts(6_338_000, 0) + // Minimum execution time: 6_687_000 picoseconds. + Weight::from_parts(6_820_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 671_000 picoseconds. - Weight::from_parts(715_000, 0) + // Minimum execution time: 653_000 picoseconds. + Weight::from_parts(728_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 655_000 picoseconds. - Weight::from_parts(694_000, 0) + // Minimum execution time: 668_000 picoseconds. + Weight::from_parts(721_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 810_000 picoseconds. - Weight::from_parts(858_000, 0) + // Minimum execution time: 832_000 picoseconds. + Weight::from_parts(900_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +284,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 73_136_000 picoseconds. - Weight::from_parts(75_314_000, 6196) + // Minimum execution time: 75_131_000 picoseconds. + Weight::from_parts(77_142_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +293,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_515_000 picoseconds. - Weight::from_parts(4_768_000, 0) + // Minimum execution time: 4_820_000 picoseconds. + Weight::from_parts(5_089_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +316,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 68_072_000 picoseconds. - Weight::from_parts(69_866_000, 6196) + // Minimum execution time: 70_079_000 picoseconds. + Weight::from_parts(71_762_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,22 +325,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 696_000 picoseconds. - Weight::from_parts(736_000, 0) + // Minimum execution time: 722_000 picoseconds. + Weight::from_parts(784_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 618_000 picoseconds. - Weight::from_parts(681_000, 0) + // Minimum execution time: 613_000 picoseconds. + Weight::from_parts(674_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 647_000 picoseconds. - Weight::from_parts(672_000, 0) + // Minimum execution time: 608_000 picoseconds. + Weight::from_parts(683_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -334,22 +348,29 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1489` - // Minimum execution time: 2_496_000 picoseconds. - Weight::from_parts(2_617_000, 1489) + // Minimum execution time: 2_466_000 picoseconds. + Weight::from_parts(2_705_000, 1489) .saturating_add(T::DbWeight::get().reads(1)) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 637_000 picoseconds. - Weight::from_parts(675_000, 0) + // Minimum execution time: 623_000 picoseconds. + Weight::from_parts(687_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 607_000 picoseconds. - Weight::from_parts(683_000, 0) + // Minimum execution time: 673_000 picoseconds. + Weight::from_parts(752_000, 0) + } + pub fn alias_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 638_000 picoseconds. + Weight::from_parts(708_000, 0) } } 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 b554d00508be18f107b2e5c7c2c10b2dbd9e5b4c..88ccd42dff7f89acfcfe9e84070155f84d0427f1 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 @@ -28,7 +28,7 @@ use frame_support::{ parameter_types, traits::{ tokens::imbalance::{ResolveAssetTo, ResolveTo}, - ConstU32, Contains, Equals, Everything, Nothing, PalletInfoAccess, + ConstU32, Contains, Equals, Everything, PalletInfoAccess, }, }; use frame_system::EnsureRoot; @@ -42,28 +42,29 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; -use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; +use snowbridge_router_primitives::inbound::EthereumLocationsConverterFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto, TryConvertInto}; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}; use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, - AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, - EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, - GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, - SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, - WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, + AccountId32Aliases, AliasChildLocation, AllowExplicitUnpaidExecutionFrom, + AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, + DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, + FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, + IsConcrete, LocalMint, MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, + NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, + SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithLatestLocationConverter, WithUniqueTopic, + XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; parameter_types! { pub const WestendLocation: Location = Location::parent(); - pub const RelayNetwork: Option = Some(NetworkId::Westend); + pub const RelayNetwork: Option = Some(NetworkId::ByGenesis(WESTEND_GENESIS_HASH)); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); @@ -100,7 +101,7 @@ pub type LocationToAccountId = ( GlobalConsensusParachainConvertsFor, // Ethereum contract sovereign account. // (Used to get convert ethereum contract locations to sovereign account) - GlobalConsensusEthereumConvertsFor, + EthereumLocationsConverterFor, ); /// Means for transacting the native currency on this chain. @@ -171,7 +172,7 @@ pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConverte StartsWithExplicitGlobalConsensus, ), Balance, - xcm::v4::Location, + xcm::v5::Location, >; /// Means for transacting foreign assets from different global consensus. @@ -358,14 +359,14 @@ pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< crate::AssetConversion, crate::NativeAndAssets, ( - TrustBackedAssetsAsLocation, + TrustBackedAssetsAsLocation, ForeignAssetsConvertedConcreteId, // `ForeignAssetsConvertedConcreteId` excludes the relay token, so we add it back here. MatchedConvertedConcreteId< - xcm::v4::Location, + xcm::v5::Location, Balance, Equals, - WithLatestLocationConverter, + WithLatestLocationConverter, TryConvertInto, >, ), @@ -411,7 +412,7 @@ impl xcm_executor::Config for XcmConfig { TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, Balance, - xcm::v4::Location, + xcm::v5::Location, >, ForeignAssetsConvertedConcreteId, ), @@ -462,7 +463,8 @@ impl xcm_executor::Config for XcmConfig { (bridging::to_rococo::UniversalAliases, bridging::to_ethereum::UniversalAliases); type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; - type Aliasers = Nothing; + // We allow any origin to alias into a child sub-location (equivalent to DescendOrigin). + type Aliasers = AliasChildLocation; type TransactionalProcessor = FrameTransactionalProcessor; type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); @@ -538,9 +540,9 @@ impl cumulus_pallet_xcm::Config for Runtime { /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] -impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { - fn create_asset_id_parameter(id: u32) -> xcm::v4::Location { - xcm::v4::Location::new(1, [xcm::v4::Junction::Parachain(id)]) +impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> xcm::v5::Location { + xcm::v5::Location::new(1, [xcm::v5::Junction::Parachain(id)]) } } @@ -596,7 +598,7 @@ pub mod bridging { ] ); - pub const RococoNetwork: NetworkId = NetworkId::Rococo; + pub const RococoNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); pub RococoEcosystem: Location = Location::new(2, [GlobalConsensus(RococoNetwork::get())]); pub RocLocation: Location = Location::new(2, [GlobalConsensus(RococoNetwork::get())]); pub AssetHubRococo: Location = Location::new(2, [ @@ -667,7 +669,7 @@ pub mod bridging { /// `Option` represents static "base fee" which is used for total delivery fee calculation. pub BridgeTable: sp_std::vec::Vec = sp_std::vec![ NetworkExportTableItem::new( - EthereumNetwork::get(), + EthereumNetwork::get().into(), Some(sp_std::vec![Junctions::Here]), SiblingBridgeHub::get(), Some(( @@ -680,7 +682,7 @@ pub mod bridging { /// Universal aliases pub UniversalAliases: BTreeSet<(Location, Junction)> = BTreeSet::from_iter( sp_std::vec![ - (SiblingBridgeHubWithEthereumInboundQueueInstance::get(), GlobalConsensus(EthereumNetwork::get())), + (SiblingBridgeHubWithEthereumInboundQueueInstance::get(), GlobalConsensus(EthereumNetwork::get().into())), ] ); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 6a48b19ce9468f8f9de9dab54e7be9938ad4f714..5d0f843554a11572b21a67037f3dcf088039a823 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -52,7 +52,10 @@ use sp_core::crypto::Ss58Codec; use sp_runtime::traits::MaybeEquivalence; use std::{convert::Into, ops::Mul}; use testnet_parachains_constants::westend::{consensus::*, currency::UNITS, fee::WeightToFee}; -use xcm::latest::prelude::{Assets as XcmAssets, *}; +use xcm::latest::{ + prelude::{Assets as XcmAssets, *}, + ROCOCO_GENESIS_HASH, +}; use xcm_builder::WithLatestLocationConverter; use xcm_executor::traits::{ConvertLocation, JustTry, WeightTrader}; use xcm_runtime_apis::conversions::LocationToAccountHelper; @@ -87,7 +90,7 @@ fn slot_durations() -> SlotDurations { fn setup_pool_for_paying_fees_with_foreign_assets( (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( AccountId, - xcm::v4::Location, + xcm::v5::Location, Balance, ), ) { @@ -95,7 +98,7 @@ fn setup_pool_for_paying_fees_with_foreign_assets( // setup a pool to pay fees with `foreign_asset_id_location` tokens let pool_owner: AccountId = [14u8; 32].into(); - let native_asset = xcm::v4::Location::parent(); + let native_asset = xcm::v5::Location::parent(); let pool_liquidity: Balance = existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); @@ -220,10 +223,10 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), Box::new( - xcm::v4::Location::try_from(native_location.clone()).expect("conversion works") + xcm::v5::Location::try_from(native_location.clone()).expect("conversion works") ), Box::new( - xcm::v4::Location::try_from(asset_1_location.clone()) + xcm::v5::Location::try_from(asset_1_location.clone()) .expect("conversion works") ) )); @@ -231,10 +234,10 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), Box::new( - xcm::v4::Location::try_from(native_location.clone()).expect("conversion works") + xcm::v5::Location::try_from(native_location.clone()).expect("conversion works") ), Box::new( - xcm::v4::Location::try_from(asset_1_location.clone()) + xcm::v5::Location::try_from(asset_1_location.clone()) .expect("conversion works") ), pool_liquidity, @@ -272,8 +275,8 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); let (reserve1, reserve2) = AssetConversion::get_reserves( - xcm::v4::Location::try_from(native_location).expect("conversion works"), - xcm::v4::Location::try_from(asset_1_location.clone()).expect("conversion works"), + xcm::v5::Location::try_from(native_location).expect("conversion works"), + xcm::v5::Location::try_from(asset_1_location.clone()).expect("conversion works"), ) .unwrap(); let asset_refund = @@ -311,12 +314,12 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { let bob: AccountId = SOME_ASSET_ADMIN.into(); let staking_pot = CollatorSelection::account_id(); let native_location = - xcm::v4::Location::try_from(WestendLocation::get()).expect("conversion works"); - let foreign_location = xcm::v4::Location { + xcm::v5::Location::try_from(WestendLocation::get()).expect("conversion works"); + let foreign_location = xcm::v5::Location { parents: 1, interior: ( - xcm::v4::Junction::Parachain(1234), - xcm::v4::Junction::GeneralIndex(12345), + xcm::v5::Junction::Parachain(1234), + xcm::v5::Junction::GeneralIndex(12345), ) .into(), }; @@ -498,11 +501,11 @@ fn test_foreign_asset_xcm_take_first_trader() { .execute_with(|| { // We need root origin to create a sufficient asset let minimum_asset_balance = 3333333_u128; - let foreign_location = xcm::v4::Location { + let foreign_location = xcm::v5::Location { parents: 1, interior: ( - xcm::v4::Junction::Parachain(1234), - xcm::v4::Junction::GeneralIndex(12345), + xcm::v5::Junction::Parachain(1234), + xcm::v5::Junction::GeneralIndex(12345), ) .into(), }; @@ -522,7 +525,7 @@ fn test_foreign_asset_xcm_take_first_trader() { minimum_asset_balance )); - let asset_location_v4: Location = foreign_location.clone().try_into().unwrap(); + let asset_location_v5: Location = foreign_location.clone().try_into().unwrap(); // Set Alice as block author, who will receive fees RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); @@ -537,7 +540,7 @@ fn test_foreign_asset_xcm_take_first_trader() { // Lets pay with: asset_amount_needed + asset_amount_extra let asset_amount_extra = 100_u128; let asset: Asset = - (asset_location_v4.clone(), asset_amount_needed + asset_amount_extra).into(); + (asset_location_v5.clone(), asset_amount_needed + asset_amount_extra).into(); let mut trader = ::Trader::new(); let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; @@ -546,7 +549,7 @@ fn test_foreign_asset_xcm_take_first_trader() { let unused_assets = trader.buy_weight(bought, asset.into(), &ctx).expect("Expected Ok"); // Check whether a correct amount of unused assets is returned assert_ok!( - unused_assets.ensure_contains(&(asset_location_v4, asset_amount_extra).into()) + unused_assets.ensure_contains(&(asset_location_v5, asset_amount_extra).into()) ); // Drop trader @@ -834,11 +837,11 @@ fn test_assets_balances_api_works() { .build() .execute_with(|| { let local_asset_id = 1; - let foreign_asset_id_location = xcm::v4::Location { + let foreign_asset_id_location = xcm::v5::Location { parents: 1, interior: [ - xcm::v4::Junction::Parachain(1234), - xcm::v4::Junction::GeneralIndex(12345), + xcm::v5::Junction::Parachain(1234), + xcm::v5::Junction::GeneralIndex(12345), ] .into(), }; @@ -929,7 +932,7 @@ fn test_assets_balances_api_works() { .into()))); // check foreign asset assert!(result.inner().iter().any(|asset| asset.eq(&( - WithLatestLocationConverter::::convert_back( + WithLatestLocationConverter::::convert_back( &foreign_asset_id_location ) .unwrap(), @@ -1022,13 +1025,13 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ Runtime, XcmConfig, ForeignAssetsInstance, - xcm::v4::Location, + xcm::v5::Location, JustTry, collator_session_keys(), ExistentialDeposit::get(), - xcm::v4::Location { + xcm::v5::Location { parents: 1, - interior: [xcm::v4::Junction::Parachain(1313), xcm::v4::Junction::GeneralIndex(12345)] + interior: [xcm::v5::Junction::Parachain(1313), xcm::v5::Junction::GeneralIndex(12345)] .into() }, Box::new(|| { @@ -1045,8 +1048,8 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p WeightToFee, LocationToAccountId, ForeignAssetsInstance, - xcm::v4::Location, - WithLatestLocationConverter, + xcm::v5::Location, + WithLatestLocationConverter, collator_session_keys(), ExistentialDeposit::get(), AssetDeposit::get(), @@ -1123,8 +1126,10 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s let block_author_account = AccountId::from(BLOCK_AUTHOR_ACCOUNT); let staking_pot = StakingPot::get(); - let foreign_asset_id_location = - xcm::v4::Location::new(2, [xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::Rococo)]); + let foreign_asset_id_location = xcm::v5::Location::new( + 2, + [xcm::v5::Junction::GlobalConsensus(xcm::v5::NetworkId::ByGenesis(ROCOCO_GENESIS_HASH))], + ); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); @@ -1154,7 +1159,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s }, ( [PalletInstance(bp_bridge_hub_westend::WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX)].into(), - GlobalConsensus(Rococo), + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), [Parachain(1000)].into() ), || { @@ -1192,8 +1197,10 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic let block_author_account = AccountId::from(BLOCK_AUTHOR_ACCOUNT); let staking_pot = StakingPot::get(); - let foreign_asset_id_location = - xcm::v4::Location::new(2, [xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::Rococo)]); + let foreign_asset_id_location = xcm::v5::Location::new( + 2, + [xcm::v5::Junction::GlobalConsensus(xcm::v5::NetworkId::ByGenesis(ROCOCO_GENESIS_HASH))], + ); let foreign_asset_id_minimum_balance = 1_000_000_000; // sovereign account as foreign asset owner (can be whoever for this scenario) let foreign_asset_owner = LocationToAccountId::convert_location(&Location::parent()).unwrap(); @@ -1216,7 +1223,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic bridging_to_asset_hub_rococo, ( [PalletInstance(bp_bridge_hub_westend::WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX)].into(), - GlobalConsensus(Rococo), + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), [Parachain(1000)].into() ), || { diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs index 26046e5974b5b6c16cc19e8726cf9aa5c89d978b..1d2d45b42c5d02779be9e25f8bae63d4a9195519 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -293,15 +293,15 @@ mod tests { pub UniversalLocationNetworkId: NetworkId = NetworkId::ByGenesis([9; 32]); } - // set up a converter which uses `xcm::v3::Location` under the hood + // set up a converter which uses `xcm::v4::Location` under the hood type Convert = ForeignAssetsConvertedConcreteId< ( StartsWith, StartsWithExplicitGlobalConsensus, ), u128, - xcm::v3::Location, - WithLatestLocationConverter, + xcm::v4::Location, + WithLatestLocationConverter, >; let test_data = vec![ @@ -348,18 +348,18 @@ mod tests { // ok ( ma_1000(1, [Parachain(200)].into()), - Ok((xcm::v3::Location::new(1, [xcm::v3::Junction::Parachain(200)]), 1000)), + Ok((xcm::v4::Location::new(1, [xcm::v4::Junction::Parachain(200)]), 1000)), ), ( ma_1000(2, [Parachain(200)].into()), - Ok((xcm::v3::Location::new(2, [xcm::v3::Junction::Parachain(200)]), 1000)), + Ok((xcm::v4::Location::new(2, [xcm::v4::Junction::Parachain(200)]), 1000)), ), ( ma_1000(1, [Parachain(200), GeneralIndex(1234)].into()), Ok(( - xcm::v3::Location::new( + xcm::v4::Location::new( 1, - [xcm::v3::Junction::Parachain(200), xcm::v3::Junction::GeneralIndex(1234)], + [xcm::v4::Junction::Parachain(200), xcm::v4::Junction::GeneralIndex(1234)], ), 1000, )), @@ -367,9 +367,9 @@ mod tests { ( ma_1000(2, [Parachain(200), GeneralIndex(1234)].into()), Ok(( - xcm::v3::Location::new( + xcm::v4::Location::new( 2, - [xcm::v3::Junction::Parachain(200), xcm::v3::Junction::GeneralIndex(1234)], + [xcm::v4::Junction::Parachain(200), xcm::v4::Junction::GeneralIndex(1234)], ), 1000, )), @@ -377,9 +377,9 @@ mod tests { ( ma_1000(2, [GlobalConsensus(NetworkId::ByGenesis([7; 32]))].into()), Ok(( - xcm::v3::Location::new( + xcm::v4::Location::new( 2, - [xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::ByGenesis( + [xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::ByGenesis( [7; 32], ))], ), @@ -397,14 +397,14 @@ mod tests { .into(), ), Ok(( - xcm::v3::Location::new( + xcm::v4::Location::new( 2, [ - xcm::v3::Junction::GlobalConsensus(xcm::v3::NetworkId::ByGenesis( + xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::ByGenesis( [7; 32], )), - xcm::v3::Junction::Parachain(200), - xcm::v3::Junction::GeneralIndex(1234), + xcm::v4::Junction::Parachain(200), + xcm::v4::Junction::GeneralIndex(1234), ], ), 1000, @@ -414,7 +414,7 @@ mod tests { for (asset, expected_result) in test_data { assert_eq!( - >::matches_fungibles( + >::matches_fungibles( &asset.clone().try_into().unwrap() ), expected_result, diff --git a/cumulus/parachains/runtimes/assets/common/src/matching.rs b/cumulus/parachains/runtimes/assets/common/src/matching.rs index 9ac2056a67f43361ae279e304d3bc1b60d8f04b0..aa9d7929cb9373de2d095e66c7761e599dbfb581 100644 --- a/cumulus/parachains/runtimes/assets/common/src/matching.rs +++ b/cumulus/parachains/runtimes/assets/common/src/matching.rs @@ -146,10 +146,11 @@ impl, OriginLocation: Get> mod tests { use super::*; use frame_support::parameter_types; + use xcm::latest::{ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}; parameter_types! { - pub UniversalLocation: InteriorLocation = [GlobalConsensus(Rococo), Parachain(1000)].into(); - pub ExpectedNetworkId: NetworkId = Wococo; + pub UniversalLocation: InteriorLocation = [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(1000)].into(); + pub ExpectedNetworkId: NetworkId = ByGenesis(WESTEND_GENESIS_HASH); } #[test] @@ -158,26 +159,30 @@ mod tests { let asset: Location = ( Parent, Parent, - GlobalConsensus(Wococo), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000), PalletInstance(1), GeneralIndex(1), ) .into(); - let origin: Location = (Parent, Parent, GlobalConsensus(Wococo), Parachain(1000)).into(); + let origin: Location = + (Parent, Parent, GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000)) + .into(); assert!(FromNetwork::::contains(&asset, &origin)); // asset and origin from local consensus fails let asset: Location = ( Parent, Parent, - GlobalConsensus(Rococo), + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(1000), PalletInstance(1), GeneralIndex(1), ) .into(); - let origin: Location = (Parent, Parent, GlobalConsensus(Rococo), Parachain(1000)).into(); + let origin: Location = + (Parent, Parent, GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(1000)) + .into(); assert!(!FromNetwork::::contains(&asset, &origin)); // asset and origin from here fails @@ -195,14 +200,16 @@ mod tests { GeneralIndex(1), ) .into(); - let origin: Location = (Parent, Parent, GlobalConsensus(Wococo), Parachain(1000)).into(); + let origin: Location = + (Parent, Parent, GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000)) + .into(); assert!(!FromNetwork::::contains(&asset, &origin)); // origin from different consensus fails let asset: Location = ( Parent, Parent, - GlobalConsensus(Wococo), + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000), PalletInstance(1), GeneralIndex(1), diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs index c80222142304b52e31b1e8c0343707ca6fcdbf3a..8dc720e2775300910e820d5ade327b30b5099917 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -367,9 +367,9 @@ pub fn teleports_for_foreign_assets_works< ::Balance: From + Into, SovereignAccountOf: ConvertLocation>, >::AssetId: - From + Into, + From + Into, >::AssetIdParameter: - From + Into, + From + Into, >::Balance: From + Into, ::AccountId: @@ -381,11 +381,11 @@ pub fn teleports_for_foreign_assets_works< { // foreign parachain with the same consensus currency as asset let foreign_para_id = 2222; - let foreign_asset_id_location = xcm::v4::Location { + let foreign_asset_id_location = xcm::v5::Location { parents: 1, interior: [ - xcm::v4::Junction::Parachain(foreign_para_id), - xcm::v4::Junction::GeneralIndex(1234567), + xcm::v5::Junction::Parachain(foreign_para_id), + xcm::v5::Junction::GeneralIndex(1234567), ] .into(), }; @@ -1202,19 +1202,13 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor let xcm = Xcm(vec![ WithdrawAsset(buy_execution_fee.clone().into()), BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: Weight::from_parts(40_000_000_000, 8000), - call: foreign_asset_create.into(), - }, + Transact { origin_kind: OriginKind::Xcm, call: foreign_asset_create.into() }, Transact { origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::from_parts(20_000_000_000, 8000), call: foreign_asset_set_metadata.into(), }, Transact { origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::from_parts(20_000_000_000, 8000), call: foreign_asset_set_team.into(), }, ExpectTransactStatus(MaybeErrorCode::Success), @@ -1321,11 +1315,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor let xcm = Xcm(vec![ WithdrawAsset(buy_execution_fee.clone().into()), BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: Weight::from_parts(20_000_000_000, 8000), - call: foreign_asset_create.into(), - }, + Transact { origin_kind: OriginKind::Xcm, call: foreign_asset_create.into() }, ExpectTransactStatus(MaybeErrorCode::from(DispatchError::BadOrigin.encode())), ]); diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs index d8676117474023cdb9bbb750248eb48cf59f2e32..4f144e24aa300471b1e14e62816032c2e64234f8 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -331,7 +331,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< block_author_account: AccountIdOf, (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( AccountIdOf, - xcm::v4::Location, + xcm::v5::Location, u128, ), foreign_asset_id_amount_to_transfer: u128, @@ -357,9 +357,9 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< BalanceOf: From + Into, XcmConfig: xcm_executor::Config, >::AssetId: - From + Into, + From + Into, >::AssetIdParameter: - From + Into, + From + Into, >::Balance: From + Into + From, ::AccountId: Into<<::RuntimeOrigin as OriginTrait>::AccountId> diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index c226ed9c4fa06ca6e80702e604f2194319452cff..7e0385692375630c54ab585c3c1ff77dc267afc6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -246,12 +246,13 @@ where { use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; use sp_runtime::traits::Zero; - use xcm::VersionedInteriorLocation; + use xcm::{latest::ROCOCO_GENESIS_HASH, VersionedInteriorLocation}; // insert bridge metadata let lane_id = with; let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); - let universal_source = [GlobalConsensus(Rococo), Parachain(sibling_para_id)].into(); + let universal_source = + [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(sibling_para_id)].into(); let universal_destination = [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get()), Parachain(2075)].into(); let bridge_id = BridgeId::new(&universal_source, &universal_destination); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index 29ea4e05f29ee396df1aa351b796113e5b009468..0eab3c74a7e23ddddb9c39e460f6f5a6286ef202 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -43,14 +43,14 @@ use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSyst use polkadot_parachain_primitives::primitives::Sibling; use testnet_parachains_constants::rococo::currency::UNITS as ROC; use xcm::{ - latest::prelude::*, + latest::{prelude::*, WESTEND_GENESIS_HASH}, prelude::{InteriorLocation, NetworkId}, }; use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { pub BridgeRococoToWestendMessagesPalletInstance: InteriorLocation = [PalletInstance(::index() as u8)].into(); - pub WestendGlobalConsensusNetwork: NetworkId = NetworkId::Westend; + pub WestendGlobalConsensusNetwork: NetworkId = NetworkId::ByGenesis(WESTEND_GENESIS_HASH); pub WestendGlobalConsensusNetworkLocation: Location = Location::new( 2, [GlobalConsensus(WestendGlobalConsensusNetwork::get())] @@ -175,13 +175,15 @@ where { use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; use sp_runtime::traits::Zero; - use xcm::VersionedInteriorLocation; + use xcm::{latest::ROCOCO_GENESIS_HASH, VersionedInteriorLocation}; // insert bridge metadata let lane_id = with; let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); - let universal_source = [GlobalConsensus(Rococo), Parachain(sibling_para_id)].into(); - let universal_destination = [GlobalConsensus(Westend), Parachain(2075)].into(); + let universal_source = + [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(sibling_para_id)].into(); + let universal_destination = + [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(2075)].into(); let bridge_id = BridgeId::new(&universal_source, &universal_destination); // insert only bridge metadata, because the benchmarks create lanes diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs index d1b599967bf3690cbe93172beac225d38b14e75a..98e2450ee8327467701488a74e0d99c0cb8db2dd 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs @@ -18,10 +18,12 @@ use crate::*; use alloc::{vec, vec::Vec}; use cumulus_primitives_core::ParaId; +use frame_support::build_struct_json_patch; use parachains_common::{AccountId, AuraId}; use sp_genesis_builder::PresetId; use sp_keyring::Sr25519Keyring; use testnet_parachains_constants::rococo::xcm_version::SAFE_XCM_VERSION; +use xcm::latest::WESTEND_GENESIS_HASH; const BRIDGE_HUB_ROCOCO_ED: Balance = ExistentialDeposit::get(); @@ -33,7 +35,7 @@ fn bridge_hub_rococo_genesis( asset_hub_para_id: ParaId, opened_bridges: Vec<(Location, InteriorLocation, Option)>, ) -> serde_json::Value { - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts .iter() @@ -41,11 +43,10 @@ fn bridge_hub_rococo_genesis( .map(|k| (k, 1u128 << 60)) .collect::>(), }, - parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + parachain_info: ParachainInfoConfig { parachain_id: id }, collator_selection: CollatorSelectionConfig { invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), candidacy_bond: BRIDGE_HUB_ROCOCO_ED * 16, - ..Default::default() }, session: SessionConfig { keys: invulnerables @@ -58,39 +59,21 @@ fn bridge_hub_rococo_genesis( ) }) .collect(), - ..Default::default() - }, - polkadot_xcm: PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - }, - bridge_westend_grandpa: BridgeWestendGrandpaConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() }, + polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, + bridge_westend_grandpa: BridgeWestendGrandpaConfig { owner: bridges_pallet_owner.clone() }, bridge_westend_messages: BridgeWestendMessagesConfig { owner: bridges_pallet_owner.clone(), - ..Default::default() - }, - xcm_over_bridge_hub_westend: XcmOverBridgeHubWestendConfig { - opened_bridges, - ..Default::default() }, - ethereum_system: EthereumSystemConfig { - para_id: id, - asset_hub_para_id, - ..Default::default() - }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + xcm_over_bridge_hub_westend: XcmOverBridgeHubWestendConfig { opened_bridges }, + ethereum_system: EthereumSystemConfig { para_id: id, asset_hub_para_id }, + }) } /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option> { - let patch = match id.try_into() { - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => bridge_hub_rococo_genesis( + let patch = match id.as_ref() { + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => bridge_hub_rococo_genesis( // initial collators. vec![ (Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into()), @@ -102,11 +85,11 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option bridge_hub_rococo_genesis( + sp_genesis_builder::DEV_RUNTIME_PRESET => bridge_hub_rococo_genesis( // initial collators. vec![ (Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into()), diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index f63e1f8fcf65bd95c2ac6dcb5f37bf1aae17aee3..ff7af475f5e2fd347036ea81e7caea9f404191d2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -47,7 +47,7 @@ use pallet_bridge_messages::LaneIdOf; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, @@ -80,6 +80,9 @@ use bridge_hub_common::{ }; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; + +#[cfg(feature = "runtime-benchmarks")] +use xcm::latest::WESTEND_GENESIS_HASH; use xcm::VersionedLocation; use xcm_config::{TreasuryAccount, XcmOriginToTransactDispatchOrigin, XcmRouter}; @@ -235,8 +238,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("bridge-hub-rococo"), - impl_name: create_runtime_str!("bridge-hub-rococo"), + spec_name: alloc::borrow::Cow::Borrowed("bridge-hub-rococo"), + impl_name: alloc::borrow::Cow::Borrowed("bridge-hub-rococo"), authoring_version: 1, spec_version: 1_016_001, impl_version: 0, @@ -1070,7 +1073,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; @@ -1270,7 +1273,7 @@ impl_runtime_apis! { ); // open bridge - let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(8765)].into(); + let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(NetworkId::ByGenesis(WESTEND_GENESIS_HASH)), Parachain(8765)].into(); let locations = XcmOverBridgeHubWestend::bridge_locations( sibling_parachain_location.clone(), bridge_destination_universal_location.clone(), @@ -1292,7 +1295,7 @@ impl_runtime_apis! { Ok( ( sibling_parachain_location, - NetworkId::Westend, + NetworkId::ByGenesis(WESTEND_GENESIS_HASH), [Parachain(8765)].into() ) ) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs index b40cbfeeb8f2770bbc15e77d2a0a657b209a45ef..60a0fc005ca1626e36b8a5b3dc55b0dd8d9ce0ab 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs @@ -22,7 +22,10 @@ use codec::Encode; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use xcm::{latest::prelude::*, DoubleEncoded}; +use xcm::{ + latest::{prelude::*, AssetTransferFilter}, + DoubleEncoded, +}; trait WeighAssets { fn weigh_assets(&self, weight: Weight) -> Weight; @@ -81,11 +84,7 @@ impl XcmWeightInfo for BridgeHubRococoXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::transfer_reserve_asset()) } - fn transact( - _origin_type: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_type: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -133,12 +132,35 @@ impl XcmWeightInfo for BridgeHubRococoXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmFungibleWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmFungibleWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -231,4 +253,7 @@ impl XcmWeightInfo for BridgeHubRococoXcmWeight { fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { XcmGeneric::::unpaid_execution() } + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index f2cee0e3e80751f3f9dde9cbb812623fb3ae2bf8..4a5623fc8b93ed97d7df8522424adc5560cdf45a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 30_988_000 picoseconds. - Weight::from_parts(31_496_000, 3593) + // Minimum execution time: 32_488_000 picoseconds. + Weight::from_parts(33_257_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 42_805_000 picoseconds. - Weight::from_parts(44_207_000, 6196) + // Minimum execution time: 46_250_000 picoseconds. + Weight::from_parts(46_856_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `223` // Estimated: `8799` - // Minimum execution time: 103_376_000 picoseconds. - Weight::from_parts(104_770_000, 8799) + // Minimum execution time: 106_863_000 picoseconds. + Weight::from_parts(109_554_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -124,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 71_234_000 picoseconds. - Weight::from_parts(72_990_000, 6196) + // Minimum execution time: 74_835_000 picoseconds. + Weight::from_parts(75_993_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -133,8 +133,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_636_000 picoseconds. - Weight::from_parts(2_777_000, 0) + // Minimum execution time: 2_709_000 picoseconds. + Weight::from_parts(2_901_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,8 +142,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 23_839_000 picoseconds. - Weight::from_parts(24_568_000, 3593) + // Minimum execution time: 25_194_000 picoseconds. + Weight::from_parts(25_805_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -167,8 +167,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `122` // Estimated: `6196` - // Minimum execution time: 78_345_000 picoseconds. - Weight::from_parts(80_558_000, 6196) + // Minimum execution time: 82_570_000 picoseconds. + Weight::from_parts(84_060_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -192,9 +192,34 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 46_614_000 picoseconds. - Weight::from_parts(47_354_000, 3593) + // Minimum execution time: 51_959_000 picoseconds. + Weight::from_parts(53_434_000, 3593) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `122` + // Estimated: `6196` + // Minimum execution time: 86_918_000 picoseconds. + Weight::from_parts(89_460_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 9a9137c18093b49a1c10ed59ba5e13fd9a2be2e8..b8bd4c4e2d44d0f61a8a1899af2d947a94984ac9 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 70_133_000 picoseconds. - Weight::from_parts(71_765_000, 6196) + // Minimum execution time: 69_010_000 picoseconds. + Weight::from_parts(70_067_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,15 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 959_000 picoseconds. - Weight::from_parts(996_000, 0) + // Minimum execution time: 1_069_000 picoseconds. + Weight::from_parts(1_116_000, 0) + } + pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_011_000 picoseconds. + Weight::from_parts(2_095_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +93,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 7_537_000 picoseconds. - Weight::from_parts(7_876_000, 3497) + // Minimum execution time: 7_630_000 picoseconds. + Weight::from_parts(7_992_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_774_000 picoseconds. - Weight::from_parts(7_895_000, 0) + // Minimum execution time: 7_909_000 picoseconds. + Weight::from_parts(8_100_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_577_000 picoseconds. - Weight::from_parts(1_622_000, 0) + // Minimum execution time: 1_749_000 picoseconds. + Weight::from_parts(1_841_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 973_000 picoseconds. - Weight::from_parts(1_008_000, 0) + // Minimum execution time: 1_109_000 picoseconds. + Weight::from_parts(1_156_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_027_000 picoseconds. - Weight::from_parts(1_052_000, 0) + // Minimum execution time: 1_073_000 picoseconds. + Weight::from_parts(1_143_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 953_000 picoseconds. - Weight::from_parts(992_000, 0) + // Minimum execution time: 1_050_000 picoseconds. + Weight::from_parts(1_084_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 949_000 picoseconds. - Weight::from_parts(1_020_000, 0) + // Minimum execution time: 1_060_000 picoseconds. + Weight::from_parts(1_114_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 979_000 picoseconds. - Weight::from_parts(1_032_000, 0) + // Minimum execution time: 1_065_000 picoseconds. + Weight::from_parts(1_112_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +166,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 66_663_000 picoseconds. - Weight::from_parts(67_728_000, 6196) + // Minimum execution time: 65_538_000 picoseconds. + Weight::from_parts(66_943_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +177,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 11_074_000 picoseconds. - Weight::from_parts(11_439_000, 3555) + // Minimum execution time: 10_898_000 picoseconds. + Weight::from_parts(11_262_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +186,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 943_000 picoseconds. - Weight::from_parts(1_021_000, 0) + // Minimum execution time: 1_026_000 picoseconds. + Weight::from_parts(1_104_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +207,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_123_000 picoseconds. - Weight::from_parts(25_687_000, 3503) + // Minimum execution time: 25_133_000 picoseconds. + Weight::from_parts(25_526_000, 3503) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +218,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_868_000 picoseconds. - Weight::from_parts(3_124_000, 0) + // Minimum execution time: 2_946_000 picoseconds. + Weight::from_parts(3_074_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_378_000 picoseconds. - Weight::from_parts(1_458_000, 0) + // Minimum execution time: 1_428_000 picoseconds. + Weight::from_parts(1_490_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_036_000 picoseconds. - Weight::from_parts(1_105_000, 0) + // Minimum execution time: 1_158_000 picoseconds. + Weight::from_parts(1_222_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 945_000 picoseconds. - Weight::from_parts(1_021_000, 0) + // Minimum execution time: 1_056_000 picoseconds. + Weight::from_parts(1_117_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 931_000 picoseconds. - Weight::from_parts(1_006_000, 0) + // Minimum execution time: 1_045_000 picoseconds. + Weight::from_parts(1_084_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_139_000 picoseconds. - Weight::from_parts(1_206_000, 0) + // Minimum execution time: 1_224_000 picoseconds. + Weight::from_parts(1_268_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +277,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 72_884_000 picoseconds. - Weight::from_parts(74_331_000, 6196) + // Minimum execution time: 70_789_000 picoseconds. + Weight::from_parts(72_321_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +286,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_432_000 picoseconds. - Weight::from_parts(4_542_000, 0) + // Minimum execution time: 4_521_000 picoseconds. + Weight::from_parts(4_649_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +309,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 67_102_000 picoseconds. - Weight::from_parts(68_630_000, 6196) + // Minimum execution time: 66_129_000 picoseconds. + Weight::from_parts(68_089_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,22 +318,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 995_000 picoseconds. - Weight::from_parts(1_057_000, 0) + // Minimum execution time: 1_094_000 picoseconds. + Weight::from_parts(1_157_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 956_000 picoseconds. - Weight::from_parts(1_021_000, 0) + // Minimum execution time: 1_059_000 picoseconds. + Weight::from_parts(1_109_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 944_000 picoseconds. - Weight::from_parts(986_000, 0) + // Minimum execution time: 1_053_000 picoseconds. + Weight::from_parts(1_080_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -343,12 +350,12 @@ impl WeightInfo { /// The range of component `x` is `[1, 1000]`. pub fn export_message(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `589` - // Estimated: `6529` - // Minimum execution time: 58_111_000 picoseconds. - Weight::from_parts(59_123_071, 6529) - // Standard Error: 167 - .saturating_add(Weight::from_parts(43_658, 0).saturating_mul(x.into())) + // Measured: `190` + // Estimated: `6130` + // Minimum execution time: 42_081_000 picoseconds. + Weight::from_parts(42_977_658, 6130) + // Standard Error: 77 + .saturating_add(Weight::from_parts(44_912, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -356,14 +363,21 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 950_000 picoseconds. - Weight::from_parts(1_002_000, 0) + // Minimum execution time: 1_041_000 picoseconds. + Weight::from_parts(1_084_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 963_000 picoseconds. - Weight::from_parts(1_012_000, 0) + // Minimum execution time: 1_085_000 picoseconds. + Weight::from_parts(1_161_000, 0) + } + pub fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 707_000 picoseconds. + Weight::from_parts(749_000, 0) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 2fb186703a88663fb6c3284297c91d7c7038298d..d36075444f7b4c42937f16764e4da15552b1b1a8 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -40,7 +40,7 @@ use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_runtime_common::XcmExportFeeToSibling; use sp_runtime::traits::AccountIdConversion; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, @@ -59,7 +59,7 @@ use xcm_executor::{ parameter_types! { pub const TokenLocation: Location = Location::parent(); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub RelayNetwork: NetworkId = NetworkId::Rococo; + pub RelayNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); pub const MaxInstructions: u32 = 100; @@ -296,7 +296,7 @@ impl, FeeHandler: HandleFee> FeeManager fn is_waived(origin: Option<&Location>, fee_reason: FeeReason) -> bool { let Some(loc) = origin else { return false }; if let Export { network, destination: Here } = fee_reason { - if network == EthereumNetwork::get() { + if network == EthereumNetwork::get().into() { return false } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 01674287fde19b6ea116aad19b136b14ad95a39e..2e7dd98e9dceb22496e166d833a438453780b0b2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -37,7 +37,7 @@ use sp_runtime::{ AccountId32, Perbill, }; use testnet_parachains_constants::rococo::{consensus::*, fee::WeightToFee}; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH}; use xcm_runtime_apis::conversions::LocationToAccountHelper; parameter_types! { @@ -377,7 +377,7 @@ mod bridge_hub_westend_tests { bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, SIBLING_PARACHAIN_ID, - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), || { // we need to create lane between sibling parachain and remote destination bridge_hub_test_utils::ensure_opened_bridge::< @@ -411,7 +411,7 @@ mod bridge_hub_westend_tests { bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, SIBLING_PARACHAIN_ID, - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), || { // we need to create lane between sibling parachain and remote destination bridge_hub_test_utils::ensure_opened_bridge::< @@ -643,7 +643,7 @@ mod bridge_hub_bulletin_tests { slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, SIBLING_PEOPLE_PARACHAIN_ID, - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), || { // we need to create lane between RococoPeople and RococoBulletin bridge_hub_test_utils::ensure_opened_bridge::< @@ -676,7 +676,7 @@ mod bridge_hub_bulletin_tests { slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, SIBLING_PEOPLE_PARACHAIN_ID, - Rococo, + ByGenesis(ROCOCO_GENESIS_HASH), || { // we need to create lane between RococoPeople and RococoBulletin bridge_hub_test_utils::ensure_opened_bridge::< diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index dbca4166a13513b23e09d01798cec73aceb1097f..94921fd8af9a36dac8ba781c10a0f50c503153a2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -229,3 +229,48 @@ pub mod benchmark_helpers { } } } + +pub(crate) mod migrations { + use alloc::vec::Vec; + use frame_support::pallet_prelude::*; + use snowbridge_core::TokenId; + + #[frame_support::storage_alias] + pub type OldNativeToForeignId = StorageMap< + snowbridge_pallet_system::Pallet, + Blake2_128Concat, + xcm::v4::Location, + TokenId, + OptionQuery, + >; + + /// One shot migration for NetworkId::Westend to NetworkId::ByGenesis(WESTEND_GENESIS_HASH) + pub struct MigrationForXcmV5(core::marker::PhantomData); + impl frame_support::traits::OnRuntimeUpgrade + for MigrationForXcmV5 + { + fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + let translate_westend = |pre: xcm::v4::Location| -> Option { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Some(xcm::v5::Location::try_from(pre).expect("valid location")) + }; + snowbridge_pallet_system::ForeignToNativeId::::translate_values(translate_westend); + + let old_keys = OldNativeToForeignId::::iter_keys().collect::>(); + for old_key in old_keys { + if let Some(old_val) = OldNativeToForeignId::::get(&old_key) { + snowbridge_pallet_system::NativeToForeignId::::insert( + &xcm::v5::Location::try_from(old_key.clone()).expect("valid location"), + old_val, + ); + } + OldNativeToForeignId::::remove(old_key); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + } + + weight + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index aca51b320e9beadf61ee8c42c1eca80b09aad7e7..62c93da7c831bb5ee796d43cd5fce1945be0e61e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -44,7 +44,7 @@ use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSyst use polkadot_parachain_primitives::primitives::Sibling; use testnet_parachains_constants::westend::currency::UNITS as WND; use xcm::{ - latest::prelude::*, + latest::{prelude::*, ROCOCO_GENESIS_HASH}, prelude::{InteriorLocation, NetworkId}, }; use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; @@ -57,7 +57,7 @@ parameter_types! { pub const MaxRococoParaHeadDataSize: u32 = bp_rococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; pub BridgeWestendToRococoMessagesPalletInstance: InteriorLocation = [PalletInstance(::index() as u8)].into(); - pub RococoGlobalConsensusNetwork: NetworkId = NetworkId::Rococo; + pub RococoGlobalConsensusNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); pub RococoGlobalConsensusNetworkLocation: Location = Location::new( 2, [GlobalConsensus(RococoGlobalConsensusNetwork::get())] @@ -204,13 +204,15 @@ where { use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; use sp_runtime::traits::Zero; - use xcm::VersionedInteriorLocation; + use xcm::{latest::WESTEND_GENESIS_HASH, VersionedInteriorLocation}; // insert bridge metadata let lane_id = with; let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); - let universal_source = [GlobalConsensus(Westend), Parachain(sibling_para_id)].into(); - let universal_destination = [GlobalConsensus(Rococo), Parachain(2075)].into(); + let universal_source = + [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(sibling_para_id)].into(); + let universal_destination = + [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(2075)].into(); let bridge_id = BridgeId::new(&universal_source, &universal_destination); // insert only bridge metadata, because the benchmarks create lanes @@ -332,7 +334,6 @@ mod tests { pub mod migration { use super::*; use bp_messages::LegacyLaneId; - use frame_support::traits::ConstBool; parameter_types! { pub AssetHubWestendToAssetHubRococoMessagesLane: LegacyLaneId = LegacyLaneId([0, 0, 0, 2]); @@ -340,18 +341,6 @@ pub mod migration { pub AssetHubRococoUniversalLocation: InteriorLocation = [GlobalConsensus(RococoGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)].into(); } - /// Ensure that the existing lanes for the AHW<>AHR bridge are correctly configured. - pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< - Runtime, - XcmOverBridgeHubRococoInstance, - AssetHubWestendToAssetHubRococoMessagesLane, - // the lanes are already created for AHR<>AHW, but we need to link them to the bridge - // structs - ConstBool, - AssetHubWestendLocation, - AssetHubRococoUniversalLocation, - >; - mod v1_wrong { use bp_messages::{LaneState, MessageNonce, UnrewardedRelayer}; use bp_runtime::AccountIdOf; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs index 2949ae01fdcccdc6dfa9dfc229d76e3021c5e08e..69ba9ca9ece79cac8e0635569a62e29363b5798c 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs @@ -18,10 +18,12 @@ use crate::*; use alloc::{vec, vec::Vec}; use cumulus_primitives_core::ParaId; +use frame_support::build_struct_json_patch; use parachains_common::{AccountId, AuraId}; use sp_genesis_builder::PresetId; use sp_keyring::Sr25519Keyring; use testnet_parachains_constants::westend::xcm_version::SAFE_XCM_VERSION; +use xcm::latest::ROCOCO_GENESIS_HASH; const BRIDGE_HUB_WESTEND_ED: Balance = ExistentialDeposit::get(); @@ -33,7 +35,7 @@ fn bridge_hub_westend_genesis( asset_hub_para_id: ParaId, opened_bridges: Vec<(Location, InteriorLocation, Option)>, ) -> serde_json::Value { - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts .iter() @@ -41,11 +43,10 @@ fn bridge_hub_westend_genesis( .map(|k| (k, 1u128 << 60)) .collect::>(), }, - parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + parachain_info: ParachainInfoConfig { parachain_id: id }, collator_selection: CollatorSelectionConfig { invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), candidacy_bond: BRIDGE_HUB_WESTEND_ED * 16, - ..Default::default() }, session: SessionConfig { keys: invulnerables @@ -58,39 +59,19 @@ fn bridge_hub_westend_genesis( ) }) .collect(), - ..Default::default() }, - polkadot_xcm: PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - }, - bridge_rococo_grandpa: BridgeRococoGrandpaConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() - }, - bridge_rococo_messages: BridgeRococoMessagesConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() - }, - xcm_over_bridge_hub_rococo: XcmOverBridgeHubRococoConfig { - opened_bridges, - ..Default::default() - }, - ethereum_system: EthereumSystemConfig { - para_id: id, - asset_hub_para_id, - ..Default::default() - }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, + bridge_rococo_grandpa: BridgeRococoGrandpaConfig { owner: bridges_pallet_owner.clone() }, + bridge_rococo_messages: BridgeRococoMessagesConfig { owner: bridges_pallet_owner.clone() }, + xcm_over_bridge_hub_rococo: XcmOverBridgeHubRococoConfig { opened_bridges }, + ethereum_system: EthereumSystemConfig { para_id: id, asset_hub_para_id }, + }) } /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option> { - let patch = match id.try_into() { - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => bridge_hub_westend_genesis( + let patch = match id.as_ref() { + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => bridge_hub_westend_genesis( // initial collators. vec![ (Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into()), @@ -102,11 +83,14 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option bridge_hub_westend_genesis( + sp_genesis_builder::DEV_RUNTIME_PRESET => bridge_hub_westend_genesis( // initial collators. vec![ (Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into()), diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 1d7cd5de40eb45f3d901b7a4f3c434381a8b950c..0654000167910ffbfc360d0699975bd68fe59006 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -45,7 +45,7 @@ use cumulus_primitives_core::{ClaimQueueOffset, CoreSelector, ParaId}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, @@ -87,6 +87,9 @@ use pallet_bridge_messages::LaneIdOf; pub use sp_runtime::BuildStorage; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; + +#[cfg(feature = "runtime-benchmarks")] +use xcm::latest::ROCOCO_GENESIS_HASH; use xcm::prelude::*; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; @@ -152,20 +155,20 @@ pub type Migrations = ( Runtime, bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, >, - bridge_to_rococo_config::migration::StaticToDynamicLanes, frame_support::migrations::RemoveStorage< BridgeRococoMessagesPalletName, OutboundLanesCongestedSignalsKey, RocksDbWeight, >, pallet_bridge_relayers::migration::v1::MigrationToV1, - // permanent - pallet_xcm::migration::MigrateToLatestXcmVersion, snowbridge_pallet_system::migration::v0::InitializeOnUpgrade< Runtime, ConstU32, ConstU32, >, + bridge_to_ethereum_config::migrations::MigrationForXcmV5, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); parameter_types! { @@ -220,8 +223,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("bridge-hub-westend"), - impl_name: create_runtime_str!("bridge-hub-westend"), + spec_name: alloc::borrow::Cow::Borrowed("bridge-hub-westend"), + impl_name: alloc::borrow::Cow::Borrowed("bridge-hub-westend"), authoring_version: 1, spec_version: 1_016_001, impl_version: 0, @@ -955,7 +958,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; @@ -1155,7 +1158,7 @@ impl_runtime_apis! { ); // open bridge - let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(NetworkId::Rococo), Parachain(8765)].into(); + let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(8765)].into(); let locations = XcmOverBridgeHubRococo::bridge_locations( sibling_parachain_location.clone(), bridge_destination_universal_location.clone(), @@ -1177,7 +1180,7 @@ impl_runtime_apis! { Ok( ( sibling_parachain_location, - NetworkId::Rococo, + NetworkId::ByGenesis(ROCOCO_GENESIS_HASH), [Parachain(8765)].into() ) ) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/mod.rs index 3961cc6d5cdd63a67b5ea4cbc805448e01f98939..473807ea5eb19d9ae86a2c41679932423993ecea 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/mod.rs @@ -23,7 +23,10 @@ use codec::Encode; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use xcm::{latest::prelude::*, DoubleEncoded}; +use xcm::{ + latest::{prelude::*, AssetTransferFilter}, + DoubleEncoded, +}; trait WeighAssets { fn weigh_assets(&self, weight: Weight) -> Weight; @@ -82,11 +85,7 @@ impl XcmWeightInfo for BridgeHubWestendXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::transfer_reserve_asset()) } - fn transact( - _origin_type: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_type: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -134,12 +133,35 @@ impl XcmWeightInfo for BridgeHubWestendXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmFungibleWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmFungibleWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -152,6 +174,9 @@ impl XcmWeightInfo for BridgeHubWestendXcmWeight { fn clear_error() -> Weight { XcmGeneric::::clear_error() } + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight { XcmGeneric::::claim_asset() } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 5bd1d1680aa1ef596940b16f3e5df911647b15f1..555303d30b613a68bedd9a55f59c742f7614ef62 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 30_218_000 picoseconds. - Weight::from_parts(30_783_000, 3593) + // Minimum execution time: 31_340_000 picoseconds. + Weight::from_parts(32_044_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 42_631_000 picoseconds. - Weight::from_parts(43_127_000, 6196) + // Minimum execution time: 44_483_000 picoseconds. + Weight::from_parts(45_215_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `260` // Estimated: `8799` - // Minimum execution time: 100_978_000 picoseconds. - Weight::from_parts(102_819_000, 8799) + // Minimum execution time: 106_531_000 picoseconds. + Weight::from_parts(109_012_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -124,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 71_533_000 picoseconds. - Weight::from_parts(72_922_000, 6196) + // Minimum execution time: 75_043_000 picoseconds. + Weight::from_parts(77_425_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -133,8 +133,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_863_000 picoseconds. - Weight::from_parts(2_997_000, 0) + // Minimum execution time: 2_739_000 picoseconds. + Weight::from_parts(2_855_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,8 +142,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 23_763_000 picoseconds. - Weight::from_parts(24_438_000, 3593) + // Minimum execution time: 25_043_000 picoseconds. + Weight::from_parts(25_297_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -167,8 +167,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `159` // Estimated: `6196` - // Minimum execution time: 78_182_000 picoseconds. - Weight::from_parts(79_575_000, 6196) + // Minimum execution time: 82_421_000 picoseconds. + Weight::from_parts(84_128_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -192,9 +192,34 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `107` // Estimated: `3593` - // Minimum execution time: 46_767_000 picoseconds. - Weight::from_parts(47_823_000, 3593) + // Minimum execution time: 52_465_000 picoseconds. + Weight::from_parts(53_568_000, 3593) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) + // Proof: `ParachainSystem::UpwardDeliveryFeeFactor` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `159` + // Estimated: `6196` + // Minimum execution time: 87_253_000 picoseconds. + Weight::from_parts(88_932_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 16c483a21817080bbeab7461339bbf4c30f3676f..849456af9255e5d45610421ee8565ba7179081bd 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 70_715_000 picoseconds. - Weight::from_parts(72_211_000, 6196) + // Minimum execution time: 70_353_000 picoseconds. + Weight::from_parts(72_257_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,15 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 968_000 picoseconds. - Weight::from_parts(1_022_000, 0) + // Minimum execution time: 996_000 picoseconds. + Weight::from_parts(1_027_000, 0) + } + pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_926_000 picoseconds. + Weight::from_parts(2_033_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +93,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 7_718_000 picoseconds. - Weight::from_parts(7_894_000, 3497) + // Minimum execution time: 7_961_000 picoseconds. + Weight::from_parts(8_256_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_662_000 picoseconds. - Weight::from_parts(7_937_000, 0) + // Minimum execution time: 7_589_000 picoseconds. + Weight::from_parts(7_867_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_699_000 picoseconds. - Weight::from_parts(1_783_000, 0) + // Minimum execution time: 1_602_000 picoseconds. + Weight::from_parts(1_660_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 977_000 picoseconds. - Weight::from_parts(1_045_000, 0) + // Minimum execution time: 1_056_000 picoseconds. + Weight::from_parts(1_096_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 971_000 picoseconds. - Weight::from_parts(1_030_000, 0) + // Minimum execution time: 1_014_000 picoseconds. + Weight::from_parts(1_075_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 958_000 picoseconds. - Weight::from_parts(996_000, 0) + // Minimum execution time: 986_000 picoseconds. + Weight::from_parts(1_031_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 992_000 picoseconds. - Weight::from_parts(1_056_000, 0) + // Minimum execution time: 1_015_000 picoseconds. + Weight::from_parts(1_069_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 975_000 picoseconds. - Weight::from_parts(1_026_000, 0) + // Minimum execution time: 993_000 picoseconds. + Weight::from_parts(1_063_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +166,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 67_236_000 picoseconds. - Weight::from_parts(68_712_000, 6196) + // Minimum execution time: 66_350_000 picoseconds. + Weight::from_parts(68_248_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +177,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 10_890_000 picoseconds. - Weight::from_parts(11_223_000, 3555) + // Minimum execution time: 11_247_000 picoseconds. + Weight::from_parts(11_468_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +186,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 959_000 picoseconds. - Weight::from_parts(1_018_000, 0) + // Minimum execution time: 1_060_000 picoseconds. + Weight::from_parts(1_103_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +207,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_162_000 picoseconds. - Weight::from_parts(25_621_000, 3503) + // Minimum execution time: 25_599_000 picoseconds. + Weight::from_parts(26_336_000, 3503) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +218,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_949_000 picoseconds. - Weight::from_parts(3_119_000, 0) + // Minimum execution time: 2_863_000 picoseconds. + Weight::from_parts(3_090_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_329_000 picoseconds. - Weight::from_parts(1_410_000, 0) + // Minimum execution time: 1_385_000 picoseconds. + Weight::from_parts(1_468_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_063_000 picoseconds. - Weight::from_parts(1_101_000, 0) + // Minimum execution time: 1_087_000 picoseconds. + Weight::from_parts(1_164_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 991_000 picoseconds. - Weight::from_parts(1_041_000, 0) + // Minimum execution time: 1_022_000 picoseconds. + Weight::from_parts(1_066_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 944_000 picoseconds. - Weight::from_parts(998_000, 0) + // Minimum execution time: 1_015_000 picoseconds. + Weight::from_parts(1_070_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_100_000 picoseconds. - Weight::from_parts(1_180_000, 0) + // Minimum execution time: 1_203_000 picoseconds. + Weight::from_parts(1_241_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +277,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 71_203_000 picoseconds. - Weight::from_parts(73_644_000, 6196) + // Minimum execution time: 70_773_000 picoseconds. + Weight::from_parts(72_730_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +286,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_018_000 picoseconds. - Weight::from_parts(4_267_000, 0) + // Minimum execution time: 4_173_000 picoseconds. + Weight::from_parts(4_445_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +309,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 67_893_000 picoseconds. - Weight::from_parts(69_220_000, 6196) + // Minimum execution time: 66_471_000 picoseconds. + Weight::from_parts(68_362_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,22 +318,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 980_000 picoseconds. - Weight::from_parts(1_043_000, 0) + // Minimum execution time: 1_067_000 picoseconds. + Weight::from_parts(1_108_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 944_000 picoseconds. - Weight::from_parts(981_000, 0) + // Minimum execution time: 997_000 picoseconds. + Weight::from_parts(1_043_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 930_000 picoseconds. - Weight::from_parts(962_000, 0) + // Minimum execution time: 1_000_000 picoseconds. + Weight::from_parts(1_056_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -343,12 +350,12 @@ impl WeightInfo { /// The range of component `x` is `[1, 1000]`. pub fn export_message(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `552` - // Estimated: `6492` - // Minimum execution time: 56_762_000 picoseconds. - Weight::from_parts(58_320_046, 6492) - // Standard Error: 162 - .saturating_add(Weight::from_parts(51_730, 0).saturating_mul(x.into())) + // Measured: `225` + // Estimated: `6165` + // Minimum execution time: 43_316_000 picoseconds. + Weight::from_parts(45_220_843, 6165) + // Standard Error: 169 + .saturating_add(Weight::from_parts(44_459, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -356,14 +363,21 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 971_000 picoseconds. - Weight::from_parts(1_018_000, 0) + // Minimum execution time: 998_000 picoseconds. + Weight::from_parts(1_054_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 979_000 picoseconds. - Weight::from_parts(1_026_000, 0) + // Minimum execution time: 995_000 picoseconds. + Weight::from_parts(1_060_000, 0) + } + pub fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 707_000 picoseconds. + Weight::from_parts(749_000, 0) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index ae31ca4cedf2336be6d103c73cc9067989928ae5..e692568932fea6d13b8dd350158f5fe52f303864 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -39,7 +39,7 @@ use snowbridge_runtime_common::XcmExportFeeToSibling; use sp_runtime::traits::AccountIdConversion; use sp_std::marker::PhantomData; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, @@ -57,7 +57,7 @@ use xcm_executor::{ parameter_types! { pub const WestendLocation: Location = Location::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Westend; + pub const RelayNetwork: NetworkId = NetworkId::ByGenesis(WESTEND_GENESIS_HASH); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); @@ -290,7 +290,7 @@ impl, FeeHandler: HandleFee> FeeManager fn is_waived(origin: Option<&Location>, fee_reason: FeeReason) -> bool { let Some(loc) = origin else { return false }; if let Export { network, destination: Here } = fee_reason { - if network == EthereumNetwork::get() { + if network == EthereumNetwork::get().into() { return false } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index e5b67353c0f3f1a39ecd8025447fcee83e589f9f..69301b34fe6b616dcc4a290b8e1f256389a8a956 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -46,7 +46,7 @@ use sp_runtime::{ AccountId32, Perbill, }; use testnet_parachains_constants::westend::{consensus::*, fee::WeightToFee}; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_runtime_apis::conversions::LocationToAccountHelper; // Random para id of sibling chain used in tests. @@ -296,7 +296,7 @@ fn relayed_incoming_message_works() { bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, SIBLING_PARACHAIN_ID, - Westend, + ByGenesis(WESTEND_GENESIS_HASH), || { // we need to create lane between sibling parachain and remote destination bridge_hub_test_utils::ensure_opened_bridge::< @@ -330,7 +330,7 @@ fn free_relay_extrinsic_works() { bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, SIBLING_PARACHAIN_ID, - Westend, + ByGenesis(WESTEND_GENESIS_HASH), || { // we need to create lane between sibling parachain and remote destination bridge_hub_test_utils::ensure_opened_bridge::< diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/message_queue.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/message_queue.rs index 5f91897262f4b8eed44a2da7ad945945dda67cc4..2f5aa76fbdd7717177c32d0529c7ca1de661723a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/src/message_queue.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/message_queue.rs @@ -23,7 +23,7 @@ use frame_support::{ use pallet_message_queue::OnQueueChanged; use scale_info::TypeInfo; use snowbridge_core::ChannelId; -use xcm::v4::{Junction, Location}; +use xcm::latest::prelude::{Junction, Location}; /// The aggregate origin of an inbound message. /// This is specialized for BridgeHub, as the snowbridge-outbound-queue-pallet is also using @@ -53,7 +53,7 @@ impl From for Location { Here => Location::here(), Parent => Location::parent(), Sibling(id) => Location::new(1, Junction::Parachain(id.into())), - // NOTE: We don't need this conversion for Snowbridge. However we have to + // NOTE: We don't need this conversion for Snowbridge. However, we have to // implement it anyway as xcm_builder::ProcessXcmMessage requires it. Snowbridge(_) => Location::default(), } diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs index 24372f57ae7d829bfc4f60552cde13537c489a6e..ad6db0b83e809d9e0fbc884d2574b646bca21662 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs @@ -129,11 +129,8 @@ pub fn initialize_bridge_by_governance_works( }); // execute XCM with Transacts to `initialize bridge` as governance does - assert_ok!(RuntimeHelper::::execute_as_governance( - initialize_call.encode(), - initialize_call.get_dispatch_info().call_weight, - ) - .ensure_complete()); + assert_ok!(RuntimeHelper::::execute_as_governance(initialize_call.encode(),) + .ensure_complete()); // check mode after assert_eq!( @@ -172,7 +169,6 @@ pub fn change_bridge_grandpa_pallet_mode_by_governance_works::execute_as_governance( set_operating_mode_call.encode(), - set_operating_mode_call.get_dispatch_info().call_weight, ) .ensure_complete()); @@ -225,7 +221,6 @@ pub fn change_bridge_parachains_pallet_mode_by_governance_works::execute_as_governance( set_operating_mode_call.encode(), - set_operating_mode_call.get_dispatch_info().call_weight, ) .ensure_complete()); @@ -278,7 +273,6 @@ pub fn change_bridge_messages_pallet_mode_by_governance_works::execute_as_governance( set_operating_mode_call.encode(), - set_operating_mode_call.get_dispatch_info().call_weight, ) .ensure_complete()); diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/genesis_config_presets.rs index aec8e96cedc0b5e3d6fdf354c12557813ac96d00..007ff6164a74123efa4e9b8ca4760405f496e55b 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/genesis_config_presets.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/genesis_config_presets.rs @@ -18,6 +18,7 @@ use crate::*; use alloc::{vec, vec::Vec}; use cumulus_primitives_core::ParaId; +use frame_support::build_struct_json_patch; use parachains_common::{AccountId, AuraId}; use sp_genesis_builder::PresetId; use sp_keyring::Sr25519Keyring; @@ -30,7 +31,7 @@ fn collectives_westend_genesis( endowed_accounts: Vec, id: ParaId, ) -> serde_json::Value { - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts .iter() @@ -38,11 +39,10 @@ fn collectives_westend_genesis( .map(|k| (k, COLLECTIVES_WESTEND_ED * 4096)) .collect::>(), }, - parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + parachain_info: ParachainInfoConfig { parachain_id: id }, collator_selection: CollatorSelectionConfig { invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), candidacy_bond: COLLECTIVES_WESTEND_ED * 16, - ..Default::default() }, session: SessionConfig { keys: invulnerables @@ -55,22 +55,15 @@ fn collectives_westend_genesis( ) }) .collect(), - ..Default::default() }, - polkadot_xcm: PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, + }) } /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option> { - let patch = match id.try_into() { - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => collectives_westend_genesis( + let patch = match id.as_ref() { + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => collectives_westend_genesis( // initial collators. vec![ (Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into()), @@ -79,7 +72,7 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option collectives_westend_genesis( + sp_genesis_builder::DEV_RUNTIME_PRESET => collectives_westend_genesis( // initial collators. vec![(Sr25519Keyring::Alice.to_account_id(), Sr25519Keyring::Alice.public().into())], vec![ diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 8cb2e42cb31bda1bf494762f306eda48760e0dcc..c3e105a84fb63c87514d4fcd56520d7de78dccad 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -56,7 +56,7 @@ use impls::{AllianceProposalProvider, EqualOrGreatestRootCmp}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{AccountIdConversion, BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Perbill, @@ -123,8 +123,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("collectives-westend"), - impl_name: create_runtime_str!("collectives-westend"), + spec_name: alloc::borrow::Cow::Borrowed("collectives-westend"), + impl_name: alloc::borrow::Cow::Borrowed("collectives-westend"), authoring_version: 1, spec_version: 1_016_001, impl_version: 0, @@ -1061,7 +1061,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs index f8e03303c32eb4eef03629467af7bfa4b947cd77..56ef2e8ba02f25e400ac4bc05383978fcfec2af0 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs @@ -33,7 +33,7 @@ use parachains_common::xcm_config::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use westend_runtime_constants::xcm as xcm_constants; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, @@ -50,7 +50,7 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const RootLocation: Location = Location::here(); pub const WndLocation: Location = Location::parent(); - pub const RelayNetwork: Option = Some(NetworkId::Westend); + pub const RelayNetwork: Option = Some(NetworkId::ByGenesis(WESTEND_GENESIS_HASH)); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 2fc3fe4f31415cf6fd060626cf7eea169f4c8014..f661a8bdccfe90c5931e0676efe63765fd7167c1 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -37,7 +37,7 @@ use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSele use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Perbill, @@ -141,8 +141,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("contracts-rococo"), - impl_name: create_runtime_str!("contracts-rococo"), + spec_name: alloc::borrow::Cow::Borrowed("contracts-rococo"), + impl_name: alloc::borrow::Cow::Borrowed("contracts-rococo"), authoring_version: 1, spec_version: 1_016_001, impl_version: 0, @@ -772,7 +772,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 39fdd30a049857e83733c9223f2ae01fb410edb5..0151837aa351e40b1334a23c74b8cc0612eb2f44 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -36,7 +36,7 @@ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::AccountIdConversion; use testnet_parachains_constants::rococo::currency::CENTS; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, @@ -52,7 +52,7 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const RelayLocation: Location = Location::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Rococo; + pub const RelayNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); pub const ExecutiveBody: BodyId = BodyId::Executive; diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs index 3910a747e9bb2d6026ec32406a8ccb4b1a21b3af..d76ac443a147dd27e2fc953b7ac4280296a900a5 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs @@ -134,7 +134,6 @@ impl CoretimeInterface for CoretimeAllocator { }, Instruction::Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(1000000000, 200000), call: request_core_count_call.encode().into(), }, ]); @@ -164,7 +163,6 @@ impl CoretimeInterface for CoretimeAllocator { }, Instruction::Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(1000000000, 200000), call: request_revenue_info_at_call.encode().into(), }, ]); @@ -193,7 +191,6 @@ impl CoretimeInterface for CoretimeAllocator { }, Instruction::Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(1000000000, 200000), call: credit_account_call.encode().into(), }, ]); @@ -258,7 +255,6 @@ impl CoretimeInterface for CoretimeAllocator { }, Instruction::Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(1_000_000_000, 200000), call: assign_core_call.encode().into(), }, ]); diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index f2ccb9c552e3375cce1e578998cd22b6465f6467..a4ff48bfc0a010c605ce07a456848355d9e57170 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -67,7 +67,7 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, @@ -146,8 +146,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("coretime-rococo"), - impl_name: create_runtime_str!("coretime-rococo"), + spec_name: alloc::borrow::Cow::Borrowed("coretime-rococo"), + impl_name: alloc::borrow::Cow::Borrowed("coretime-rococo"), authoring_version: 1, spec_version: 1_016_001, impl_version: 0, @@ -918,7 +918,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs index b8db473f10662d5b1ef045d28f943fe0cb1430b9..48f1366e2c5ff5568178cabe5bc8075b2b94fc1e 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/mod.rs @@ -22,7 +22,10 @@ use alloc::vec::Vec; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use xcm::{latest::prelude::*, DoubleEncoded}; +use xcm::{ + latest::{prelude::*, AssetTransferFilter}, + DoubleEncoded, +}; trait WeighAssets { fn weigh_assets(&self, weight: Weight) -> Weight; @@ -81,11 +84,7 @@ impl XcmWeightInfo for CoretimeRococoXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::transfer_reserve_asset()) } - fn transact( - _origin_type: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_type: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -132,12 +131,35 @@ impl XcmWeightInfo for CoretimeRococoXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmFungibleWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmFungibleWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -229,4 +251,7 @@ impl XcmWeightInfo for CoretimeRococoXcmWeight { fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { XcmGeneric::::unpaid_execution() } + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index c8dbdadf7b15dfcd2ea86326fcff2495a2087a3a..0a2d74de0cb8e42a801daeca2470aed82c417dd9 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 29_812_000 picoseconds. - Weight::from_parts(30_526_000, 3593) + // Minimum execution time: 31_260_000 picoseconds. + Weight::from_parts(31_771_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 39_430_000 picoseconds. - Weight::from_parts(39_968_000, 6196) + // Minimum execution time: 42_231_000 picoseconds. + Weight::from_parts(42_718_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `207` // Estimated: `6196` - // Minimum execution time: 65_555_000 picoseconds. - Weight::from_parts(67_161_000, 6196) + // Minimum execution time: 68_764_000 picoseconds. + Weight::from_parts(70_505_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 30_491_000 picoseconds. - Weight::from_parts(31_991_000, 3571) + // Minimum execution time: 31_390_000 picoseconds. + Weight::from_parts(32_057_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_568_000 picoseconds. - Weight::from_parts(2_703_000, 0) + // Minimum execution time: 2_288_000 picoseconds. + Weight::from_parts(2_477_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 22_159_000 picoseconds. - Weight::from_parts(22_517_000, 3593) + // Minimum execution time: 22_946_000 picoseconds. + Weight::from_parts(23_462_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3593` - // Minimum execution time: 57_126_000 picoseconds. - Weight::from_parts(58_830_000, 3593) + // Minimum execution time: 59_017_000 picoseconds. + Weight::from_parts(60_338_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,9 +180,32 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 26_589_000 picoseconds. - Weight::from_parts(27_285_000, 3571) + // Minimum execution time: 29_953_000 picoseconds. + Weight::from_parts(30_704_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `3593` + // Minimum execution time: 65_118_000 picoseconds. + Weight::from_parts(66_096_000, 3593) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 676048f92ad937e7cb80f268fdc1ca4e90779d42..229dafb7c5ed8757514a79d5a8327c80ace9e351 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -64,8 +64,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 23_760_000 picoseconds. - Weight::from_parts(24_411_000, 3571) + // Minimum execution time: 29_263_000 picoseconds. + Weight::from_parts(30_387_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -73,8 +73,15 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 522_000 picoseconds. - Weight::from_parts(546_000, 0) + // Minimum execution time: 603_000 picoseconds. + Weight::from_parts(664_000, 0) + } + pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_530_000 picoseconds. + Weight::from_parts(1_662_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -82,58 +89,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 5_830_000 picoseconds. - Weight::from_parts(6_069_000, 3497) + // Minimum execution time: 7_290_000 picoseconds. + Weight::from_parts(7_493_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_508_000 picoseconds. - Weight::from_parts(5_801_000, 0) + // Minimum execution time: 6_785_000 picoseconds. + Weight::from_parts(7_012_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_130_000 picoseconds. - Weight::from_parts(1_239_000, 0) + // Minimum execution time: 1_299_000 picoseconds. + Weight::from_parts(1_380_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 541_000 picoseconds. - Weight::from_parts(567_000, 0) + // Minimum execution time: 655_000 picoseconds. + Weight::from_parts(681_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 560_000 picoseconds. - Weight::from_parts(591_000, 0) + // Minimum execution time: 625_000 picoseconds. + Weight::from_parts(669_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 505_000 picoseconds. - Weight::from_parts(547_000, 0) + // Minimum execution time: 607_000 picoseconds. + Weight::from_parts(650_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 538_000 picoseconds. - Weight::from_parts(565_000, 0) + // Minimum execution time: 655_000 picoseconds. + Weight::from_parts(688_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 514_000 picoseconds. - Weight::from_parts(541_000, 0) + // Minimum execution time: 602_000 picoseconds. + Weight::from_parts(650_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -151,8 +158,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 20_920_000 picoseconds. - Weight::from_parts(21_437_000, 3571) + // Minimum execution time: 26_176_000 picoseconds. + Weight::from_parts(26_870_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -162,8 +169,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 8_549_000 picoseconds. - Weight::from_parts(8_821_000, 3555) + // Minimum execution time: 10_674_000 picoseconds. + Weight::from_parts(10_918_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -171,8 +178,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 525_000 picoseconds. - Weight::from_parts(544_000, 0) + // Minimum execution time: 601_000 picoseconds. + Weight::from_parts(639_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -190,8 +197,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 19_645_000 picoseconds. - Weight::from_parts(20_104_000, 3539) + // Minimum execution time: 24_220_000 picoseconds. + Weight::from_parts(24_910_000, 3539) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -201,44 +208,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_232_000 picoseconds. - Weight::from_parts(2_334_000, 0) + // Minimum execution time: 2_464_000 picoseconds. + Weight::from_parts(2_618_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 883_000 picoseconds. - Weight::from_parts(945_000, 0) + // Minimum execution time: 984_000 picoseconds. + Weight::from_parts(1_041_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 600_000 picoseconds. - Weight::from_parts(645_000, 0) + // Minimum execution time: 730_000 picoseconds. + Weight::from_parts(769_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 527_000 picoseconds. - Weight::from_parts(552_000, 0) + // Minimum execution time: 615_000 picoseconds. + Weight::from_parts(658_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 527_000 picoseconds. - Weight::from_parts(550_000, 0) + // Minimum execution time: 607_000 picoseconds. + Weight::from_parts(637_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 657_000 picoseconds. - Weight::from_parts(703_000, 0) + // Minimum execution time: 791_000 picoseconds. + Weight::from_parts(838_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -256,8 +263,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 24_999_000 picoseconds. - Weight::from_parts(25_671_000, 3571) + // Minimum execution time: 30_210_000 picoseconds. + Weight::from_parts(30_973_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -265,8 +272,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_159_000 picoseconds. - Weight::from_parts(3_296_000, 0) + // Minimum execution time: 3_097_000 picoseconds. + Weight::from_parts(3_277_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -284,8 +291,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 21_052_000 picoseconds. - Weight::from_parts(22_153_000, 3571) + // Minimum execution time: 26_487_000 picoseconds. + Weight::from_parts(27_445_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -293,35 +300,42 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 547_000 picoseconds. - Weight::from_parts(584_000, 0) + // Minimum execution time: 655_000 picoseconds. + Weight::from_parts(689_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 506_000 picoseconds. - Weight::from_parts(551_000, 0) + // Minimum execution time: 627_000 picoseconds. + Weight::from_parts(659_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 508_000 picoseconds. - Weight::from_parts(527_000, 0) + // Minimum execution time: 603_000 picoseconds. + Weight::from_parts(650_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 527_000 picoseconds. - Weight::from_parts(558_000, 0) + // Minimum execution time: 594_000 picoseconds. + Weight::from_parts(645_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 514_000 picoseconds. - Weight::from_parts(553_000, 0) + // Minimum execution time: 650_000 picoseconds. + Weight::from_parts(673_000, 0) + } + pub fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 707_000 picoseconds. + Weight::from_parts(749_000, 0) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs index 2eae13de2fd4032871b808e00482b0a326b675ef..37bf1e6814472b30f12c90adeb9b4da226c1799b 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs @@ -37,7 +37,7 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::AccountIdConversion; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, @@ -53,7 +53,7 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const RocRelayLocation: Location = Location::parent(); - pub const RelayNetwork: Option = Some(NetworkId::Rococo); + pub const RelayNetwork: Option = Some(NetworkId::ByGenesis(ROCOCO_GENESIS_HASH)); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs index 86769cb2da112d1c7b37c36306216f09b90f034d..f0c03849750adc0ca81187926e921131c57ced45 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs @@ -127,12 +127,6 @@ impl CoretimeInterface for CoretimeAllocator { use crate::coretime::CoretimeProviderCalls::RequestCoreCount; let request_core_count_call = RelayRuntimePallets::Coretime(RequestCoreCount(count)); - // Weight for `request_core_count` from westend benchmarks: - // `ref_time` = 7889000 + (3 * 25000000) + (1 * 100000000) = 182889000 - // `proof_size` = 1636 - // Add 5% to each component and round to 2 significant figures. - let call_weight = Weight::from_parts(190_000_000, 1700); - let message = Xcm(vec![ Instruction::UnpaidExecution { weight_limit: WeightLimit::Unlimited, @@ -140,7 +134,6 @@ impl CoretimeInterface for CoretimeAllocator { }, Instruction::Transact { origin_kind: OriginKind::Native, - require_weight_at_most: call_weight, call: request_core_count_call.encode().into(), }, ]); @@ -170,7 +163,6 @@ impl CoretimeInterface for CoretimeAllocator { }, Instruction::Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(1000000000, 200000), call: request_revenue_info_at_call.encode().into(), }, ]); @@ -199,7 +191,6 @@ impl CoretimeInterface for CoretimeAllocator { }, Instruction::Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(1000000000, 200000), call: credit_account_call.encode().into(), }, ]); @@ -225,12 +216,6 @@ impl CoretimeInterface for CoretimeAllocator { ) { use crate::coretime::CoretimeProviderCalls::AssignCore; - // Weight for `assign_core` from westend benchmarks: - // `ref_time` = 10177115 + (1 * 25000000) + (2 * 100000000) + (57600 * 13932) = 937660315 - // `proof_size` = 3612 - // Add 5% to each component and round to 2 significant figures. - let call_weight = Weight::from_parts(980_000_000, 3800); - // The relay chain currently only allows `assign_core` to be called with a complete mask // and only ever with increasing `begin`. The assignments must be truncated to avoid // dropping that core's assignment completely. @@ -270,7 +255,6 @@ impl CoretimeInterface for CoretimeAllocator { }, Instruction::Transact { origin_kind: OriginKind::Native, - require_weight_at_most: call_weight, call: assign_core_call.encode().into(), }, ]); diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 2f944e79fe00e21899e66cd71dfcbf6a9aad0d8c..edede5aeb46b2ba688b920a9c12932c05e769322 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -67,7 +67,7 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, @@ -146,8 +146,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("coretime-westend"), - impl_name: create_runtime_str!("coretime-westend"), + spec_name: alloc::borrow::Cow::Borrowed("coretime-westend"), + impl_name: alloc::borrow::Cow::Borrowed("coretime-westend"), authoring_version: 1, spec_version: 1_016_001, impl_version: 0, @@ -910,7 +910,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs index f35f7bfc188dce73525b5742345e7eb9ed459385..1f4b4aa5c5a87c4a4788d9312790b986704d940a 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/mod.rs @@ -21,7 +21,10 @@ use alloc::vec::Vec; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use xcm::{latest::prelude::*, DoubleEncoded}; +use xcm::{ + latest::{prelude::*, AssetTransferFilter}, + DoubleEncoded, +}; trait WeighAssets { fn weigh_assets(&self, weight: Weight) -> Weight; @@ -80,11 +83,7 @@ impl XcmWeightInfo for CoretimeWestendXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::transfer_reserve_asset()) } - fn transact( - _origin_type: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_type: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -132,12 +131,35 @@ impl XcmWeightInfo for CoretimeWestendXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmFungibleWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmFungibleWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -150,6 +172,9 @@ impl XcmWeightInfo for CoretimeWestendXcmWeight { fn clear_error() -> Weight { XcmGeneric::::clear_error() } + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight { XcmGeneric::::claim_asset() } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 935636651eb9dcb5ce0580978d9f1340313ef289..227f3617da00e0b875b48331529b1dea50a632be 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 29_866_000 picoseconds. - Weight::from_parts(30_363_000, 3593) + // Minimum execution time: 30_623_000 picoseconds. + Weight::from_parts(31_009_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 39_434_000 picoseconds. - Weight::from_parts(40_274_000, 6196) + // Minimum execution time: 40_553_000 picoseconds. + Weight::from_parts(41_309_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `207` // Estimated: `6196` - // Minimum execution time: 66_303_000 picoseconds. - Weight::from_parts(68_294_000, 6196) + // Minimum execution time: 66_837_000 picoseconds. + Weight::from_parts(68_463_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 30_523_000 picoseconds. - Weight::from_parts(31_289_000, 3571) + // Minimum execution time: 30_020_000 picoseconds. + Weight::from_parts(31_409_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_517_000 picoseconds. - Weight::from_parts(2_634_000, 0) + // Minimum execution time: 2_355_000 picoseconds. + Weight::from_parts(2_464_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 22_151_000 picoseconds. - Weight::from_parts(22_907_000, 3593) + // Minimum execution time: 22_702_000 picoseconds. + Weight::from_parts(23_422_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3593` - // Minimum execution time: 57_763_000 picoseconds. - Weight::from_parts(58_941_000, 3593) + // Minimum execution time: 58_610_000 picoseconds. + Weight::from_parts(59_659_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,9 +180,32 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 26_322_000 picoseconds. - Weight::from_parts(27_197_000, 3571) + // Minimum execution time: 29_178_000 picoseconds. + Weight::from_parts(29_860_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `106` + // Estimated: `3593` + // Minimum execution time: 63_658_000 picoseconds. + Weight::from_parts(64_869_000, 3593) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 7390f35e3974060abf4efd0e621239fa6a9bead0..bd70bc4f4bd9dcf1fa8c3d9643f5b352b1e9bf21 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -64,8 +64,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 23_688_000 picoseconds. - Weight::from_parts(24_845_000, 3571) + // Minimum execution time: 29_463_000 picoseconds. + Weight::from_parts(30_178_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -73,8 +73,15 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 569_000 picoseconds. - Weight::from_parts(619_000, 0) + // Minimum execution time: 568_000 picoseconds. + Weight::from_parts(608_000, 0) + } + pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_530_000 picoseconds. + Weight::from_parts(1_585_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -82,58 +89,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 5_851_000 picoseconds. - Weight::from_parts(6_061_000, 3497) + // Minimum execution time: 7_400_000 picoseconds. + Weight::from_parts(7_572_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_770_000 picoseconds. - Weight::from_parts(5_916_000, 0) + // Minimum execution time: 6_951_000 picoseconds. + Weight::from_parts(7_173_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_155_000 picoseconds. - Weight::from_parts(1_270_000, 0) + // Minimum execution time: 1_245_000 picoseconds. + Weight::from_parts(1_342_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 558_000 picoseconds. - Weight::from_parts(628_000, 0) + // Minimum execution time: 613_000 picoseconds. + Weight::from_parts(657_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 603_000 picoseconds. - Weight::from_parts(630_000, 0) + // Minimum execution time: 613_000 picoseconds. + Weight::from_parts(656_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 533_000 picoseconds. - Weight::from_parts(563_000, 0) + // Minimum execution time: 570_000 picoseconds. + Weight::from_parts(608_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 597_000 picoseconds. - Weight::from_parts(644_000, 0) + // Minimum execution time: 557_000 picoseconds. + Weight::from_parts(607_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 536_000 picoseconds. - Weight::from_parts(588_000, 0) + // Minimum execution time: 557_000 picoseconds. + Weight::from_parts(578_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -151,8 +158,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 21_146_000 picoseconds. - Weight::from_parts(21_771_000, 3571) + // Minimum execution time: 26_179_000 picoseconds. + Weight::from_parts(27_089_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -162,8 +169,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 8_446_000 picoseconds. - Weight::from_parts(8_660_000, 3555) + // Minimum execution time: 10_724_000 picoseconds. + Weight::from_parts(10_896_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -171,8 +178,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 561_000 picoseconds. - Weight::from_parts(594_000, 0) + // Minimum execution time: 567_000 picoseconds. + Weight::from_parts(623_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -190,8 +197,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `74` // Estimated: `3539` - // Minimum execution time: 19_953_000 picoseconds. - Weight::from_parts(20_608_000, 3539) + // Minimum execution time: 24_367_000 picoseconds. + Weight::from_parts(25_072_000, 3539) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -201,44 +208,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_290_000 picoseconds. - Weight::from_parts(2_370_000, 0) + // Minimum execution time: 2_554_000 picoseconds. + Weight::from_parts(2_757_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 943_000 picoseconds. - Weight::from_parts(987_000, 0) + // Minimum execution time: 922_000 picoseconds. + Weight::from_parts(992_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 635_000 picoseconds. - Weight::from_parts(699_000, 0) + // Minimum execution time: 688_000 picoseconds. + Weight::from_parts(723_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 553_000 picoseconds. - Weight::from_parts(609_000, 0) + // Minimum execution time: 607_000 picoseconds. + Weight::from_parts(647_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 547_000 picoseconds. - Weight::from_parts(581_000, 0) + // Minimum execution time: 591_000 picoseconds. + Weight::from_parts(620_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 700_000 picoseconds. - Weight::from_parts(757_000, 0) + // Minimum execution time: 735_000 picoseconds. + Weight::from_parts(802_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -256,8 +263,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 24_953_000 picoseconds. - Weight::from_parts(25_516_000, 3571) + // Minimum execution time: 29_923_000 picoseconds. + Weight::from_parts(30_770_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -265,8 +272,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_746_000 picoseconds. - Weight::from_parts(2_944_000, 0) + // Minimum execution time: 2_884_000 picoseconds. + Weight::from_parts(3_088_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -284,8 +291,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 21_325_000 picoseconds. - Weight::from_parts(21_942_000, 3571) + // Minimum execution time: 26_632_000 picoseconds. + Weight::from_parts(27_228_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -293,35 +300,42 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 600_000 picoseconds. - Weight::from_parts(631_000, 0) + // Minimum execution time: 599_000 picoseconds. + Weight::from_parts(655_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 534_000 picoseconds. - Weight::from_parts(566_000, 0) + // Minimum execution time: 587_000 picoseconds. + Weight::from_parts(628_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 540_000 picoseconds. - Weight::from_parts(565_000, 0) + // Minimum execution time: 572_000 picoseconds. + Weight::from_parts(631_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 542_000 picoseconds. - Weight::from_parts(581_000, 0) + // Minimum execution time: 570_000 picoseconds. + Weight::from_parts(615_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 568_000 picoseconds. - Weight::from_parts(597_000, 0) + // Minimum execution time: 624_000 picoseconds. + Weight::from_parts(659_000, 0) + } + pub fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 707_000 picoseconds. + Weight::from_parts(749_000, 0) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs index 1205be95c932964b4ebf05171d0544bae3d2694a..5616c585a13c4f9cb334a560ca07d4db67c8de83 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs @@ -37,7 +37,7 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::AccountIdConversion; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, @@ -53,7 +53,7 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const TokenRelayLocation: Location = Location::parent(); - pub const RelayNetwork: Option = Some(NetworkId::Westend); + pub const RelayNetwork: Option = Some(NetworkId::ByGenesis(WESTEND_GENESIS_HASH)); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index ad656cdbb83a89dd74f50092dd0208c2a0c1348f..fdf467ab64b8a11cf8672afe8ad47146e66a6b64 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -55,7 +55,7 @@ use sp_api::impl_runtime_apis; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, @@ -99,8 +99,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("glutton-westend"), - impl_name: create_runtime_str!("glutton-westend"), + spec_name: alloc::borrow::Cow::Borrowed("glutton-westend"), + impl_name: alloc::borrow::Cow::Borrowed("glutton-westend"), authoring_version: 1, spec_version: 1_016_001, impl_version: 0, @@ -460,7 +460,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs index d1fb50c1ab095cb07eb3f849c5e5d8be42fd1fc6..b67c32495d67c140d0ba23fdc8aec1de622b428e 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs @@ -22,7 +22,7 @@ use frame_support::{ traits::{Contains, Everything, Nothing}, weights::Weight, }; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ AllowExplicitUnpaidExecutionFrom, FixedWeightBounds, FrameTransactionalProcessor, ParentAsSuperuser, ParentIsPreset, SovereignSignedViaLocation, @@ -30,7 +30,7 @@ use xcm_builder::{ parameter_types! { pub const WestendLocation: Location = Location::parent(); - pub const WestendNetwork: NetworkId = NetworkId::Westend; + pub const WestendNetwork: NetworkId = NetworkId::ByGenesis(WESTEND_GENESIS_HASH); pub UniversalLocation: InteriorLocation = [GlobalConsensus(WestendNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index bb290c0af97dc1a43d123d1a50a5fdb2a0c2edc0..25356a84806d0582472f51114eee03260f1df8a4 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -58,7 +58,7 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, @@ -134,8 +134,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("people-rococo"), - impl_name: create_runtime_str!("people-rococo"), + spec_name: alloc::borrow::Cow::Borrowed("people-rococo"), + impl_name: alloc::borrow::Cow::Borrowed("people-rococo"), authoring_version: 1, spec_version: 1_016_001, impl_version: 0, @@ -886,7 +886,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs index 58007173ae1d505ad5f831e8f4962d2c4e491615..b82872a1cbf2fa03a31d1323030c862099e05499 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs @@ -21,7 +21,10 @@ use alloc::vec::Vec; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use xcm::{latest::prelude::*, DoubleEncoded}; +use xcm::{ + latest::{prelude::*, AssetTransferFilter}, + DoubleEncoded, +}; trait WeighAssets { fn weigh_assets(&self, weight: Weight) -> Weight; @@ -80,11 +83,7 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::transfer_reserve_asset()) } - fn transact( - _origin_type: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_type: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -131,12 +130,35 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmFungibleWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmFungibleWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -228,4 +250,7 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { XcmGeneric::::unpaid_execution() } + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 4dd44e66dd5e3cf0e545fac692cf27257cb075f4..f594c45e1cf63c772e7d1ac93a2d80fc675defce 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("people-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 30_428_000 picoseconds. - Weight::from_parts(31_184_000, 3593) + // Minimum execution time: 30_760_000 picoseconds. + Weight::from_parts(31_209_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 41_912_000 picoseconds. - Weight::from_parts(43_346_000, 6196) + // Minimum execution time: 43_379_000 picoseconds. + Weight::from_parts(44_202_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `223` // Estimated: `6196` - // Minimum execution time: 67_706_000 picoseconds. - Weight::from_parts(69_671_000, 6196) + // Minimum execution time: 67_467_000 picoseconds. + Weight::from_parts(69_235_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 29_790_000 picoseconds. - Weight::from_parts(30_655_000, 3535) + // Minimum execution time: 29_243_000 picoseconds. + Weight::from_parts(30_176_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_438_000 picoseconds. - Weight::from_parts(2_597_000, 0) + // Minimum execution time: 2_294_000 picoseconds. + Weight::from_parts(2_424_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 24_040_000 picoseconds. - Weight::from_parts(24_538_000, 3593) + // Minimum execution time: 24_058_000 picoseconds. + Weight::from_parts(24_588_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `122` // Estimated: `3593` - // Minimum execution time: 58_275_000 picoseconds. - Weight::from_parts(59_899_000, 3593) + // Minimum execution time: 59_164_000 picoseconds. + Weight::from_parts(60_431_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,9 +180,32 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 25_638_000 picoseconds. - Weight::from_parts(26_514_000, 3535) + // Minimum execution time: 28_379_000 picoseconds. + Weight::from_parts(29_153_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `122` + // Estimated: `3593` + // Minimum execution time: 64_505_000 picoseconds. + Weight::from_parts(66_587_000, 3593) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 729a3211704198bf5c59e1e2ac47e03f88fb0340..30e28fac7e577a22ec75e2a5f72bfa405f11cfeb 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("people-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -64,8 +64,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 29_430_000 picoseconds. - Weight::from_parts(30_111_000, 3535) + // Minimum execution time: 28_898_000 picoseconds. + Weight::from_parts(29_717_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -73,8 +73,15 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 607_000 picoseconds. - Weight::from_parts(672_000, 0) + // Minimum execution time: 690_000 picoseconds. + Weight::from_parts(759_000, 0) + } + pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_695_000 picoseconds. + Weight::from_parts(1_799_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -82,58 +89,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 7_445_000 picoseconds. - Weight::from_parts(7_623_000, 3497) + // Minimum execution time: 7_441_000 picoseconds. + Weight::from_parts(7_746_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_749_000 picoseconds. - Weight::from_parts(7_073_000, 0) + // Minimum execution time: 6_881_000 picoseconds. + Weight::from_parts(7_219_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_275_000 picoseconds. - Weight::from_parts(1_409_000, 0) + // Minimum execution time: 1_390_000 picoseconds. + Weight::from_parts(1_471_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 670_000 picoseconds. - Weight::from_parts(709_000, 0) + // Minimum execution time: 698_000 picoseconds. + Weight::from_parts(743_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 635_000 picoseconds. - Weight::from_parts(723_000, 0) + // Minimum execution time: 695_000 picoseconds. + Weight::from_parts(746_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 650_000 picoseconds. + // Minimum execution time: 664_000 picoseconds. Weight::from_parts(699_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 678_000 picoseconds. - Weight::from_parts(728_000, 0) + // Minimum execution time: 698_000 picoseconds. + Weight::from_parts(748_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 657_000 picoseconds. - Weight::from_parts(703_000, 0) + // Minimum execution time: 669_000 picoseconds. + Weight::from_parts(726_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -151,8 +158,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 25_795_000 picoseconds. - Weight::from_parts(26_415_000, 3535) + // Minimum execution time: 25_991_000 picoseconds. + Weight::from_parts(26_602_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -162,8 +169,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 10_792_000 picoseconds. - Weight::from_parts(11_061_000, 3555) + // Minimum execution time: 10_561_000 picoseconds. + Weight::from_parts(10_913_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -171,8 +178,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 624_000 picoseconds. - Weight::from_parts(682_000, 0) + // Minimum execution time: 654_000 picoseconds. + Weight::from_parts(707_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -190,8 +197,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 23_906_000 picoseconds. - Weight::from_parts(24_740_000, 3503) + // Minimum execution time: 23_813_000 picoseconds. + Weight::from_parts(24_352_000, 3503) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -201,44 +208,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_621_000 picoseconds. - Weight::from_parts(2_788_000, 0) + // Minimum execution time: 2_499_000 picoseconds. + Weight::from_parts(2_655_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 954_000 picoseconds. - Weight::from_parts(1_046_000, 0) + // Minimum execution time: 1_065_000 picoseconds. + Weight::from_parts(1_108_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 742_000 picoseconds. - Weight::from_parts(790_000, 0) + // Minimum execution time: 747_000 picoseconds. + Weight::from_parts(807_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 664_000 picoseconds. - Weight::from_parts(722_000, 0) + // Minimum execution time: 685_000 picoseconds. + Weight::from_parts(750_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 619_000 picoseconds. - Weight::from_parts(672_000, 0) + // Minimum execution time: 664_000 picoseconds. + Weight::from_parts(711_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 798_000 picoseconds. - Weight::from_parts(851_000, 0) + // Minimum execution time: 830_000 picoseconds. + Weight::from_parts(880_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -256,8 +263,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 29_580_000 picoseconds. - Weight::from_parts(31_100_000, 3535) + // Minimum execution time: 30_051_000 picoseconds. + Weight::from_parts(30_720_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -265,8 +272,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_150_000 picoseconds. - Weight::from_parts(3_326_000, 0) + // Minimum execution time: 3_136_000 picoseconds. + Weight::from_parts(3_265_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -284,8 +291,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 26_152_000 picoseconds. - Weight::from_parts(26_635_000, 3535) + // Minimum execution time: 25_980_000 picoseconds. + Weight::from_parts(26_868_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -293,35 +300,42 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 693_000 picoseconds. - Weight::from_parts(724_000, 0) + // Minimum execution time: 708_000 picoseconds. + Weight::from_parts(755_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 632_000 picoseconds. - Weight::from_parts(678_000, 0) + // Minimum execution time: 667_000 picoseconds. + Weight::from_parts(702_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 646_000 picoseconds. - Weight::from_parts(694_000, 0) + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(695_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 622_000 picoseconds. - Weight::from_parts(656_000, 0) + // Minimum execution time: 669_000 picoseconds. + Weight::from_parts(707_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 639_000 picoseconds. - Weight::from_parts(679_000, 0) + // Minimum execution time: 685_000 picoseconds. + Weight::from_parts(757_000, 0) + } + pub fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 707_000 picoseconds. + Weight::from_parts(749_000, 0) } } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs index a2e20e2778b621326ff39624aef261a863665d77..724d87587c6c51ac5d7b564862d0694f0dd7141b 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs @@ -34,7 +34,7 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use sp_runtime::traits::AccountIdConversion; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, @@ -51,7 +51,7 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const RootLocation: Location = Location::here(); pub const RelayLocation: Location = Location::parent(); - pub const RelayNetwork: Option = Some(NetworkId::Rococo); + pub const RelayNetwork: Option = Some(NetworkId::ByGenesis(ROCOCO_GENESIS_HASH)); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 7a586504badba13603817156105f5980b7012d9e..1c5183636c49964850adcd1748cca3ed10bbaf17 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -58,7 +58,7 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, @@ -133,8 +133,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("people-westend"), - impl_name: create_runtime_str!("people-westend"), + spec_name: alloc::borrow::Cow::Borrowed("people-westend"), + impl_name: alloc::borrow::Cow::Borrowed("people-westend"), authoring_version: 1, spec_version: 1_016_001, impl_version: 0, @@ -884,7 +884,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs index b44e8d4b61b8fe57ea4191b891da6dcbaa962813..8ca9771dca468d89d9d2a38c67b6428fad44ee64 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs @@ -21,7 +21,10 @@ use alloc::vec::Vec; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use xcm::{latest::prelude::*, DoubleEncoded}; +use xcm::{ + latest::{prelude::*, AssetTransferFilter}, + DoubleEncoded, +}; trait WeighAssets { fn weigh_assets(&self, weight: Weight) -> Weight; @@ -80,11 +83,7 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::transfer_reserve_asset()) } - fn transact( - _origin_type: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_type: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -131,12 +130,35 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmFungibleWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmFungibleWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -228,4 +250,7 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { XcmGeneric::::unpaid_execution() } + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 8f6bfde986bb36c4fd3c90bdfb767727133f8c35..c12da204f35b485e97260c0f9356ed0f648d4655 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("people-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 30_040_000 picoseconds. - Weight::from_parts(30_758_000, 3593) + // Minimum execution time: 30_401_000 picoseconds. + Weight::from_parts(30_813_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 42_135_000 picoseconds. - Weight::from_parts(42_970_000, 6196) + // Minimum execution time: 43_150_000 picoseconds. + Weight::from_parts(43_919_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `223` // Estimated: `6196` - // Minimum execution time: 67_385_000 picoseconds. - Weight::from_parts(69_776_000, 6196) + // Minimum execution time: 67_808_000 picoseconds. + Weight::from_parts(69_114_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 29_804_000 picoseconds. - Weight::from_parts(30_662_000, 3535) + // Minimum execution time: 29_312_000 picoseconds. + Weight::from_parts(30_347_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_358_000 picoseconds. - Weight::from_parts(2_497_000, 0) + // Minimum execution time: 2_283_000 picoseconds. + Weight::from_parts(2_448_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 23_732_000 picoseconds. - Weight::from_parts(24_098_000, 3593) + // Minimum execution time: 23_556_000 picoseconds. + Weight::from_parts(24_419_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `122` // Estimated: `3593` - // Minimum execution time: 58_449_000 picoseconds. - Weight::from_parts(60_235_000, 3593) + // Minimum execution time: 58_342_000 picoseconds. + Weight::from_parts(59_598_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,9 +180,32 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 25_708_000 picoseconds. - Weight::from_parts(26_495_000, 3535) + // Minimum execution time: 28_285_000 picoseconds. + Weight::from_parts(29_016_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `122` + // Estimated: `3593` + // Minimum execution time: 65_211_000 picoseconds. + Weight::from_parts(67_200_000, 3593) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 1377d31f2db7df67b58a699253af03498f7f6741..3c539902abc8a560e316c5275d290d9cc03e57c9 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("people-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -64,8 +64,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 29_537_000 picoseconds. - Weight::from_parts(30_513_000, 3535) + // Minimum execution time: 29_015_000 picoseconds. + Weight::from_parts(30_359_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -73,8 +73,15 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 683_000 picoseconds. - Weight::from_parts(738_000, 0) + // Minimum execution time: 572_000 picoseconds. + Weight::from_parts(637_000, 0) + } + pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_550_000 picoseconds. + Weight::from_parts(1_604_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -82,58 +89,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 7_498_000 picoseconds. - Weight::from_parts(7_904_000, 3497) + // Minimum execution time: 7_354_000 picoseconds. + Weight::from_parts(7_808_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_029_000 picoseconds. - Weight::from_parts(7_325_000, 0) + // Minimum execution time: 6_716_000 picoseconds. + Weight::from_parts(7_067_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_343_000 picoseconds. - Weight::from_parts(1_410_000, 0) + // Minimum execution time: 1_280_000 picoseconds. + Weight::from_parts(1_355_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 696_000 picoseconds. - Weight::from_parts(734_000, 0) + // Minimum execution time: 587_000 picoseconds. + Weight::from_parts(645_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 690_000 picoseconds. - Weight::from_parts(740_000, 0) + // Minimum execution time: 629_000 picoseconds. + Weight::from_parts(662_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 667_000 picoseconds. - Weight::from_parts(697_000, 0) + // Minimum execution time: 590_000 picoseconds. + Weight::from_parts(639_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 692_000 picoseconds. - Weight::from_parts(743_000, 0) + // Minimum execution time: 651_000 picoseconds. + Weight::from_parts(688_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 670_000 picoseconds. - Weight::from_parts(712_000, 0) + // Minimum execution time: 601_000 picoseconds. + Weight::from_parts(630_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -151,8 +158,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 26_405_000 picoseconds. - Weight::from_parts(26_877_000, 3535) + // Minimum execution time: 25_650_000 picoseconds. + Weight::from_parts(26_440_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -162,8 +169,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 10_953_000 picoseconds. - Weight::from_parts(11_345_000, 3555) + // Minimum execution time: 10_492_000 picoseconds. + Weight::from_parts(10_875_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -171,8 +178,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 644_000 picoseconds. - Weight::from_parts(693_000, 0) + // Minimum execution time: 597_000 picoseconds. + Weight::from_parts(647_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -190,8 +197,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 24_157_000 picoseconds. - Weight::from_parts(24_980_000, 3503) + // Minimum execution time: 23_732_000 picoseconds. + Weight::from_parts(24_290_000, 3503) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -201,44 +208,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_767_000 picoseconds. - Weight::from_parts(2_844_000, 0) + // Minimum execution time: 2_446_000 picoseconds. + Weight::from_parts(2_613_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_079_000 picoseconds. - Weight::from_parts(1_141_000, 0) + // Minimum execution time: 960_000 picoseconds. + Weight::from_parts(1_045_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 776_000 picoseconds. - Weight::from_parts(829_000, 0) + // Minimum execution time: 703_000 picoseconds. + Weight::from_parts(739_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 696_000 picoseconds. - Weight::from_parts(740_000, 0) + // Minimum execution time: 616_000 picoseconds. + Weight::from_parts(651_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 655_000 picoseconds. - Weight::from_parts(684_000, 0) + // Minimum execution time: 621_000 picoseconds. + Weight::from_parts(660_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 825_000 picoseconds. - Weight::from_parts(853_000, 0) + // Minimum execution time: 794_000 picoseconds. + Weight::from_parts(831_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -256,8 +263,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 30_222_000 picoseconds. - Weight::from_parts(31_110_000, 3535) + // Minimum execution time: 29_527_000 picoseconds. + Weight::from_parts(30_614_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -265,8 +272,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_108_000 picoseconds. - Weight::from_parts(3_325_000, 0) + // Minimum execution time: 3_189_000 picoseconds. + Weight::from_parts(3_296_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -284,8 +291,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 26_548_000 picoseconds. - Weight::from_parts(26_911_000, 3535) + // Minimum execution time: 25_965_000 picoseconds. + Weight::from_parts(26_468_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -293,35 +300,42 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 684_000 picoseconds. - Weight::from_parts(726_000, 0) + // Minimum execution time: 618_000 picoseconds. + Weight::from_parts(659_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 649_000 picoseconds. - Weight::from_parts(700_000, 0) + // Minimum execution time: 593_000 picoseconds. + Weight::from_parts(618_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 650_000 picoseconds. - Weight::from_parts(686_000, 0) + // Minimum execution time: 603_000 picoseconds. + Weight::from_parts(634_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 652_000 picoseconds. - Weight::from_parts(703_000, 0) + // Minimum execution time: 568_000 picoseconds. + Weight::from_parts(629_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 673_000 picoseconds. - Weight::from_parts(742_000, 0) + // Minimum execution time: 598_000 picoseconds. + Weight::from_parts(655_000, 0) + } + pub fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 707_000 picoseconds. + Weight::from_parts(749_000, 0) } } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs index bec5b923d8ad9a23f1b176c9c4f909855df17422..25256495ef91e48c9e024d168486aecdc9620751 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs @@ -34,7 +34,7 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use sp_runtime::traits::AccountIdConversion; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, @@ -51,7 +51,7 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const RootLocation: Location = Location::here(); pub const RelayLocation: Location = Location::parent(); - pub const RelayNetwork: Option = Some(NetworkId::Westend); + pub const RelayNetwork: Option = Some(NetworkId::ByGenesis(WESTEND_GENESIS_HASH)); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into(); diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs index 36cf2bf4f83b504315146fa9e4a9091370c76e5f..05ecf6ca8e814dc4d064b21be18d7b92b789f792 100644 --- a/cumulus/parachains/runtimes/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs @@ -441,15 +441,11 @@ impl< AllPalletsWithoutSystem, > RuntimeHelper { - pub fn execute_as_governance(call: Vec, require_weight_at_most: Weight) -> Outcome { + pub fn execute_as_governance(call: Vec) -> Outcome { // prepare xcm as governance will do let xcm = Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Superuser, - require_weight_at_most, - call: call.into(), - }, + Transact { origin_kind: OriginKind::Superuser, call: call.into() }, ExpectTransactStatus(MaybeErrorCode::Success), ]); @@ -473,11 +469,7 @@ impl< let xcm = Xcm(vec![ WithdrawAsset(buy_execution_fee.clone().into()), BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: call.get_dispatch_info().call_weight, - call: call.encode().into(), - }, + Transact { origin_kind: OriginKind::Xcm, call: call.encode().into() }, ExpectTransactStatus(MaybeErrorCode::Success), ]); diff --git a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs index 1c58df189b673af60bc3e4a9ae7391294287e2aa..a66163154cf696bf4f0708075e9cf853417d4700 100644 --- a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs @@ -72,17 +72,9 @@ pub fn change_storage_constant_by_governance_works::SystemWeightInfo::set_storage(1); - // execute XCM with Transact to `set_storage` as governance does - assert_ok!(RuntimeHelper::::execute_as_governance( - set_storage_call, - require_weight_at_most - ) - .ensure_complete()); + assert_ok!(RuntimeHelper::::execute_as_governance(set_storage_call,) + .ensure_complete()); // check delivery reward constant after (stored) assert_eq!(StorageConstant::get(), new_storage_constant_value); @@ -127,19 +119,10 @@ pub fn set_storage_keys_by_governance_works( items: storage_items.clone(), }); - // estimate - storing just 1 value - use frame_system::WeightInfo; - let require_weight_at_most = - ::SystemWeightInfo::set_storage( - storage_items.len().try_into().unwrap(), - ); - // execute XCM with Transact to `set_storage` as governance does - assert_ok!(RuntimeHelper::::execute_as_governance( - kill_storage_call, - require_weight_at_most - ) - .ensure_complete()); + assert_ok!( + RuntimeHelper::::execute_as_governance(kill_storage_call,).ensure_complete() + ); }); runtime.execute_with(|| { assert_storage(); diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index 14c4fe5203847e8789a7432d323d51abfaae8a89..3a6b9d42f211bbca9895454c16483dff2d9042b8 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -79,6 +79,7 @@ pallet-collator-selection = { workspace = true } parachain-info = { workspace = true } parachains-common = { workspace = true } assets-common = { workspace = true } +snowbridge-router-primitives = { workspace = true } primitive-types = { version = "0.12.1", default-features = false, features = ["codec", "num-traits", "scale-info"] } @@ -123,6 +124,7 @@ std = [ "polkadot-runtime-common/std", "primitive-types/std", "scale-info/std", + "snowbridge-router-primitives/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", @@ -168,6 +170,7 @@ runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 136592c5602601072e843f9d28c0bf60c7bd5bd7..b51670c792d6badee368c5b176d086cc72bacc04 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -36,6 +36,7 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use assets_common::{ + foreign_creators::ForeignCreators, local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, AssetIdForTrustBackedAssetsConvert, }; @@ -73,7 +74,7 @@ use sp_api::impl_runtime_apis; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Dispatchable}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, @@ -82,7 +83,7 @@ pub use sp_runtime::{traits::ConvertInto, MultiAddress, Perbill, Permill}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm_config::{ForeignAssetsAssetId, XcmOriginToTransactDispatchOrigin}; +use xcm_config::{ForeignAssetsAssetId, LocationToAccountId, XcmOriginToTransactDispatchOrigin}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -242,8 +243,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("penpal-parachain"), - impl_name: create_runtime_str!("penpal-parachain"), + spec_name: alloc::borrow::Cow::Borrowed("penpal-parachain"), + impl_name: alloc::borrow::Cow::Borrowed("penpal-parachain"), authoring_version: 1, spec_version: 1, impl_version: 0, @@ -494,7 +495,10 @@ impl pallet_assets::Config for Runtime { type AssetId = ForeignAssetsAssetId; type AssetIdParameter = ForeignAssetsAssetId; type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; + // This is to allow any other remote location to create foreign assets. Used in tests, not + // recommended on real chains. + type CreateOrigin = + ForeignCreators; type ForceOrigin = EnsureRoot; type AssetDeposit = ForeignAssetsAssetDeposit; type MetadataDepositBase = ForeignAssetsMetadataDepositBase; @@ -1117,7 +1121,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{Benchmarking, BenchmarkBatch}; use sp_storage::TrackedStorageKey; diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index b72d6d232a1d13b89551cf5158d2da699b049e41..375c3d509f48e24cddb1d49685db3fb392795267 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -44,13 +44,15 @@ use pallet_xcm::XcmPassthrough; use parachains_common::{xcm_config::AssetFeeAsExistentialDepositMultiplier, TREASURY_PALLET_ID}; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::{impls::ToAuthor, xcm_sender::ExponentialPrice}; +use snowbridge_router_primitives::inbound::EthereumLocationsConverterFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto, Identity, TryConvertInto}; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ - AccountId32Aliases, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, - AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FixedWeightBounds, - FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, + AccountId32Aliases, AliasOriginRootUsingFilter, AllowHrmpNotificationsFromRelayChain, + AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + AsPrefixedGeneralIndex, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, + FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, @@ -65,8 +67,8 @@ parameter_types! { pub const PenpalNativeCurrency: Location = Location::here(); // The Penpal runtime is utilized for testing with various environment setups. // This storage item allows us to customize the `NetworkId` where Penpal is deployed. - // By default, it is set to `NetworkId::Rococo` and can be changed using `System::set_storage`. - pub storage RelayNetworkId: NetworkId = NetworkId::Westend; + // By default, it is set to `Westend Network` and can be changed using `System::set_storage`. + pub storage RelayNetworkId: NetworkId = NetworkId::ByGenesis(WESTEND_GENESIS_HASH); pub RelayNetwork: Option = Some(RelayNetworkId::get()); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [ @@ -92,6 +94,12 @@ pub type LocationToAccountId = ( AccountId32Aliases, // Foreign locations alias into accounts according to a hash of their standard description. HashedDescription>, + // Different global consensus parachain sovereign account. + // (Used for over-bridge transfers and reserve processing) + GlobalConsensusParachainConvertsFor, + // Ethereum contract sovereign account. + // (Used to get convert ethereum contract locations to sovereign account) + EthereumLocationsConverterFor, ); /// Means for transacting assets on this chain. @@ -398,7 +406,8 @@ impl xcm_executor::Config for XcmConfig { type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; - type Aliasers = Nothing; + // We allow trusted Asset Hub root to alias other locations. + type Aliasers = AliasOriginRootUsingFilter; type TransactionalProcessor = FrameTransactionalProcessor; type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 34bd45b6ef990b85ede27c56f9291dcdee59dc80..42556e0b493cd6fb413742a67f7ce08012775213 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -30,7 +30,7 @@ use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use sp_api::impl_runtime_apis; use sp_core::OpaqueMetadata; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, Hash as HashT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, @@ -85,7 +85,7 @@ use xcm_executor::traits::JustTry; // XCM imports use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; use polkadot_parachain_primitives::primitives::Sibling; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, FungibleAdapter, IsConcrete, NativeAsset, @@ -106,8 +106,8 @@ impl_opaque_keys! { /// This runtime version. #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("test-parachain"), - impl_name: create_runtime_str!("test-parachain"), + spec_name: alloc::borrow::Cow::Borrowed("test-parachain"), + impl_name: alloc::borrow::Cow::Borrowed("test-parachain"), authoring_version: 1, spec_version: 1_014_000, impl_version: 0, @@ -332,7 +332,7 @@ impl cumulus_pallet_aura_ext::Config for Runtime {} parameter_types! { pub const RocLocation: Location = Location::parent(); - pub const RococoNetwork: NetworkId = NetworkId::Rococo; + pub const RococoNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RococoNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); diff --git a/cumulus/polkadot-omni-node/README.md b/cumulus/polkadot-omni-node/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d87b3b63c4071668a933d4ec28756dbfab43e08f --- /dev/null +++ b/cumulus/polkadot-omni-node/README.md @@ -0,0 +1,65 @@ +# Polkadot Omni Node + +This is a white labeled implementation based on [`polkadot-omni-node-lib`](https://crates.io/crates/polkadot-omni-node-lib). +It can be used to start a parachain node from a provided chain spec file. It is only compatible with runtimes that use block +number `u32` and `Aura` consensus. + +## Installation + +Download & expose it via `PATH`: + +```bash +# Download and set it on PATH. +wget https://github.com/paritytech/polkadot-sdk/releases/download//polkadot-omni-node +chmod +x polkadot-omni-node +export PATH="$PATH:`pwd`" +``` + +Compile & install via `cargo`: + +```bash +# Assuming ~/.cargo/bin is on the PATH +cargo install polkadot-omni-node +``` + +## Usage + +A basic example for an Omni Node run starts from a runtime which implements the [`sp_genesis_builder::GenesisBuilder`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html). +The interface mandates the runtime to expose a [`named-preset`](https://docs.rs/staging-chain-spec-builder/latest/staging_chain_spec_builder/#generate-chain-spec-using-runtime-provided-genesis-config-preset). + +### 1. Install chain-spec-builder + +**Note**: `chain-spec-builder` binary is published on [`crates.io`](https://crates.io) under +[`staging-chain-spec-builder`](https://crates.io/crates/staging-chain-spec-builder) due to a name conflict. +Install it with `cargo` like bellow : + +```bash +cargo install staging-chain-spec-builder +``` + +### 2. Generate a chain spec + +Omni Node expects for the chain spec to contain parachains related fields like `relay_chain` and `para_id`. +These fields can be introduced by running [`staging-chain-spec-builder`](https://crates.io/crates/staging-chain-spec-builder) +with additional flags: + +```bash +chain-spec-builder create --relay-chain --para-id -r named-preset +``` + +### 3. Run Omni Node + +And now with the generated chain spec we can start Omni Node like so: + +```bash +polkadot-omni-node --chain +``` + +## Useful links + +* [`Omni Node Polkadot SDK Docs`](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html) +* [`Chain Spec Genesis Reference Docs`](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/chain_spec_genesis/index.html) +* [`polkadot-parachain-bin`](https://crates.io/crates/polkadot-parachain-bin) +* [`polkadot-sdk-parachain-template`](https://github.com/paritytech/polkadot-sdk-parachain-template) +* [`frame-omni-bencher`](https://crates.io/crates/frame-omni-bencher) +* [`staging-chain-spec-builder`](https://crates.io/crates/staging-chain-spec-builder) diff --git a/cumulus/polkadot-omni-node/lib/README.md b/cumulus/polkadot-omni-node/lib/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5789a35a101693a3edfdce6c634aba6ba20eee5c --- /dev/null +++ b/cumulus/polkadot-omni-node/lib/README.md @@ -0,0 +1,26 @@ +# Polkadot Omni Node Library + +Helper library that can be used to run a parachain node. + +## Overview + +This library can be used to run a parachain node while also customizing the chain specs +that are supported by default by the `--chain-spec` argument of the node's `CLI` +and the parameters of the runtime that is associated with each of these chain specs. + +## API + +The library exposes the possibility to provide a [`RunConfig`]. Through this structure +2 optional configurations can be provided: +- a chain spec loader (an implementation of [`chain_spec::LoadSpec`]): this can be used for + providing the chain specs that are supported by default by the `--chain-spec` argument of the + node's `CLI` and the actual chain config associated with each one. +- a runtime resolver (an implementation of [`runtime::RuntimeResolver`]): this can be used for + providing the parameters of the runtime that is associated with each of the chain specs + +Apart from this, a [`CliConfig`] can also be provided, that can be used to customize some +user-facing binary author, support url, etc. + +## Examples + +For an example, see the [`polkadot-parachain-bin`](https://crates.io/crates/polkadot-parachain-bin) crate. diff --git a/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs index 0b1ed5d82889e5f70898dd5b82696e78c247cf3b..6bfd5f4f4cbd1743782303d0d5f6ab78d16c7b4a 100644 --- a/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs +++ b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs @@ -202,7 +202,7 @@ macro_rules! impl_node_runtime_apis { fn dispatch_benchmark( _: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, String> { unimplemented!() } } diff --git a/cumulus/polkadot-omni-node/lib/src/lib.rs b/cumulus/polkadot-omni-node/lib/src/lib.rs index 3f01f42111444342ef85e68996570df34e30a649..ccc1b542b253dd9e002baf3f62f23a6dc3b32f02 100644 --- a/cumulus/polkadot-omni-node/lib/src/lib.rs +++ b/cumulus/polkadot-omni-node/lib/src/lib.rs @@ -14,40 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -//! # Polkadot Omni Node Library -//! -//! Helper library that can be used to run a parachain node. -//! -//! ## Overview -//! -//! This library can be used to run a parachain node while also customizing the chain specs -//! that are supported by default by the `--chain-spec` argument of the node's `CLI` -//! and the parameters of the runtime that is associated with each of these chain specs. -//! -//! ## API -//! -//! The library exposes the possibility to provide a [`RunConfig`]. Through this structure -//! 2 optional configurations can be provided: -//! - a chain spec loader (an implementation of [`chain_spec::LoadSpec`]): this can be used for -//! providing the chain specs that are supported by default by the `--chain-spec` argument of the -//! node's `CLI` and the actual chain config associated with each one. -//! - a runtime resolver (an implementation of [`runtime::RuntimeResolver`]): this can be used for -//! providing the parameters of the runtime that is associated with each of the chain specs -//! -//! Apart from this, a [`CliConfig`] can also be provided, that can be used to customize some -//! user-facing binary author, support url, etc. -//! -//! ## Examples -//! -//! For an example, see the [`polkadot-parachain-bin`](https://crates.io/crates/polkadot-parachain-bin) crate. -//! -//! ## Binary -//! -//! It can be used to start a parachain node from a provided chain spec file. -//! It is only compatible with runtimes that use block number `u32` and `Aura` consensus. -//! -//! Example: `polkadot-omni-node --chain ` - +#![doc = include_str!("../README.md")] #![deny(missing_docs)] pub mod cli; diff --git a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs index e8043bd7b2aac6c8104262b4e1e5dfd3a76de577..b7fc3489da2549978f948f299b04ae87c441e16c 100644 --- a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs +++ b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs @@ -25,7 +25,7 @@ use cumulus_primitives_core::ParaId; use sc_consensus::{DefaultImportQueue, LongestChain}; use sc_consensus_manual_seal::rpc::{ManualSeal, ManualSealApiServer}; use sc_network::NetworkBackend; -use sc_service::{build_polkadot_syncing_strategy, Configuration, PartialComponents, TaskManager}; +use sc_service::{Configuration, PartialComponents, TaskManager}; use sc_telemetry::TelemetryHandle; use sp_runtime::traits::Header; use std::{marker::PhantomData, sync::Arc}; @@ -85,7 +85,7 @@ impl ManualSealNode { // Since this is a dev node, prevent it from connecting to peers. config.network.default_peers_set.in_peers = 0; config.network.default_peers_set.out_peers = 0; - let mut net_config = sc_network::config::FullNetworkConfiguration::<_, _, Net>::new( + let net_config = sc_network::config::FullNetworkConfiguration::<_, _, Net>::new( &config.network, config.prometheus_config.as_ref().map(|cfg| cfg.registry.clone()), ); @@ -93,16 +93,6 @@ impl ManualSealNode { config.prometheus_config.as_ref().map(|cfg| &cfg.registry), ); - let syncing_strategy = build_polkadot_syncing_strategy( - config.protocol_id(), - config.chain_spec.fork_id(), - &mut net_config, - None, - client.clone(), - &task_manager.spawn_handle(), - config.prometheus_config.as_ref().map(|config| &config.registry), - )?; - let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -112,7 +102,7 @@ impl ManualSealNode { import_queue, net_config, block_announce_validator_builder: None, - syncing_strategy, + warp_sync_config: None, block_relay: None, metrics, })?; diff --git a/cumulus/primitives/core/src/lib.rs b/cumulus/primitives/core/src/lib.rs index dfb574ef33018fd138a767e5f2671d38afc32729..f88e663db19eef3095fbd2282566f50e9cc6d386 100644 --- a/cumulus/primitives/core/src/lib.rs +++ b/cumulus/primitives/core/src/lib.rs @@ -333,10 +333,6 @@ pub mod rpsr_digest { } } -/// The default claim queue offset to be used if it's not configured/accessible in the parachain -/// runtime -pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 0; - /// Information about a collation. /// /// This was used in version 1 of the [`CollectCollationInfo`] runtime api. diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs index 5471640695ca2939628af0c19bb0606926ebf177..5cbe662e2700924c55814680cda4d0428ad0c450 100644 --- a/cumulus/primitives/storage-weight-reclaim/src/lib.rs +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -198,7 +198,7 @@ where let block_weight_proof_size = current.total().proof_size(); let missing_from_node = node_side_pov_size.saturating_sub(block_weight_proof_size); if missing_from_node > 0 { - log::warn!( + log::debug!( target: LOG_TARGET, "Node-side PoV size higher than runtime proof size weight. node-side: {node_side_pov_size} extrinsic_len: {extrinsic_len} runtime: {block_weight_proof_size}, missing: {missing_from_node}. Setting to node-side proof size." ); diff --git a/cumulus/test/runtime/src/genesis_config_presets.rs b/cumulus/test/runtime/src/genesis_config_presets.rs index 6cf56ef5363fd5a4da76bbcf7e6cebed9b88b11b..84ba71ae795fd508f8a8bfe0eddb269537ae6758 100644 --- a/cumulus/test/runtime/src/genesis_config_presets.rs +++ b/cumulus/test/runtime/src/genesis_config_presets.rs @@ -58,9 +58,9 @@ pub fn preset_names() -> Vec { /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &PresetId) -> Option> { - let patch = match id.try_into() { - Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) | - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => + let patch = match id.as_ref() { + sp_genesis_builder::DEV_RUNTIME_PRESET | + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => testnet_genesis_with_default_endowed(100.into()), _ => return None, }; diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 01c1571f343f8ec61322d0337b075fc33fc6bba5..b1649c410581a0f725e76312a535965db5e3cb6e 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -45,7 +45,7 @@ use sp_core::{ConstBool, ConstU32, ConstU64, OpaqueMetadata}; use cumulus_primitives_core::{ClaimQueueOffset, CoreSelector}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, Verify}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, MultiAddress, MultiSignature, @@ -126,8 +126,8 @@ const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; #[cfg(not(feature = "increment-spec-version"))] #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("cumulus-test-parachain"), - impl_name: create_runtime_str!("cumulus-test-parachain"), + spec_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"), + impl_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"), authoring_version: 1, // Read the note above. spec_version: 1, @@ -140,8 +140,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { #[cfg(feature = "increment-spec-version")] #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("cumulus-test-parachain"), - impl_name: create_runtime_str!("cumulus-test-parachain"), + spec_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"), + impl_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"), authoring_version: 1, // Read the note above. spec_version: 2, diff --git a/cumulus/xcm/xcm-emulator/src/lib.rs b/cumulus/xcm/xcm-emulator/src/lib.rs index b91246a7bda22a8bb3bfedb57cca28ce09ce8f07..ff14b747973cf3e1b0658dc147a1749e2ca20ed1 100644 --- a/cumulus/xcm/xcm-emulator/src/lib.rs +++ b/cumulus/xcm/xcm-emulator/src/lib.rs @@ -36,7 +36,10 @@ pub use core::{cell::RefCell, fmt::Debug}; pub use cumulus_primitives_core::AggregateMessageOrigin as CumulusAggregateMessageOrigin; pub use frame_support::{ assert_ok, - sp_runtime::{traits::Header as HeaderT, DispatchResult}, + sp_runtime::{ + traits::{Dispatchable, Header as HeaderT}, + DispatchResult, + }, traits::{ EnqueueMessage, ExecuteOverweightError, Get, Hooks, OnInitialize, OriginTrait, ProcessMessage, ProcessMessageError, ServiceQueues, @@ -221,7 +224,7 @@ pub trait Network { pub trait Chain: TestExt { type Network: Network; type Runtime: SystemConfig; - type RuntimeCall; + type RuntimeCall: Clone + Dispatchable; type RuntimeOrigin; type RuntimeEvent; type System; @@ -1221,7 +1224,7 @@ macro_rules! __impl_check_assertion { Args: Clone, { fn check_assertion(test: $crate::Test) { - use $crate::TestExt; + use $crate::{Dispatchable, TestExt}; let chain_name = std::any::type_name::<$chain<$network>>(); @@ -1229,6 +1232,15 @@ macro_rules! __impl_check_assertion { if let Some(dispatchable) = test.hops_dispatchable.get(chain_name) { $crate::assert_ok!(dispatchable(test.clone())); } + if let Some(call) = test.hops_calls.get(chain_name) { + $crate::assert_ok!( + match call.clone().dispatch(test.signed_origin.clone()) { + // We get rid of `post_info`. + Ok(_) => Ok(()), + Err(error_with_post_info) => Err(error_with_post_info.error), + } + ); + } if let Some(assertion) = test.hops_assertion.get(chain_name) { assertion(test); } @@ -1530,11 +1542,12 @@ where pub root_origin: Origin::RuntimeOrigin, pub hops_assertion: HashMap, pub hops_dispatchable: HashMap DispatchResult>, + pub hops_calls: HashMap, pub args: Args, _marker: PhantomData<(Destination, Hops)>, } -/// `Test` implementation +/// `Test` implementation. impl Test where Args: Clone, @@ -1544,7 +1557,7 @@ where Destination::RuntimeOrigin: OriginTrait> + Clone, Hops: Clone + CheckAssertion, { - /// Creates a new `Test` instance + /// Creates a new `Test` instance. pub fn new(test_args: TestContext) -> Self { Test { sender: TestAccount { @@ -1559,6 +1572,7 @@ where root_origin: ::RuntimeOrigin::root(), hops_assertion: Default::default(), hops_dispatchable: Default::default(), + hops_calls: Default::default(), args: test_args.args, _marker: Default::default(), } @@ -1573,6 +1587,11 @@ where let chain_name = std::any::type_name::(); self.hops_dispatchable.insert(chain_name.to_string(), dispatchable); } + /// Stores a call in a particular Chain, this will later be dispatched. + pub fn set_call(&mut self, call: Origin::RuntimeCall) { + let chain_name = std::any::type_name::(); + self.hops_calls.insert(chain_name.to_string(), call); + } /// Executes all dispatchables and assertions in order from `Origin` to `Destination` pub fn assert(&mut self) { Origin::check_assertion(self.clone()); diff --git a/docs/sdk/packages/guides/first-runtime/src/lib.rs b/docs/sdk/packages/guides/first-runtime/src/lib.rs index 92d51962b6febb03a4da80b3003e70ca174bbaf8..61ca550c8750743d1291843710627b5d106b6277 100644 --- a/docs/sdk/packages/guides/first-runtime/src/lib.rs +++ b/docs/sdk/packages/guides/first-runtime/src/lib.rs @@ -31,8 +31,8 @@ use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, RuntimeDispatchInfo #[docify::export] #[runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("first-runtime"), - impl_name: create_runtime_str!("first-runtime"), + spec_name: alloc::borrow::Cow::Borrowed("first-runtime"), + impl_name: alloc::borrow::Cow::Borrowed("first-runtime"), authoring_version: 1, spec_version: 0, impl_version: 1, @@ -130,30 +130,28 @@ pub mod genesis_config_presets { interface::{Balance, MinimumBalance}, BalancesConfig, RuntimeGenesisConfig, SudoConfig, }; + use frame::deps::frame_support::build_struct_json_patch; use serde_json::Value; /// Returns a development genesis config preset. #[docify::export] pub fn development_config_genesis() -> Value { let endowment = >::get().max(1) * 1000; - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: AccountKeyring::iter() .map(|a| (a.to_account_id(), endowment)) .collect::>(), }, sudo: SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + }) } /// Get the set of the available genesis config presets. #[docify::export] pub fn get_preset(id: &PresetId) -> Option> { - let patch = match id.try_into() { - Ok(DEV_RUNTIME_PRESET) => development_config_genesis(), + let patch = match id.as_ref() { + DEV_RUNTIME_PRESET => development_config_genesis(), _ => return None, }; Some( diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs index c1316b2f87347811409c0f6581abf57c20c3def1..5918f2b8ccd5961566ab96360f98c8e0a11c462a 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs @@ -121,12 +121,12 @@ fn preset_invalid() -> Value { /// If no preset with given `id` exits `None` is returned. #[docify::export] pub fn get_builtin_preset(id: &sp_genesis_builder::PresetId) -> Option> { - let preset = match id.try_into() { - Ok(PRESET_1) => preset_1(), - Ok(PRESET_2) => preset_2(), - Ok(PRESET_3) => preset_3(), - Ok(PRESET_4) => preset_4(), - Ok(PRESET_INVALID) => preset_invalid(), + let preset = match id.as_ref() { + PRESET_1 => preset_1(), + PRESET_2 => preset_2(), + PRESET_3 => preset_3(), + PRESET_4 => preset_4(), + PRESET_INVALID => preset_invalid(), _ => return None, }; diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs index 5be3a59dc7bb84304ae38c9758439a2a1c99795b..282fc1ff489c0195973d712c085688bdaf77a30a 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs @@ -39,8 +39,8 @@ use sp_genesis_builder::PresetId; /// The runtime version. #[runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("minimal-template-runtime"), - impl_name: create_runtime_str!("minimal-template-runtime"), + spec_name: alloc::borrow::Cow::Borrowed("minimal-template-runtime"), + impl_name: alloc::borrow::Cow::Borrowed("minimal-template-runtime"), authoring_version: 1, spec_version: 0, impl_version: 1, diff --git a/polkadot/cli/src/cli.rs b/polkadot/cli/src/cli.rs index eb67a39563421c87e4d129b19498535b329edc3a..777bb9c60671244e56153cf666d8f4702d1abaf5 100644 --- a/polkadot/cli/src/cli.rs +++ b/polkadot/cli/src/cli.rs @@ -79,7 +79,7 @@ pub struct RunCmd { /// Disable the BEEFY gadget. /// - /// Currently enabled by default on 'Rococo', 'Wococo' and 'Versi'. + /// Currently enabled by default. #[arg(long)] pub no_beefy: bool, diff --git a/polkadot/node/collation-generation/Cargo.toml b/polkadot/node/collation-generation/Cargo.toml index 855b6b0e86eb5ba8e624c572937c841662276646..777458673f5b39dba447fb14467bf22b7a6968f3 100644 --- a/polkadot/node/collation-generation/Cargo.toml +++ b/polkadot/node/collation-generation/Cargo.toml @@ -21,6 +21,7 @@ sp-core = { workspace = true, default-features = true } sp-maybe-compressed-blob = { workspace = true, default-features = true } thiserror = { workspace = true } codec = { features = ["bit-vec", "derive"], workspace = true } +schnellru = { workspace = true } [dev-dependencies] polkadot-node-subsystem-test-helpers = { workspace = true } diff --git a/polkadot/node/collation-generation/src/error.rs b/polkadot/node/collation-generation/src/error.rs index f04e3c4f20b41f3a0934c65c0563d33edf45487a..2599026080df77ffd120f767dffd0abff126d682 100644 --- a/polkadot/node/collation-generation/src/error.rs +++ b/polkadot/node/collation-generation/src/error.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +use polkadot_primitives::vstaging::CommittedCandidateReceiptError; use thiserror::Error; #[derive(Debug, Error)] @@ -30,8 +31,12 @@ pub enum Error { UtilRuntime(#[from] polkadot_node_subsystem_util::runtime::Error), #[error(transparent)] Erasure(#[from] polkadot_erasure_coding::Error), - #[error("Parachain backing state not available in runtime.")] - MissingParaBackingState, + #[error("Collation submitted before initialization")] + SubmittedBeforeInit, + #[error("V2 core index check failed: {0}")] + CandidateReceiptCheck(CommittedCandidateReceiptError), + #[error("PoV size {0} exceeded maximum size of {1}")] + POVSizeExceeded(usize, usize), } pub type Result = std::result::Result; diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index f04f69cbd38036774ef58612bd70f0f870c0fb87..b371017a8289a9614661b5b3b0d700019762ab3d 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -32,27 +32,34 @@ #![deny(missing_docs)] use codec::Encode; -use futures::{channel::oneshot, future::FutureExt, join, select}; +use error::{Error, Result}; +use futures::{channel::oneshot, future::FutureExt, select}; use polkadot_node_primitives::{ AvailableData, Collation, CollationGenerationConfig, CollationSecondedSignal, PoV, SubmitCollationParams, }; use polkadot_node_subsystem::{ - messages::{CollationGenerationMessage, CollatorProtocolMessage}, - overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, RuntimeApiError, SpawnedSubsystem, - SubsystemContext, SubsystemError, SubsystemResult, + messages::{CollationGenerationMessage, CollatorProtocolMessage, RuntimeApiMessage}, + overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, + SubsystemContext, SubsystemError, SubsystemResult, SubsystemSender, }; use polkadot_node_subsystem_util::{ - request_async_backing_params, request_availability_cores, request_para_backing_state, - request_persisted_validation_data, request_validation_code, request_validation_code_hash, - request_validators, runtime::fetch_claim_queue, + request_claim_queue, request_persisted_validation_data, request_session_index_for_child, + request_validation_code_hash, request_validators, + runtime::{request_node_features, ClaimQueueSnapshot}, }; use polkadot_primitives::{ collator_signature_payload, - vstaging::{CandidateReceiptV2 as CandidateReceipt, CoreState}, + node_features::FeatureIndex, + vstaging::{ + transpose_claim_queue, CandidateDescriptorV2, CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2, TransposedClaimQueue, + }, CandidateCommitments, CandidateDescriptor, CollatorPair, CoreIndex, Hash, Id as ParaId, - OccupiedCoreAssumption, PersistedValidationData, ScheduledCore, ValidationCodeHash, + NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, SessionIndex, + ValidationCodeHash, }; +use schnellru::{ByLength, LruMap}; use sp_core::crypto::Pair; use std::sync::Arc; @@ -69,6 +76,7 @@ const LOG_TARGET: &'static str = "parachain::collation-generation"; /// Collation Generation Subsystem pub struct CollationGenerationSubsystem { config: Option>, + session_info_cache: SessionInfoCache, metrics: Metrics, } @@ -76,7 +84,7 @@ pub struct CollationGenerationSubsystem { impl CollationGenerationSubsystem { /// Create a new instance of the `CollationGenerationSubsystem`. pub fn new(metrics: Metrics) -> Self { - Self { config: None, metrics } + Self { config: None, metrics, session_info_cache: SessionInfoCache::new() } } /// Run this subsystem @@ -117,19 +125,8 @@ impl CollationGenerationSubsystem { activated, .. }))) => { - // follow the procedure from the guide - if let Some(config) = &self.config { - let metrics = self.metrics.clone(); - if let Err(err) = handle_new_activations( - config.clone(), - activated.into_iter().map(|v| v.hash), - ctx, - metrics, - ) - .await - { - gum::warn!(target: LOG_TARGET, err = ?err, "failed to handle new activations"); - } + if let Err(err) = self.handle_new_activation(activated.map(|v| v.hash), ctx).await { + gum::warn!(target: LOG_TARGET, err = ?err, "failed to handle new activation"); } false @@ -154,14 +151,8 @@ impl CollationGenerationSubsystem { Ok(FromOrchestra::Communication { msg: CollationGenerationMessage::SubmitCollation(params), }) => { - if let Some(config) = &self.config { - if let Err(err) = - handle_submit_collation(params, config, ctx, &self.metrics).await - { - gum::error!(target: LOG_TARGET, ?err, "Failed to submit collation"); - } - } else { - gum::error!(target: LOG_TARGET, "Collation submitted before initialization"); + if let Err(err) = self.handle_submit_collation(params, ctx).await { + gum::error!(target: LOG_TARGET, ?err, "Failed to submit collation"); } false @@ -178,175 +169,132 @@ impl CollationGenerationSubsystem { }, } } -} - -#[overseer::subsystem(CollationGeneration, error=SubsystemError, prefix=self::overseer)] -impl CollationGenerationSubsystem { - fn start(self, ctx: Context) -> SpawnedSubsystem { - let future = async move { - self.run(ctx).await; - Ok(()) - } - .boxed(); - - SpawnedSubsystem { name: "collation-generation-subsystem", future } - } -} -#[overseer::contextbounds(CollationGeneration, prefix = self::overseer)] -async fn handle_new_activations( - config: Arc, - activated: impl IntoIterator, - ctx: &mut Context, - metrics: Metrics, -) -> crate::error::Result<()> { - // follow the procedure from the guide: - // https://paritytech.github.io/polkadot-sdk/book/node/collators/collation-generation.html - - // If there is no collation function provided, bail out early. - // Important: Lookahead collator and slot based collator do not use `CollatorFn`. - if config.collator.is_none() { - return Ok(()) - } - - let para_id = config.para_id; - - let _overall_timer = metrics.time_new_activations(); - - for relay_parent in activated { - let _relay_parent_timer = metrics.time_new_activations_relay_parent(); - - let (availability_cores, validators, async_backing_params) = join!( - request_availability_cores(relay_parent, ctx.sender()).await, - request_validators(relay_parent, ctx.sender()).await, - request_async_backing_params(relay_parent, ctx.sender()).await, - ); - - let availability_cores = availability_cores??; - let async_backing_params = async_backing_params?.ok(); - let n_validators = validators??.len(); - let maybe_claim_queue = fetch_claim_queue(ctx.sender(), relay_parent) - .await - .map_err(crate::error::Error::UtilRuntime)?; - - // The loop bellow will fill in cores that the para is allowed to build on. - let mut cores_to_build_on = Vec::new(); - - // This assumption refers to all cores of the parachain, taking elastic scaling - // into account. - let mut para_assumption = None; - for (core_idx, core) in availability_cores.into_iter().enumerate() { - // This nested assumption refers only to the core being iterated. - let (core_assumption, scheduled_core) = match core { - CoreState::Scheduled(scheduled_core) => - (OccupiedCoreAssumption::Free, scheduled_core), - CoreState::Occupied(occupied_core) => match async_backing_params { - Some(params) if params.max_candidate_depth >= 1 => { - // maximum candidate depth when building on top of a block - // pending availability is necessarily 1 - the depth of the - // pending block is 0 so the child has depth 1. - - // Use claim queue if available, or fallback to `next_up_on_available` - let res = match maybe_claim_queue { - Some(ref claim_queue) => { - // read what's in the claim queue for this core at depth 0. - claim_queue - .get_claim_for(CoreIndex(core_idx as u32), 0) - .map(|para_id| ScheduledCore { para_id, collator: None }) - }, - None => { - // Runtime doesn't support claim queue runtime api. Fallback to - // `next_up_on_available` - occupied_core.next_up_on_available - }, - }; + async fn handle_submit_collation( + &mut self, + params: SubmitCollationParams, + ctx: &mut Context, + ) -> Result<()> { + let Some(config) = &self.config else { + return Err(Error::SubmittedBeforeInit); + }; + let _timer = self.metrics.time_submit_collation(); - match res { - Some(res) => (OccupiedCoreAssumption::Included, res), - None => continue, - } - }, - _ => { - gum::trace!( - target: LOG_TARGET, - core_idx = %core_idx, - relay_parent = ?relay_parent, - "core is occupied. Keep going.", - ); - continue - }, - }, - CoreState::Free => { - gum::trace!( - target: LOG_TARGET, - core_idx = %core_idx, - "core is not assigned to any para. Keep going.", - ); - continue - }, - }; + let SubmitCollationParams { + relay_parent, + collation, + parent_head, + validation_code_hash, + result_sender, + core_index, + } = params; - if scheduled_core.para_id != config.para_id { - gum::trace!( + let mut validation_data = match request_persisted_validation_data( + relay_parent, + config.para_id, + OccupiedCoreAssumption::TimedOut, + ctx.sender(), + ) + .await + .await?? + { + Some(v) => v, + None => { + gum::debug!( target: LOG_TARGET, - core_idx = %core_idx, relay_parent = ?relay_parent, our_para = %config.para_id, - their_para = %scheduled_core.para_id, - "core is not assigned to our para. Keep going.", + "No validation data for para - does it exist at this relay-parent?", ); - } else { - // This does not work for elastic scaling, but it should be enough for single - // core parachains. If async backing runtime is available we later override - // the assumption based on the `para_backing_state` API response. - para_assumption = Some(core_assumption); - // Accumulate cores for building collation(s) outside the loop. - cores_to_build_on.push(CoreIndex(core_idx as u32)); - } - } + return Ok(()) + }, + }; - // Skip to next relay parent if there is no core assigned to us. - if cores_to_build_on.is_empty() { - continue + // We need to swap the parent-head data, but all other fields here will be correct. + validation_data.parent_head = parent_head; + + let claim_queue = request_claim_queue(relay_parent, ctx.sender()).await.await??; + + let session_index = + request_session_index_for_child(relay_parent, ctx.sender()).await.await??; + + let session_info = + self.session_info_cache.get(relay_parent, session_index, ctx.sender()).await?; + let collation = PreparedCollation { + collation, + relay_parent, + para_id: config.para_id, + validation_data, + validation_code_hash, + n_validators: session_info.n_validators, + core_index, + session_index, + }; + + construct_and_distribute_receipt( + collation, + config.key.clone(), + ctx.sender(), + result_sender, + &mut self.metrics, + session_info.v2_receipts, + &transpose_claim_queue(claim_queue), + ) + .await?; + + Ok(()) + } + + async fn handle_new_activation( + &mut self, + maybe_activated: Option, + ctx: &mut Context, + ) -> Result<()> { + let Some(config) = &self.config else { + return Ok(()); + }; + + let Some(relay_parent) = maybe_activated else { return Ok(()) }; + + // If there is no collation function provided, bail out early. + // Important: Lookahead collator and slot based collator do not use `CollatorFn`. + if config.collator.is_none() { + return Ok(()) } - // If at least one core is assigned to us, `para_assumption` is `Some`. - let Some(mut para_assumption) = para_assumption else { continue }; - - // If it is none it means that neither async backing or elastic scaling (which - // depends on it) are supported. We'll use the `para_assumption` we got from - // iterating cores. - if async_backing_params.is_some() { - // We are being very optimistic here, but one of the cores could pend availability some - // more block, ore even time out. - // For timeout assumption the collator can't really know because it doesn't receive - // bitfield gossip. - let para_backing_state = - request_para_backing_state(relay_parent, config.para_id, ctx.sender()) - .await - .await?? - .ok_or(crate::error::Error::MissingParaBackingState)?; - - // Override the assumption about the para's assigned cores. - para_assumption = if para_backing_state.pending_availability.is_empty() { - OccupiedCoreAssumption::Free - } else { - OccupiedCoreAssumption::Included - } + let para_id = config.para_id; + + let _timer = self.metrics.time_new_activation(); + + let session_index = + request_session_index_for_child(relay_parent, ctx.sender()).await.await??; + + let session_info = + self.session_info_cache.get(relay_parent, session_index, ctx.sender()).await?; + let n_validators = session_info.n_validators; + + let claim_queue = + ClaimQueueSnapshot::from(request_claim_queue(relay_parent, ctx.sender()).await.await??); + + let cores_to_build_on = claim_queue + .iter_claims_at_depth(0) + .filter_map(|(core_idx, para_id)| (para_id == config.para_id).then_some(core_idx)) + .collect::>(); + + // Nothing to do if no core assigned to us. + if cores_to_build_on.is_empty() { + return Ok(()) } - gum::debug!( - target: LOG_TARGET, - relay_parent = ?relay_parent, - our_para = %para_id, - ?para_assumption, - "Occupied core(s) assumption", - ); + // We are being very optimistic here, but one of the cores could be pending availability + // for some more blocks, or even time out. We assume all cores are being freed. let mut validation_data = match request_persisted_validation_data( relay_parent, para_id, - para_assumption, + // Just use included assumption always. If there are no pending candidates it's a + // no-op. + OccupiedCoreAssumption::Included, ctx.sender(), ) .await @@ -360,17 +308,20 @@ async fn handle_new_activations( our_para = %para_id, "validation data is not available", ); - continue + return Ok(()) }, }; - let validation_code_hash = match obtain_validation_code_hash_with_assumption( + let validation_code_hash = match request_validation_code_hash( relay_parent, para_id, - para_assumption, + // Just use included assumption always. If there are no pending candidates it's a + // no-op. + OccupiedCoreAssumption::Included, ctx.sender(), ) - .await? + .await + .await?? { Some(v) => v, None => { @@ -380,17 +331,19 @@ async fn handle_new_activations( our_para = %para_id, "validation code hash is not found.", ); - continue + return Ok(()) }, }; let task_config = config.clone(); - let metrics = metrics.clone(); + let metrics = self.metrics.clone(); let mut task_sender = ctx.sender().clone(); ctx.spawn( "chained-collation-builder", Box::pin(async move { + let transposed_claim_queue = transpose_claim_queue(claim_queue.0); + for core_index in cores_to_build_on { let collator_fn = match task_config.collator.as_ref() { Some(x) => x, @@ -411,7 +364,7 @@ async fn handle_new_activations( }; let parent_head = collation.head_data.clone(); - construct_and_distribute_receipt( + if let Err(err) = construct_and_distribute_receipt( PreparedCollation { collation, para_id, @@ -420,13 +373,24 @@ async fn handle_new_activations( validation_code_hash, n_validators, core_index, + session_index, }, task_config.key.clone(), &mut task_sender, result_sender, &metrics, + session_info.v2_receipts, + &transposed_claim_queue, ) - .await; + .await + { + gum::error!( + target: LOG_TARGET, + "Failed to construct and distribute collation: {}", + err + ); + return + } // Chain the collations. All else stays the same as we build the chained // collation on same relay parent. @@ -434,76 +398,64 @@ async fn handle_new_activations( } }), )?; - } - Ok(()) + Ok(()) + } } -#[overseer::contextbounds(CollationGeneration, prefix = self::overseer)] -async fn handle_submit_collation( - params: SubmitCollationParams, - config: &CollationGenerationConfig, - ctx: &mut Context, - metrics: &Metrics, -) -> crate::error::Result<()> { - let _timer = metrics.time_submit_collation(); +#[overseer::subsystem(CollationGeneration, error=SubsystemError, prefix=self::overseer)] +impl CollationGenerationSubsystem { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = async move { + self.run(ctx).await; + Ok(()) + } + .boxed(); - let SubmitCollationParams { - relay_parent, - collation, - parent_head, - validation_code_hash, - result_sender, - core_index, - } = params; + SpawnedSubsystem { name: "collation-generation-subsystem", future } + } +} - let validators = request_validators(relay_parent, ctx.sender()).await.await??; - let n_validators = validators.len(); +#[derive(Clone)] +struct PerSessionInfo { + v2_receipts: bool, + n_validators: usize, +} - // We need to swap the parent-head data, but all other fields here will be correct. - let mut validation_data = match request_persisted_validation_data( - relay_parent, - config.para_id, - OccupiedCoreAssumption::TimedOut, - ctx.sender(), - ) - .await - .await?? - { - Some(v) => v, - None => { - gum::debug!( - target: LOG_TARGET, - relay_parent = ?relay_parent, - our_para = %config.para_id, - "No validation data for para - does it exist at this relay-parent?", - ); - return Ok(()) - }, - }; +struct SessionInfoCache(LruMap); - validation_data.parent_head = parent_head; +impl SessionInfoCache { + fn new() -> Self { + Self(LruMap::new(ByLength::new(2))) + } - let collation = PreparedCollation { - collation, - relay_parent, - para_id: config.para_id, - validation_data, - validation_code_hash, - n_validators, - core_index, - }; + async fn get>( + &mut self, + relay_parent: Hash, + session_index: SessionIndex, + sender: &mut Sender, + ) -> Result { + if let Some(info) = self.0.get(&session_index) { + return Ok(info.clone()) + } - construct_and_distribute_receipt( - collation, - config.key.clone(), - ctx.sender(), - result_sender, - metrics, - ) - .await; + let n_validators = + request_validators(relay_parent, &mut sender.clone()).await.await??.len(); - Ok(()) + let node_features = request_node_features(relay_parent, session_index, sender) + .await? + .unwrap_or(NodeFeatures::EMPTY); + + let info = PerSessionInfo { + v2_receipts: node_features + .get(FeatureIndex::CandidateReceiptV2 as usize) + .map(|b| *b) + .unwrap_or(false), + n_validators, + }; + self.0.insert(session_index, info); + Ok(self.0.get(&session_index).expect("Just inserted").clone()) + } } struct PreparedCollation { @@ -514,6 +466,7 @@ struct PreparedCollation { validation_code_hash: ValidationCodeHash, n_validators: usize, core_index: CoreIndex, + session_index: SessionIndex, } /// Takes a prepared collation, along with its context, and produces a candidate receipt @@ -524,7 +477,9 @@ async fn construct_and_distribute_receipt( sender: &mut impl overseer::CollationGenerationSenderTrait, result_sender: Option>, metrics: &Metrics, -) { + v2_receipts: bool, + transposed_claim_queue: &TransposedClaimQueue, +) -> Result<()> { let PreparedCollation { collation, para_id, @@ -533,6 +488,7 @@ async fn construct_and_distribute_receipt( validation_code_hash, n_validators, core_index, + session_index, } = collation; let persisted_validation_data_hash = validation_data.hash(); @@ -550,15 +506,7 @@ async fn construct_and_distribute_receipt( // As such, honest collators never produce an uncompressed PoV which starts with // a compression magic number, which would lead validators to reject the collation. if encoded_size > validation_data.max_pov_size as usize { - gum::debug!( - target: LOG_TARGET, - para_id = %para_id, - size = encoded_size, - max_size = validation_data.max_pov_size, - "PoV exceeded maximum size" - ); - - return + return Err(Error::POVSizeExceeded(encoded_size, validation_data.max_pov_size as usize)) } pov @@ -574,18 +522,7 @@ async fn construct_and_distribute_receipt( &validation_code_hash, ); - let erasure_root = match erasure_root(n_validators, validation_data, pov.clone()) { - Ok(erasure_root) => erasure_root, - Err(err) => { - gum::error!( - target: LOG_TARGET, - para_id = %para_id, - err = ?err, - "failed to calculate erasure root", - ); - return - }, - }; + let erasure_root = erasure_root(n_validators, validation_data, pov.clone())?; let commitments = CandidateCommitments { upward_messages: collation.upward_messages, @@ -596,35 +533,67 @@ async fn construct_and_distribute_receipt( hrmp_watermark: collation.hrmp_watermark, }; - let ccr = CandidateReceipt { - commitments_hash: commitments.hash(), - descriptor: CandidateDescriptor { - signature: key.sign(&signature_payload), - para_id, - relay_parent, - collator: key.public(), - persisted_validation_data_hash, - pov_hash, - erasure_root, - para_head: commitments.head_data.hash(), - validation_code_hash, + let receipt = if v2_receipts { + let ccr = CommittedCandidateReceiptV2 { + descriptor: CandidateDescriptorV2::new( + para_id, + relay_parent, + core_index, + session_index, + persisted_validation_data_hash, + pov_hash, + erasure_root, + commitments.head_data.hash(), + validation_code_hash, + ), + commitments, + }; + + ccr.check_core_index(&transposed_claim_queue) + .map_err(Error::CandidateReceiptCheck)?; + + ccr.to_plain() + } else { + if commitments.core_selector().map_err(Error::CandidateReceiptCheck)?.is_some() { + gum::warn!( + target: LOG_TARGET, + ?pov_hash, + ?relay_parent, + para_id = %para_id, + "Candidate commitments contain UMP signal without v2 receipts being enabled.", + ); + } + CandidateReceipt { + commitments_hash: commitments.hash(), + descriptor: CandidateDescriptor { + signature: key.sign(&signature_payload), + para_id, + relay_parent, + collator: key.public(), + persisted_validation_data_hash, + pov_hash, + erasure_root, + para_head: commitments.head_data.hash(), + validation_code_hash, + } + .into(), } - .into(), }; gum::debug!( target: LOG_TARGET, - candidate_hash = ?ccr.hash(), + candidate_hash = ?receipt.hash(), ?pov_hash, ?relay_parent, para_id = %para_id, + ?core_index, "candidate is generated", ); metrics.on_collation_generated(); sender .send_message(CollatorProtocolMessage::DistributeCollation { - candidate_receipt: ccr, + candidate_receipt: receipt, parent_head_data_hash, pov, parent_head_data, @@ -632,40 +601,15 @@ async fn construct_and_distribute_receipt( core_index, }) .await; -} -async fn obtain_validation_code_hash_with_assumption( - relay_parent: Hash, - para_id: ParaId, - assumption: OccupiedCoreAssumption, - sender: &mut impl overseer::CollationGenerationSenderTrait, -) -> crate::error::Result> { - match request_validation_code_hash(relay_parent, para_id, assumption, sender) - .await - .await? - { - Ok(Some(v)) => Ok(Some(v)), - Ok(None) => Ok(None), - Err(RuntimeApiError::NotSupported { .. }) => { - match request_validation_code(relay_parent, para_id, assumption, sender).await.await? { - Ok(Some(v)) => Ok(Some(v.hash())), - Ok(None) => Ok(None), - Err(e) => { - // We assume that the `validation_code` API is always available, so any error - // is unexpected. - Err(e.into()) - }, - } - }, - Err(e @ RuntimeApiError::Execution { .. }) => Err(e.into()), - } + Ok(()) } fn erasure_root( n_validators: usize, persisted_validation: PersistedValidationData, pov: PoV, -) -> crate::error::Result { +) -> Result { let available_data = AvailableData { validation_data: persisted_validation, pov: Arc::new(pov) }; diff --git a/polkadot/node/collation-generation/src/metrics.rs b/polkadot/node/collation-generation/src/metrics.rs index c7690ec82c4fb686bfc6d6c8a661adbda2341d9e..80566dcd6fa12ec58d09cc085e40a021378fa611 100644 --- a/polkadot/node/collation-generation/src/metrics.rs +++ b/polkadot/node/collation-generation/src/metrics.rs @@ -19,9 +19,7 @@ use polkadot_node_subsystem_util::metrics::{self, prometheus}; #[derive(Clone)] pub(crate) struct MetricsInner { pub(crate) collations_generated_total: prometheus::Counter, - pub(crate) new_activations_overall: prometheus::Histogram, - pub(crate) new_activations_per_relay_parent: prometheus::Histogram, - pub(crate) new_activations_per_availability_core: prometheus::Histogram, + pub(crate) new_activation: prometheus::Histogram, pub(crate) submit_collation: prometheus::Histogram, } @@ -37,26 +35,8 @@ impl Metrics { } /// Provide a timer for new activations which updates on drop. - pub fn time_new_activations(&self) -> Option { - self.0.as_ref().map(|metrics| metrics.new_activations_overall.start_timer()) - } - - /// Provide a timer per relay parents which updates on drop. - pub fn time_new_activations_relay_parent( - &self, - ) -> Option { - self.0 - .as_ref() - .map(|metrics| metrics.new_activations_per_relay_parent.start_timer()) - } - - /// Provide a timer per availability core which updates on drop. - pub fn time_new_activations_availability_core( - &self, - ) -> Option { - self.0 - .as_ref() - .map(|metrics| metrics.new_activations_per_availability_core.start_timer()) + pub fn time_new_activation(&self) -> Option { + self.0.as_ref().map(|metrics| metrics.new_activation.start_timer()) } /// Provide a timer for submitting a collation which updates on drop. @@ -71,44 +51,22 @@ impl metrics::Metrics for Metrics { collations_generated_total: prometheus::register( prometheus::Counter::new( "polkadot_parachain_collations_generated_total", - "Number of collations generated." - )?, - registry, - )?, - new_activations_overall: prometheus::register( - prometheus::Histogram::with_opts( - prometheus::HistogramOpts::new( - "polkadot_parachain_collation_generation_new_activations", - "Time spent within fn handle_new_activations", - ) - )?, - registry, - )?, - new_activations_per_relay_parent: prometheus::register( - prometheus::Histogram::with_opts( - prometheus::HistogramOpts::new( - "polkadot_parachain_collation_generation_per_relay_parent", - "Time spent handling a particular relay parent within fn handle_new_activations" - ) + "Number of collations generated.", )?, registry, )?, - new_activations_per_availability_core: prometheus::register( - prometheus::Histogram::with_opts( - prometheus::HistogramOpts::new( - "polkadot_parachain_collation_generation_per_availability_core", - "Time spent handling a particular availability core for a relay parent in fn handle_new_activations", - ) - )?, + new_activation: prometheus::register( + prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( + "polkadot_parachain_collation_generation_new_activations", + "Time spent within fn handle_new_activation", + ))?, registry, )?, submit_collation: prometheus::register( - prometheus::Histogram::with_opts( - prometheus::HistogramOpts::new( - "polkadot_parachain_collation_generation_submit_collation", - "Time spent preparing and submitting a collation to the network protocol", - ) - )?, + prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( + "polkadot_parachain_collation_generation_submit_collation", + "Time spent preparing and submitting a collation to the network protocol", + ))?, registry, )?, }; diff --git a/polkadot/node/collation-generation/src/tests.rs b/polkadot/node/collation-generation/src/tests.rs index 78b35fde0ea27ed46d5912c72d404b3bf97dfcab..f81c14cdf8f95289f36f13453877dd9b10853d92 100644 --- a/polkadot/node/collation-generation/src/tests.rs +++ b/polkadot/node/collation-generation/src/tests.rs @@ -17,26 +17,20 @@ use super::*; use assert_matches::assert_matches; use futures::{ - lock::Mutex, task::{Context as FuturesContext, Poll}, - Future, + Future, StreamExt, }; use polkadot_node_primitives::{BlockData, Collation, CollationResult, MaybeCompressedPoV, PoV}; use polkadot_node_subsystem::{ - errors::RuntimeApiError, messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest}, ActivatedLeaf, }; -use polkadot_node_subsystem_test_helpers::{subsystem_test_harness, TestSubsystemContextHandle}; +use polkadot_node_subsystem_test_helpers::TestSubsystemContextHandle; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{ - vstaging::async_backing::{BackingState, CandidatePendingAvailability}, - AsyncBackingParams, BlockNumber, CollatorPair, HeadData, PersistedValidationData, - ScheduledCore, ValidationCode, -}; -use polkadot_primitives_test_helpers::{ - dummy_candidate_descriptor_v2, dummy_hash, dummy_head_data, dummy_validator, make_candidate, + node_features, vstaging::CandidateDescriptorVersion, CollatorPair, PersistedValidationData, }; +use polkadot_primitives_test_helpers::dummy_head_data; use rstest::rstest; use sp_keyring::sr25519::Keyring as Sr25519Keyring; use std::{ @@ -63,7 +57,7 @@ fn test_harness>(test: impl FnOnce(VirtualOv async move { let mut virtual_overseer = test_fut.await; // Ensure we have handled all responses. - if let Ok(Some(msg)) = virtual_overseer.rx.try_next() { + if let Some(msg) = virtual_overseer.rx.next().timeout(TIMEOUT).await { panic!("Did not handle all responses: {:?}", msg); } // Conclude. @@ -85,20 +79,6 @@ fn test_collation() -> Collation { } } -fn test_collation_compressed() -> Collation { - let mut collation = test_collation(); - let compressed = collation.proof_of_validity.clone().into_compressed(); - collation.proof_of_validity = MaybeCompressedPoV::Compressed(compressed); - collation -} - -fn test_validation_data() -> PersistedValidationData { - let mut persisted_validation_data = PersistedValidationData::default(); - persisted_validation_data.max_pov_size = 1024; - persisted_validation_data -} - -// Box + Unpin + Send struct TestCollator; impl Future for TestCollator { @@ -137,531 +117,11 @@ fn test_config_no_collator>(para_id: Id) -> CollationGeneration } } -fn scheduled_core_for>(para_id: Id) -> ScheduledCore { - ScheduledCore { para_id: para_id.into(), collator: None } -} - -fn dummy_candidate_pending_availability( - para_id: ParaId, - candidate_relay_parent: Hash, - relay_parent_number: BlockNumber, -) -> CandidatePendingAvailability { - let (candidate, _pvd) = make_candidate( - candidate_relay_parent, - relay_parent_number, - para_id, - dummy_head_data(), - HeadData(vec![1]), - ValidationCode(vec![1, 2, 3]).hash(), - ); - let candidate_hash = candidate.hash(); - - CandidatePendingAvailability { - candidate_hash, - descriptor: candidate.descriptor, - commitments: candidate.commitments, - relay_parent_number, - max_pov_size: 5 * 1024 * 1024, - } -} - -fn dummy_backing_state(pending_availability: Vec) -> BackingState { - let constraints = helpers::dummy_constraints( - 0, - vec![0], - dummy_head_data(), - ValidationCodeHash::from(Hash::repeat_byte(42)), - ); - - BackingState { constraints, pending_availability } -} - -#[rstest] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] -fn requests_availability_per_relay_parent(#[case] runtime_version: u32) { - let activated_hashes: Vec = - vec![[1; 32].into(), [4; 32].into(), [9; 32].into(), [16; 32].into()]; - - let requested_availability_cores = Arc::new(Mutex::new(Vec::new())); - - let overseer_requested_availability_cores = requested_availability_cores.clone(); - let overseer = |mut handle: TestSubsystemContextHandle| async move { - loop { - match handle.try_recv().await { - None => break, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::AvailabilityCores(tx)))) => { - overseer_requested_availability_cores.lock().await.push(hash); - tx.send(Ok(vec![])).unwrap(); - } - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(_hash, RuntimeApiRequest::Validators(tx)))) => { - tx.send(Ok(vec![dummy_validator(); 3])).unwrap(); - } - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::AsyncBackingParams( - tx, - ), - ))) => { - tx.send(Err(RuntimeApiError::NotSupported { runtime_api_name: "doesnt_matter" })).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::Version(tx), - ))) => { - tx.send(Ok(runtime_version)).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ClaimQueue(tx), - ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { - tx.send(Ok(BTreeMap::new())).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ParaBackingState(_para_id, tx), - ))) => { - tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap(); - }, - Some(msg) => panic!("didn't expect any other overseer requests given no availability cores; got {:?}", msg), - } - } - }; - - let subsystem_activated_hashes = activated_hashes.clone(); - subsystem_test_harness(overseer, |mut ctx| async move { - handle_new_activations( - Arc::new(test_config(123u32)), - subsystem_activated_hashes, - &mut ctx, - Metrics(None), - ) - .await - .unwrap(); - }); - - let mut requested_availability_cores = Arc::try_unwrap(requested_availability_cores) - .expect("overseer should have shut down by now") - .into_inner(); - requested_availability_cores.sort(); - - assert_eq!(requested_availability_cores, activated_hashes); -} - -#[rstest] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] -fn requests_validation_data_for_scheduled_matches(#[case] runtime_version: u32) { - let activated_hashes: Vec = vec![ - Hash::repeat_byte(1), - Hash::repeat_byte(4), - Hash::repeat_byte(9), - Hash::repeat_byte(16), - ]; - - let requested_validation_data = Arc::new(Mutex::new(Vec::new())); - - let overseer_requested_validation_data = requested_validation_data.clone(); - let overseer = |mut handle: TestSubsystemContextHandle| async move { - loop { - match handle.try_recv().await { - None => break, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - hash, - RuntimeApiRequest::AvailabilityCores(tx), - ))) => { - tx.send(Ok(vec![ - CoreState::Free, - // this is weird, see explanation below - CoreState::Scheduled(scheduled_core_for( - (hash.as_fixed_bytes()[0] * 4) as u32, - )), - CoreState::Scheduled(scheduled_core_for( - (hash.as_fixed_bytes()[0] * 5) as u32, - )), - ])) - .unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - hash, - RuntimeApiRequest::PersistedValidationData( - _para_id, - _occupied_core_assumption, - tx, - ), - ))) => { - overseer_requested_validation_data.lock().await.push(hash); - tx.send(Ok(None)).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::Validators(tx), - ))) => { - tx.send(Ok(vec![dummy_validator(); 3])).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::AsyncBackingParams(tx), - ))) => { - tx.send(Err(RuntimeApiError::NotSupported { - runtime_api_name: "doesnt_matter", - })) - .unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::Version(tx), - ))) => { - tx.send(Ok(runtime_version)).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ClaimQueue(tx), - ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { - tx.send(Ok(BTreeMap::new())).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ParaBackingState(_para_id, tx), - ))) => { - tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap(); - }, - Some(msg) => { - panic!("didn't expect any other overseer requests; got {:?}", msg) - }, - } - } - }; - - subsystem_test_harness(overseer, |mut ctx| async move { - handle_new_activations( - Arc::new(test_config(16)), - activated_hashes, - &mut ctx, - Metrics(None), - ) - .await - .unwrap(); - }); - - let requested_validation_data = Arc::try_unwrap(requested_validation_data) - .expect("overseer should have shut down by now") - .into_inner(); - - // the only activated hash should be from the 4 hash: - // each activated hash generates two scheduled cores: one with its value * 4, one with its value - // * 5 given that the test configuration has a `para_id` of 16, there's only one way to get that - // value: with the 4 hash. - assert_eq!(requested_validation_data, vec![[4; 32].into()]); -} - -#[rstest] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] -fn sends_distribute_collation_message(#[case] runtime_version: u32) { - let activated_hashes: Vec = vec![ - Hash::repeat_byte(1), - Hash::repeat_byte(4), - Hash::repeat_byte(9), - Hash::repeat_byte(16), - ]; - - // empty vec doesn't allocate on the heap, so it's ok we throw it away - let to_collator_protocol = Arc::new(Mutex::new(Vec::new())); - let inner_to_collator_protocol = to_collator_protocol.clone(); - - let overseer = |mut handle: TestSubsystemContextHandle| async move { - loop { - match handle.try_recv().await { - None => break, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - hash, - RuntimeApiRequest::AvailabilityCores(tx), - ))) => { - tx.send(Ok(vec![ - CoreState::Free, - // this is weird, see explanation below - CoreState::Scheduled(scheduled_core_for( - (hash.as_fixed_bytes()[0] * 4) as u32, - )), - CoreState::Scheduled(scheduled_core_for( - (hash.as_fixed_bytes()[0] * 5) as u32, - )), - ])) - .unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::PersistedValidationData( - _para_id, - _occupied_core_assumption, - tx, - ), - ))) => { - tx.send(Ok(Some(test_validation_data()))).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::Validators(tx), - ))) => { - tx.send(Ok(vec![dummy_validator(); 3])).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ValidationCodeHash( - _para_id, - OccupiedCoreAssumption::Free, - tx, - ), - ))) => { - tx.send(Ok(Some(ValidationCode(vec![1, 2, 3]).hash()))).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::AsyncBackingParams(tx), - ))) => { - tx.send(Err(RuntimeApiError::NotSupported { - runtime_api_name: "doesnt_matter", - })) - .unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::Version(tx), - ))) => { - tx.send(Ok(runtime_version)).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ClaimQueue(tx), - ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { - tx.send(Ok(BTreeMap::new())).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ParaBackingState(_para_id, tx), - ))) => { - tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap(); - }, - Some(msg @ AllMessages::CollatorProtocol(_)) => { - inner_to_collator_protocol.lock().await.push(msg); - }, - Some(msg) => { - panic!("didn't expect any other overseer requests; got {:?}", msg) - }, - } - } - }; - - let config = Arc::new(test_config(16)); - let subsystem_config = config.clone(); - - subsystem_test_harness(overseer, |mut ctx| async move { - handle_new_activations(subsystem_config, activated_hashes, &mut ctx, Metrics(None)) - .await - .unwrap(); - }); - - let mut to_collator_protocol = Arc::try_unwrap(to_collator_protocol) - .expect("subsystem should have shut down by now") - .into_inner(); - - // we expect a single message to be sent, containing a candidate receipt. - // we don't care too much about the `commitments_hash` right now, but let's ensure that we've - // calculated the correct descriptor - let expect_pov_hash = test_collation_compressed().proof_of_validity.into_compressed().hash(); - let expect_validation_data_hash = test_validation_data().hash(); - let expect_relay_parent = Hash::repeat_byte(4); - let expect_validation_code_hash = ValidationCode(vec![1, 2, 3]).hash(); - let expect_payload = collator_signature_payload( - &expect_relay_parent, - &config.para_id, - &expect_validation_data_hash, - &expect_pov_hash, - &expect_validation_code_hash, - ); - let expect_descriptor = CandidateDescriptor { - signature: config.key.sign(&expect_payload), - para_id: config.para_id, - relay_parent: expect_relay_parent, - collator: config.key.public(), - persisted_validation_data_hash: expect_validation_data_hash, - pov_hash: expect_pov_hash, - erasure_root: dummy_hash(), // this isn't something we're checking right now - para_head: test_collation().head_data.hash(), - validation_code_hash: expect_validation_code_hash, - }; - - assert_eq!(to_collator_protocol.len(), 1); - match AllMessages::from(to_collator_protocol.pop().unwrap()) { - AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation { - candidate_receipt, - .. - }) => { - let CandidateReceipt { descriptor, .. } = candidate_receipt; - // signature generation is non-deterministic, so we can't just assert that the - // expected descriptor is correct. What we can do is validate that the produced - // descriptor has a valid signature, then just copy in the generated signature - // and check the rest of the fields for equality. - assert!(CollatorPair::verify( - &descriptor.signature().unwrap(), - &collator_signature_payload( - &descriptor.relay_parent(), - &descriptor.para_id(), - &descriptor.persisted_validation_data_hash(), - &descriptor.pov_hash(), - &descriptor.validation_code_hash(), - ) - .as_ref(), - &descriptor.collator().unwrap(), - )); - let expect_descriptor = { - let mut expect_descriptor = expect_descriptor; - expect_descriptor.signature = descriptor.signature().clone().unwrap(); - expect_descriptor.erasure_root = descriptor.erasure_root(); - expect_descriptor.into() - }; - assert_eq!(descriptor, expect_descriptor); - }, - _ => panic!("received wrong message type"), - } -} - -#[rstest] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] -fn fallback_when_no_validation_code_hash_api(#[case] runtime_version: u32) { - // This is a variant of the above test, but with the validation code hash API disabled. - - let activated_hashes: Vec = vec![ - Hash::repeat_byte(1), - Hash::repeat_byte(4), - Hash::repeat_byte(9), - Hash::repeat_byte(16), - ]; - - // empty vec doesn't allocate on the heap, so it's ok we throw it away - let to_collator_protocol = Arc::new(Mutex::new(Vec::new())); - let inner_to_collator_protocol = to_collator_protocol.clone(); - - let overseer = |mut handle: TestSubsystemContextHandle| async move { - loop { - match handle.try_recv().await { - None => break, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - hash, - RuntimeApiRequest::AvailabilityCores(tx), - ))) => { - tx.send(Ok(vec![ - CoreState::Free, - CoreState::Scheduled(scheduled_core_for( - (hash.as_fixed_bytes()[0] * 4) as u32, - )), - CoreState::Scheduled(scheduled_core_for( - (hash.as_fixed_bytes()[0] * 5) as u32, - )), - ])) - .unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::PersistedValidationData( - _para_id, - _occupied_core_assumption, - tx, - ), - ))) => { - tx.send(Ok(Some(test_validation_data()))).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::Validators(tx), - ))) => { - tx.send(Ok(vec![dummy_validator(); 3])).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ValidationCodeHash( - _para_id, - OccupiedCoreAssumption::Free, - tx, - ), - ))) => { - tx.send(Err(RuntimeApiError::NotSupported { - runtime_api_name: "validation_code_hash", - })) - .unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ValidationCode(_para_id, OccupiedCoreAssumption::Free, tx), - ))) => { - tx.send(Ok(Some(ValidationCode(vec![1, 2, 3])))).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::AsyncBackingParams(tx), - ))) => { - tx.send(Err(RuntimeApiError::NotSupported { - runtime_api_name: "doesnt_matter", - })) - .unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::Version(tx), - ))) => { - tx.send(Ok(runtime_version)).unwrap(); - }, - Some(msg @ AllMessages::CollatorProtocol(_)) => { - inner_to_collator_protocol.lock().await.push(msg); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ClaimQueue(tx), - ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { - tx.send(Ok(Default::default())).unwrap(); - }, - Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _hash, - RuntimeApiRequest::ParaBackingState(_para_id, tx), - ))) => { - tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap(); - }, - Some(msg) => { - panic!("didn't expect any other overseer requests; got {:?}", msg) - }, - } - } - }; - - let config = Arc::new(test_config(16u32)); - let subsystem_config = config.clone(); - - // empty vec doesn't allocate on the heap, so it's ok we throw it away - subsystem_test_harness(overseer, |mut ctx| async move { - handle_new_activations(subsystem_config, activated_hashes, &mut ctx, Metrics(None)) - .await - .unwrap(); - }); - - let to_collator_protocol = Arc::try_unwrap(to_collator_protocol) - .expect("subsystem should have shut down by now") - .into_inner(); - - let expect_validation_code_hash = ValidationCode(vec![1, 2, 3]).hash(); - - assert_eq!(to_collator_protocol.len(), 1); - match &to_collator_protocol[0] { - AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation { - candidate_receipt, - .. - }) => { - let CandidateReceipt { descriptor, .. } = candidate_receipt; - assert_eq!(expect_validation_code_hash, descriptor.validation_code_hash()); - }, - _ => panic!("received wrong message type"), - } +fn node_features_with_v2_enabled() -> NodeFeatures { + let mut node_features = NodeFeatures::new(); + node_features.resize(node_features::FeatureIndex::CandidateReceiptV2 as usize + 1, false); + node_features.set(node_features::FeatureIndex::CandidateReceiptV2 as u8 as usize, true); + node_features } #[test] @@ -717,31 +177,15 @@ fn submit_collation_leads_to_distribution() { }) .await; - assert_matches!( - overseer_recv(&mut virtual_overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request(rp, RuntimeApiRequest::Validators(tx))) => { - assert_eq!(rp, relay_parent); - let _ = tx.send(Ok(vec![ - Sr25519Keyring::Alice.public().into(), - Sr25519Keyring::Bob.public().into(), - Sr25519Keyring::Charlie.public().into(), - ])); - } - ); - - assert_matches!( - overseer_recv(&mut virtual_overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request(rp, RuntimeApiRequest::PersistedValidationData(id, a, tx))) => { - assert_eq!(rp, relay_parent); - assert_eq!(id, para_id); - assert_eq!(a, OccupiedCoreAssumption::TimedOut); - - // Candidate receipt should be constructed with the real parent head. - let mut pvd = expected_pvd.clone(); - pvd.parent_head = vec![4, 5, 6].into(); - let _ = tx.send(Ok(Some(pvd))); - } - ); + helpers::handle_runtime_calls_on_submit_collation( + &mut virtual_overseer, + relay_parent, + para_id, + expected_pvd.clone(), + NodeFeatures::EMPTY, + Default::default(), + ) + .await; assert_matches!( overseer_recv(&mut virtual_overseer).await, @@ -762,78 +206,16 @@ fn submit_collation_leads_to_distribution() { }); } -// There is one core in `Occupied` state and async backing is enabled. On new head activation -// `CollationGeneration` should produce and distribute a new collation. -#[rstest] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] -fn distribute_collation_for_occupied_core_with_async_backing_enabled(#[case] runtime_version: u32) { - let activated_hash: Hash = [1; 32].into(); - let para_id = ParaId::from(5); - - // One core, in occupied state. The data in `CoreState` and `ClaimQueue` should match. - let cores: Vec = - vec![CoreState::Occupied(polkadot_primitives::vstaging::OccupiedCore { - next_up_on_available: Some(ScheduledCore { para_id, collator: None }), - occupied_since: 1, - time_out_at: 10, - next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), - availability: Default::default(), // doesn't matter - group_responsible: polkadot_primitives::GroupIndex(0), - candidate_hash: Default::default(), - candidate_descriptor: dummy_candidate_descriptor_v2(dummy_hash()), - })]; - let claim_queue = BTreeMap::from([(CoreIndex::from(0), VecDeque::from([para_id]))]).into(); - - test_harness(|mut virtual_overseer| async move { - helpers::initialize_collator(&mut virtual_overseer, para_id).await; - helpers::activate_new_head(&mut virtual_overseer, activated_hash).await; - - let pending_availability = - vec![dummy_candidate_pending_availability(para_id, activated_hash, 1)]; - helpers::handle_runtime_calls_on_new_head_activation( - &mut virtual_overseer, - activated_hash, - AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 1 }, - cores, - runtime_version, - claim_queue, - ) - .await; - helpers::handle_cores_processing_for_a_leaf( - &mut virtual_overseer, - activated_hash, - para_id, - // `CoreState` is `Occupied` => `OccupiedCoreAssumption` is `Included` - OccupiedCoreAssumption::Included, - 1, - pending_availability, - runtime_version, - ) - .await; - - virtual_overseer - }); -} - #[test] -fn distribute_collation_for_occupied_core_pre_async_backing() { +fn distribute_collation_only_for_assigned_para_id_at_offset_0() { let activated_hash: Hash = [1; 32].into(); let para_id = ParaId::from(5); - let total_cores = 3; - - // Use runtime version before async backing - let runtime_version = RuntimeApiRequest::ASYNC_BACKING_STATE_RUNTIME_REQUIREMENT - 1; - let cores = (0..total_cores) + let claim_queue = (0..=5) .into_iter() - .map(|_idx| CoreState::Scheduled(ScheduledCore { para_id, collator: None })) - .collect::>(); - - let claim_queue = cores - .iter() - .enumerate() - .map(|(idx, _core)| (CoreIndex::from(idx as u32), VecDeque::from([para_id]))) + // Set all cores assigned to para_id 5 at the second and third depths. This shouldn't + // matter. + .map(|idx| (CoreIndex(idx), VecDeque::from([ParaId::from(idx), para_id, para_id]))) .collect::>(); test_harness(|mut virtual_overseer| async move { @@ -842,10 +224,8 @@ fn distribute_collation_for_occupied_core_pre_async_backing() { helpers::handle_runtime_calls_on_new_head_activation( &mut virtual_overseer, activated_hash, - AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 1 }, - cores, - runtime_version, claim_queue, + NodeFeatures::EMPTY, ) .await; @@ -853,11 +233,7 @@ fn distribute_collation_for_occupied_core_pre_async_backing() { &mut virtual_overseer, activated_hash, para_id, - // `CoreState` is `Free` => `OccupiedCoreAssumption` is `Free` - OccupiedCoreAssumption::Free, - total_cores, - vec![], - runtime_version, + vec![5], // Only core 5 is assigned to paraid 5. ) .await; @@ -865,48 +241,22 @@ fn distribute_collation_for_occupied_core_pre_async_backing() { }); } -// There are variable number of cores of cores in `Occupied` state and async backing is enabled. -// On new head activation `CollationGeneration` should produce and distribute a new collation -// with proper assumption about the para candidate chain availability at next block. +// There are variable number of cores assigned to the paraid. +// On new head activation `CollationGeneration` should produce and distribute the right number of +// new collations with proper assumption about the para candidate chain availability at next block. #[rstest] #[case(0)] #[case(1)] #[case(2)] -fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elastic_scaling( - #[case] candidates_pending_avail: u32, -) { +#[case(3)] +fn distribute_collation_with_elastic_scaling(#[case] total_cores: u32) { let activated_hash: Hash = [1; 32].into(); let para_id = ParaId::from(5); - // Using latest runtime with the fancy claim queue exposed. - let runtime_version = RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT; - let cores = (0..3) + let claim_queue = (0..total_cores) .into_iter() - .map(|idx| { - CoreState::Occupied(polkadot_primitives::vstaging::OccupiedCore { - next_up_on_available: Some(ScheduledCore { para_id, collator: None }), - occupied_since: 0, - time_out_at: 10, - next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), - availability: Default::default(), // doesn't matter - group_responsible: polkadot_primitives::GroupIndex(idx as u32), - candidate_hash: Default::default(), - candidate_descriptor: dummy_candidate_descriptor_v2(dummy_hash()), - }) - }) - .collect::>(); - - let pending_availability = (0..candidates_pending_avail) - .into_iter() - .map(|_idx| dummy_candidate_pending_availability(para_id, activated_hash, 0)) - .collect::>(); - - let claim_queue = cores - .iter() - .enumerate() - .map(|(idx, _core)| (CoreIndex::from(idx as u32), VecDeque::from([para_id]))) + .map(|idx| (CoreIndex(idx), VecDeque::from([para_id]))) .collect::>(); - let total_cores = cores.len(); test_harness(|mut virtual_overseer| async move { helpers::initialize_collator(&mut virtual_overseer, para_id).await; @@ -914,10 +264,8 @@ fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elasti helpers::handle_runtime_calls_on_new_head_activation( &mut virtual_overseer, activated_hash, - AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 1 }, - cores, - runtime_version, claim_queue, + NodeFeatures::EMPTY, ) .await; @@ -925,16 +273,7 @@ fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elasti &mut virtual_overseer, activated_hash, para_id, - // if at least 1 cores is occupied => `OccupiedCoreAssumption` is `Included` - // else assumption is `Free`. - if candidates_pending_avail > 0 { - OccupiedCoreAssumption::Included - } else { - OccupiedCoreAssumption::Free - }, - total_cores, - pending_availability, - runtime_version, + (0..total_cores).collect(), ) .await; @@ -942,136 +281,128 @@ fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elasti }); } -// There are variable number of cores of cores in `Free` state and async backing is enabled. -// On new head activation `CollationGeneration` should produce and distribute a new collation -// with proper assumption about the para candidate chain availability at next block. #[rstest] -#[case(0)] -#[case(1)] -#[case(2)] -fn distribute_collation_for_free_cores_with_async_backing_enabled_and_elastic_scaling( - #[case] total_cores: usize, -) { - let activated_hash: Hash = [1; 32].into(); +#[case(true)] +#[case(false)] +fn test_candidate_receipt_versioning(#[case] v2_receipts: bool) { + let relay_parent = Hash::repeat_byte(0); + let validation_code_hash = ValidationCodeHash::from(Hash::repeat_byte(42)); + let parent_head = dummy_head_data(); let para_id = ParaId::from(5); - // Using latest runtime with the fancy claim queue exposed. - let runtime_version = RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT; - - let cores = (0..total_cores) - .into_iter() - .map(|_idx| CoreState::Scheduled(ScheduledCore { para_id, collator: None })) - .collect::>(); - - let claim_queue = cores - .iter() - .enumerate() - .map(|(idx, _core)| (CoreIndex::from(idx as u32), VecDeque::from([para_id]))) - .collect::>(); + let expected_pvd = PersistedValidationData { + parent_head: parent_head.clone(), + relay_parent_number: 10, + relay_parent_storage_root: Hash::repeat_byte(1), + max_pov_size: 1024, + }; + let node_features = + if v2_receipts { node_features_with_v2_enabled() } else { NodeFeatures::EMPTY }; + let expected_descriptor_version = + if v2_receipts { CandidateDescriptorVersion::V2 } else { CandidateDescriptorVersion::V1 }; test_harness(|mut virtual_overseer| async move { - helpers::initialize_collator(&mut virtual_overseer, para_id).await; - helpers::activate_new_head(&mut virtual_overseer, activated_hash).await; - helpers::handle_runtime_calls_on_new_head_activation( - &mut virtual_overseer, - activated_hash, - AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 1 }, - cores, - runtime_version, - claim_queue, - ) - .await; + virtual_overseer + .send(FromOrchestra::Communication { + msg: CollationGenerationMessage::Initialize(test_config_no_collator(para_id)), + }) + .await; - helpers::handle_cores_processing_for_a_leaf( + virtual_overseer + .send(FromOrchestra::Communication { + msg: CollationGenerationMessage::SubmitCollation(SubmitCollationParams { + relay_parent, + collation: test_collation(), + parent_head: dummy_head_data(), + validation_code_hash, + result_sender: None, + core_index: CoreIndex(0), + }), + }) + .await; + + helpers::handle_runtime_calls_on_submit_collation( &mut virtual_overseer, - activated_hash, + relay_parent, para_id, - // `CoreState` is `Free` => `OccupiedCoreAssumption` is `Free` - OccupiedCoreAssumption::Free, - total_cores, - vec![], - runtime_version, + expected_pvd.clone(), + node_features, + [(CoreIndex(0), [para_id].into_iter().collect())].into_iter().collect(), ) .await; + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation { + candidate_receipt, + parent_head_data_hash, + .. + }) => { + let CandidateReceipt { descriptor, .. } = candidate_receipt; + assert_eq!(parent_head_data_hash, parent_head.hash()); + assert_eq!(descriptor.persisted_validation_data_hash(), expected_pvd.hash()); + assert_eq!(descriptor.para_head(), dummy_head_data().hash()); + assert_eq!(descriptor.validation_code_hash(), validation_code_hash); + // Check that the right version was indeed used. + assert_eq!(descriptor.version(), expected_descriptor_version); + } + ); + virtual_overseer }); } -// There is one core in `Occupied` state and async backing is disabled. On new head activation -// no new collation should be generated. -#[rstest] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] -#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] -fn no_collation_is_distributed_for_occupied_core_with_async_backing_disabled( - #[case] runtime_version: u32, -) { - let activated_hash: Hash = [1; 32].into(); +#[test] +fn v2_receipts_failed_core_index_check() { + let relay_parent = Hash::repeat_byte(0); + let validation_code_hash = ValidationCodeHash::from(Hash::repeat_byte(42)); + let parent_head = dummy_head_data(); let para_id = ParaId::from(5); - - // One core, in occupied state. The data in `CoreState` and `ClaimQueue` should match. - let cores: Vec = - vec![CoreState::Occupied(polkadot_primitives::vstaging::OccupiedCore { - next_up_on_available: Some(ScheduledCore { para_id, collator: None }), - occupied_since: 1, - time_out_at: 10, - next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }), - availability: Default::default(), // doesn't matter - group_responsible: polkadot_primitives::GroupIndex(0), - candidate_hash: Default::default(), - candidate_descriptor: dummy_candidate_descriptor_v2(dummy_hash()), - })]; - let claim_queue = BTreeMap::from([(CoreIndex::from(0), VecDeque::from([para_id]))]).into(); + let expected_pvd = PersistedValidationData { + parent_head: parent_head.clone(), + relay_parent_number: 10, + relay_parent_storage_root: Hash::repeat_byte(1), + max_pov_size: 1024, + }; test_harness(|mut virtual_overseer| async move { - helpers::initialize_collator(&mut virtual_overseer, para_id).await; - helpers::activate_new_head(&mut virtual_overseer, activated_hash).await; + virtual_overseer + .send(FromOrchestra::Communication { + msg: CollationGenerationMessage::Initialize(test_config_no_collator(para_id)), + }) + .await; - helpers::handle_runtime_calls_on_new_head_activation( + virtual_overseer + .send(FromOrchestra::Communication { + msg: CollationGenerationMessage::SubmitCollation(SubmitCollationParams { + relay_parent, + collation: test_collation(), + parent_head: dummy_head_data(), + validation_code_hash, + result_sender: None, + core_index: CoreIndex(0), + }), + }) + .await; + + helpers::handle_runtime_calls_on_submit_collation( &mut virtual_overseer, - activated_hash, - AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: 0 }, - cores, - runtime_version, - claim_queue, + relay_parent, + para_id, + expected_pvd.clone(), + node_features_with_v2_enabled(), + // Core index commitment is on core 0 but don't add any assignment for core 0. + [(CoreIndex(1), [para_id].into_iter().collect())].into_iter().collect(), ) .await; + // No collation is distributed. + virtual_overseer }); } - mod helpers { - use polkadot_primitives::{ - async_backing::{Constraints, InboundHrmpLimitations}, - BlockNumber, - }; - use super::*; - - // A set for dummy constraints for `ParaBackingState`` - pub(crate) fn dummy_constraints( - min_relay_parent_number: BlockNumber, - valid_watermarks: Vec, - required_parent: HeadData, - validation_code_hash: ValidationCodeHash, - ) -> Constraints { - Constraints { - min_relay_parent_number, - max_pov_size: 5 * 1024 * 1024, - max_code_size: 1_000_000, - ump_remaining: 10, - ump_remaining_bytes: 1_000, - max_ump_num_per_candidate: 10, - dmp_remaining_messages: vec![], - hrmp_inbound: InboundHrmpLimitations { valid_watermarks }, - hrmp_channels_out: vec![], - max_hrmp_num_per_candidate: 0, - required_parent, - validation_code_hash, - upgrade_restriction: None, - future_validation_code: None, - } - } + use std::collections::{BTreeMap, VecDeque}; // Sends `Initialize` with a collator config pub async fn initialize_collator(virtual_overseer: &mut VirtualOverseer, para_id: ParaId) { @@ -1098,22 +429,18 @@ mod helpers { .await; } - // Handle all runtime calls performed in `handle_new_activations`. Conditionally expects a - // `CLAIM_QUEUE_RUNTIME_REQUIREMENT` call if the passed `runtime_version` is greater or equal to - // `CLAIM_QUEUE_RUNTIME_REQUIREMENT` + // Handle all runtime calls performed in `handle_new_activation`. pub async fn handle_runtime_calls_on_new_head_activation( virtual_overseer: &mut VirtualOverseer, activated_hash: Hash, - async_backing_params: AsyncBackingParams, - cores: Vec, - runtime_version: u32, claim_queue: BTreeMap>, + node_features: NodeFeatures, ) { assert_matches!( overseer_recv(virtual_overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::AvailabilityCores(tx))) => { + AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::SessionIndexForChild(tx))) => { assert_eq!(hash, activated_hash); - let _ = tx.send(Ok(cores)); + tx.send(Ok(1)).unwrap(); } ); @@ -1121,73 +448,46 @@ mod helpers { overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::Validators(tx))) => { assert_eq!(hash, activated_hash); - let _ = tx.send(Ok(vec![ + tx.send(Ok(vec![ Sr25519Keyring::Alice.public().into(), Sr25519Keyring::Bob.public().into(), Sr25519Keyring::Charlie.public().into(), - ])); + ])).unwrap(); } ); - let async_backing_response = - if runtime_version >= RuntimeApiRequest::ASYNC_BACKING_STATE_RUNTIME_REQUIREMENT { - Ok(async_backing_params) - } else { - Err(RuntimeApiError::NotSupported { runtime_api_name: "async_backing_params" }) - }; - assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( - hash, - RuntimeApiRequest::AsyncBackingParams( - tx, - ), - )) => { + hash, + RuntimeApiRequest::NodeFeatures(session_index, tx), + )) => { + assert_eq!(1, session_index); assert_eq!(hash, activated_hash); - let _ = tx.send(async_backing_response); + + tx.send(Ok(node_features)).unwrap(); } ); assert_matches!( overseer_recv(virtual_overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - hash, - RuntimeApiRequest::Version(tx), - )) => { + AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::ClaimQueue(tx))) => { assert_eq!(hash, activated_hash); - let _ = tx.send(Ok(runtime_version)); + tx.send(Ok(claim_queue)).unwrap(); } ); - - if runtime_version == RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT { - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - hash, - RuntimeApiRequest::ClaimQueue(tx), - )) => { - assert_eq!(hash, activated_hash); - let _ = tx.send(Ok(claim_queue.into())); - } - ); - } } - // Handles all runtime requests performed in `handle_new_activations` for the case when a + // Handles all runtime requests performed in `handle_new_activation` for the case when a // collation should be prepared for the new leaf pub async fn handle_cores_processing_for_a_leaf( virtual_overseer: &mut VirtualOverseer, activated_hash: Hash, para_id: ParaId, - expected_occupied_core_assumption: OccupiedCoreAssumption, - cores_assigned: usize, - pending_availability: Vec, - runtime_version: u32, + cores_assigned: Vec, ) { // Expect no messages if no cores is assigned to the para - if cores_assigned == 0 { - assert!(overseer_recv(virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + if cores_assigned.is_empty() { return } @@ -1201,23 +501,12 @@ mod helpers { max_pov_size: 1024, }; - if runtime_version >= RuntimeApiRequest::ASYNC_BACKING_STATE_RUNTIME_REQUIREMENT { - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(parent, RuntimeApiRequest::ParaBackingState(p_id, tx)) - ) if parent == activated_hash && p_id == para_id => { - tx.send(Ok(Some(dummy_backing_state(pending_availability)))).unwrap(); - } - ); - } - assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, RuntimeApiRequest::PersistedValidationData(id, a, tx))) => { assert_eq!(hash, activated_hash); assert_eq!(id, para_id); - assert_eq!(a, expected_occupied_core_assumption); + assert_eq!(a, OccupiedCoreAssumption::Included); let _ = tx.send(Ok(Some(pvd.clone()))); } @@ -1235,20 +524,22 @@ mod helpers { )) => { assert_eq!(hash, activated_hash); assert_eq!(id, para_id); - assert_eq!(assumption, expected_occupied_core_assumption); + assert_eq!(assumption, OccupiedCoreAssumption::Included); let _ = tx.send(Ok(Some(validation_code_hash))); } ); - for _ in 0..cores_assigned { + for core in cores_assigned { assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation{ candidate_receipt, parent_head_data_hash, + core_index, .. }) => { + assert_eq!(CoreIndex(core), core_index); assert_eq!(parent_head_data_hash, parent_head.hash()); assert_eq!(candidate_receipt.descriptor().persisted_validation_data_hash(), pvd.hash()); assert_eq!(candidate_receipt.descriptor().para_head(), dummy_head_data().hash()); @@ -1257,4 +548,69 @@ mod helpers { ); } } + + // Handles all runtime requests performed in `handle_submit_collation` + pub async fn handle_runtime_calls_on_submit_collation( + virtual_overseer: &mut VirtualOverseer, + relay_parent: Hash, + para_id: ParaId, + expected_pvd: PersistedValidationData, + node_features: NodeFeatures, + claim_queue: BTreeMap>, + ) { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(rp, RuntimeApiRequest::PersistedValidationData(id, a, tx))) => { + assert_eq!(rp, relay_parent); + assert_eq!(id, para_id); + assert_eq!(a, OccupiedCoreAssumption::TimedOut); + + tx.send(Ok(Some(expected_pvd))).unwrap(); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + rp, + RuntimeApiRequest::ClaimQueue(tx), + )) => { + assert_eq!(rp, relay_parent); + tx.send(Ok(claim_queue)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(rp, RuntimeApiRequest::SessionIndexForChild(tx))) => { + assert_eq!(rp, relay_parent); + tx.send(Ok(1)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(rp, RuntimeApiRequest::Validators(tx))) => { + assert_eq!(rp, relay_parent); + tx.send(Ok(vec![ + Sr25519Keyring::Alice.public().into(), + Sr25519Keyring::Bob.public().into(), + Sr25519Keyring::Charlie.public().into(), + ])).unwrap(); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + rp, + RuntimeApiRequest::NodeFeatures(session_index, tx), + )) => { + assert_eq!(1, session_index); + assert_eq!(rp, relay_parent); + + tx.send(Ok(node_features.clone())).unwrap(); + } + ); + } } diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index b5362d32ad88d356bf45588589d6b02f24bd92eb..30121418a2fde448c6ea085a8b4797deb04795ac 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -632,6 +632,7 @@ async fn request_candidate_validation( ) -> Result { let (tx, rx) = oneshot::channel(); let is_system = candidate_receipt.descriptor.para_id().is_system(); + let relay_parent = candidate_receipt.descriptor.relay_parent(); sender .send_message(CandidateValidationMessage::ValidateFromExhaustive { @@ -641,9 +642,9 @@ async fn request_candidate_validation( pov, executor_params, exec_kind: if is_system { - PvfExecKind::BackingSystemParas + PvfExecKind::BackingSystemParas(relay_parent) } else { - PvfExecKind::Backing + PvfExecKind::Backing(relay_parent) }, response_sender: tx, }) diff --git a/polkadot/node/core/backing/src/tests/mod.rs b/polkadot/node/core/backing/src/tests/mod.rs index dbb974a634fe6a330995c9bf3e1697843729e2c8..97e25c04282c4dad91415db3f30e598fdaf9fb97 100644 --- a/polkadot/node/core/backing/src/tests/mod.rs +++ b/polkadot/node/core/backing/src/tests/mod.rs @@ -435,7 +435,7 @@ async fn assert_validate_from_exhaustive( ) if validation_data == *assert_pvd && validation_code == *assert_validation_code && *pov == *assert_pov && candidate_receipt.descriptor == assert_candidate.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate_receipt.commitments_hash == assert_candidate.commitments.hash() => { response_sender.send(Ok(ValidationResult::Valid( @@ -652,7 +652,7 @@ fn backing_works(#[case] elastic_scaling_mvp: bool) { ) if validation_data == pvd_ab && validation_code == validation_code_ab && *pov == pov_ab && candidate_receipt.descriptor == candidate_a.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate_receipt.commitments_hash == candidate_a_commitments_hash => { response_sender.send(Ok( @@ -1288,7 +1288,7 @@ fn backing_works_while_validation_ongoing() { ) if validation_data == pvd_abc && validation_code == validation_code_abc && *pov == pov_abc && candidate_receipt.descriptor == candidate_a.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate_a_commitments_hash == candidate_receipt.commitments_hash => { // we never validate the candidate. our local node @@ -1455,7 +1455,7 @@ fn backing_misbehavior_works() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_a && candidate_receipt.descriptor == candidate_a.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate_a_commitments_hash == candidate_receipt.commitments_hash => { response_sender.send(Ok( @@ -1622,7 +1622,7 @@ fn backing_dont_second_invalid() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_block_a && candidate_receipt.descriptor == candidate_a.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate_a.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap(); @@ -1662,7 +1662,7 @@ fn backing_dont_second_invalid() { ) if validation_data == pvd_b && validation_code == validation_code_b && *pov == pov_block_b && candidate_receipt.descriptor == candidate_b.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate_b.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Ok( @@ -1789,7 +1789,7 @@ fn backing_second_after_first_fails_works() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_a && candidate_receipt.descriptor == candidate.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap(); @@ -1933,7 +1933,7 @@ fn backing_works_after_failed_validation() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_a && candidate_receipt.descriptor == candidate.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Err(ValidationFailed("Internal test error".into()))).unwrap(); @@ -2212,7 +2212,7 @@ fn retry_works() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_a && candidate_receipt.descriptor == candidate.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate.commitments.hash() == candidate_receipt.commitments_hash ); virtual_overseer @@ -2754,7 +2754,7 @@ fn validator_ignores_statements_from_disabled_validators() { ) if validation_data == pvd && validation_code == expected_validation_code && *pov == expected_pov && candidate_receipt.descriptor == candidate.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate_commitments_hash == candidate_receipt.commitments_hash => { response_sender.send(Ok( diff --git a/polkadot/node/core/backing/src/tests/prospective_parachains.rs b/polkadot/node/core/backing/src/tests/prospective_parachains.rs index caddd24080578d74ea4d4964f8ec590eaebb162e..db5409ee4bd50206283ccbd82f9b94682142b1e1 100644 --- a/polkadot/node/core/backing/src/tests/prospective_parachains.rs +++ b/polkadot/node/core/backing/src/tests/prospective_parachains.rs @@ -276,7 +276,7 @@ async fn assert_validate_seconded_candidate( &validation_code == assert_validation_code && &*pov == assert_pov && candidate_receipt.descriptor == candidate.descriptor && - exec_kind == PvfExecKind::BackingSystemParas && + matches!(exec_kind, PvfExecKind::BackingSystemParas(_)) && candidate.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Ok(ValidationResult::Valid( diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index 1e732e2f1f03e2d32387f1902731c0c26e1d3bcd..25614349486ea6c85ee4fc2d529be8d77a4472b4 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -31,13 +31,16 @@ use polkadot_node_primitives::{InvalidCandidate, PoV, ValidationResult}; use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{ - CandidateValidationMessage, PreCheckOutcome, PvfExecKind, RuntimeApiMessage, - RuntimeApiRequest, ValidationFailed, + CandidateValidationMessage, ChainApiMessage, PreCheckOutcome, PvfExecKind, + RuntimeApiMessage, RuntimeApiRequest, ValidationFailed, }, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult, SubsystemSender, }; -use polkadot_node_subsystem_util::{self as util, runtime::ClaimQueueSnapshot}; +use polkadot_node_subsystem_util::{ + self as util, + runtime::{prospective_parachains_mode, ClaimQueueSnapshot, ProspectiveParachainsMode}, +}; use polkadot_overseer::ActiveLeavesUpdate; use polkadot_parachain_primitives::primitives::ValidationResult as WasmValidationResult; use polkadot_primitives::{ @@ -279,6 +282,7 @@ async fn run( comm = ctx.recv().fuse() => { match comm { Ok(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update))) => { + update_active_leaves(ctx.sender(), validation_host.clone(), update.clone()).await; maybe_prepare_validation(ctx.sender(), keystore.clone(), validation_host.clone(), update, &mut prepare_state).await; }, Ok(FromOrchestra::Signal(OverseerSignal::BlockFinalized(..))) => {}, @@ -551,6 +555,66 @@ where Some(processed_code_hashes) } +async fn update_active_leaves( + sender: &mut Sender, + mut validation_backend: impl ValidationBackend, + update: ActiveLeavesUpdate, +) where + Sender: SubsystemSender + SubsystemSender, +{ + let ancestors = get_block_ancestors(sender, update.activated.as_ref().map(|x| x.hash)).await; + if let Err(err) = validation_backend.update_active_leaves(update, ancestors).await { + gum::warn!( + target: LOG_TARGET, + ?err, + "cannot update active leaves in validation backend", + ); + }; +} + +async fn get_allowed_ancestry_len(sender: &mut Sender, relay_parent: Hash) -> Option +where + Sender: SubsystemSender + SubsystemSender, +{ + match prospective_parachains_mode(sender, relay_parent).await { + Ok(ProspectiveParachainsMode::Enabled { allowed_ancestry_len, .. }) => + Some(allowed_ancestry_len), + res => { + gum::warn!(target: LOG_TARGET, ?res, "async backing is disabled"); + None + }, + } +} + +async fn get_block_ancestors( + sender: &mut Sender, + maybe_relay_parent: Option, +) -> Vec +where + Sender: SubsystemSender + SubsystemSender, +{ + let Some(relay_parent) = maybe_relay_parent else { return vec![] }; + let Some(allowed_ancestry_len) = get_allowed_ancestry_len(sender, relay_parent).await else { + return vec![] + }; + + let (tx, rx) = oneshot::channel(); + sender + .send_message(ChainApiMessage::Ancestors { + hash: relay_parent, + k: allowed_ancestry_len, + response_channel: tx, + }) + .await; + match rx.await { + Ok(Ok(x)) => x, + res => { + gum::warn!(target: LOG_TARGET, ?res, "cannot request ancestors"); + vec![] + }, + } +} + struct RuntimeRequestFailed; async fn runtime_api_request( @@ -698,7 +762,7 @@ async fn validate_candidate_exhaustive( // We only check the session index for backing. match (exec_kind, candidate_receipt.descriptor.session_index()) { - (PvfExecKind::Backing | PvfExecKind::BackingSystemParas, Some(session_index)) => { + (PvfExecKind::Backing(_) | PvfExecKind::BackingSystemParas(_), Some(session_index)) => { let Some(expected_session_index) = maybe_expected_session_index else { let error = "cannot fetch session index from the runtime"; gum::warn!( @@ -731,7 +795,7 @@ async fn validate_candidate_exhaustive( let result = match exec_kind { // Retry is disabled to reduce the chance of nondeterministic blocks getting backed and // honest backers getting slashed. - PvfExecKind::Backing | PvfExecKind::BackingSystemParas => { + PvfExecKind::Backing(_) | PvfExecKind::BackingSystemParas(_) => { let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare); let exec_timeout = pvf_exec_timeout(&executor_params, exec_kind.into()); let pvf = PvfPrepData::from_code( @@ -809,6 +873,15 @@ async fn validate_candidate_exhaustive( ); Err(ValidationFailed(e.to_string())) }, + Err(e @ ValidationError::ExecutionDeadline) => { + gum::warn!( + target: LOG_TARGET, + ?para_id, + ?e, + "Job assigned too late, execution queue probably overloaded", + ); + Err(ValidationFailed(e.to_string())) + }, Ok(res) => if res.head_data.hash() != candidate_receipt.descriptor.para_head() { gum::info!(target: LOG_TARGET, ?para_id, "Invalid candidate (para_head)"); @@ -846,7 +919,7 @@ async fn validate_candidate_exhaustive( // descriptor core index. ( Some(_core_index), - PvfExecKind::Backing | PvfExecKind::BackingSystemParas, + PvfExecKind::Backing(_) | PvfExecKind::BackingSystemParas(_), ) => { let Some(claim_queue) = maybe_claim_queue else { let error = "cannot fetch the claim queue from the runtime"; @@ -994,7 +1067,12 @@ trait ValidationBackend { retry_immediately = true; }, - Ok(_) | Err(ValidationError::Invalid(_) | ValidationError::Preparation(_)) => break, + Ok(_) | + Err( + ValidationError::Invalid(_) | + ValidationError::Preparation(_) | + ValidationError::ExecutionDeadline, + ) => break, } // If we got a possibly transient error, retry once after a brief delay, on the @@ -1035,6 +1113,12 @@ trait ValidationBackend { async fn precheck_pvf(&mut self, pvf: PvfPrepData) -> Result<(), PrepareError>; async fn heads_up(&mut self, active_pvfs: Vec) -> Result<(), String>; + + async fn update_active_leaves( + &mut self, + update: ActiveLeavesUpdate, + ancestors: Vec, + ) -> Result<(), String>; } #[async_trait] @@ -1085,6 +1169,14 @@ impl ValidationBackend for ValidationHost { async fn heads_up(&mut self, active_pvfs: Vec) -> Result<(), String> { self.heads_up(active_pvfs).await } + + async fn update_active_leaves( + &mut self, + update: ActiveLeavesUpdate, + ancestors: Vec, + ) -> Result<(), String> { + self.update_active_leaves(update, ancestors).await + } } /// Does basic checks of a candidate. Provide the encoded PoV-block. Returns `Ok` if basic checks diff --git a/polkadot/node/core/candidate-validation/src/tests.rs b/polkadot/node/core/candidate-validation/src/tests.rs index 391247858ed60d4cf5a4da8a939450d624880d49..98e34a1cb4c1342216a89b0e4fd6457868d1d35d 100644 --- a/polkadot/node/core/candidate-validation/src/tests.rs +++ b/polkadot/node/core/candidate-validation/src/tests.rs @@ -473,6 +473,14 @@ impl ValidationBackend for MockValidateCandidateBackend { async fn heads_up(&mut self, _active_pvfs: Vec) -> Result<(), String> { unreachable!() } + + async fn update_active_leaves( + &mut self, + _update: ActiveLeavesUpdate, + _ancestors: Vec, + ) -> Result<(), String> { + unreachable!() + } } #[test] @@ -531,7 +539,7 @@ fn session_index_checked_only_in_backing() { candidate_receipt.clone(), Arc::new(pov.clone()), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Default::default(), )) @@ -670,7 +678,7 @@ fn candidate_validation_ok_is_ok(#[case] v2_descriptor: bool) { candidate_receipt, Arc::new(pov), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Some(ClaimQueueSnapshot(cq)), )) @@ -748,7 +756,7 @@ fn invalid_session_or_core_index() { candidate_receipt.clone(), Arc::new(pov.clone()), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Default::default(), )) @@ -764,7 +772,7 @@ fn invalid_session_or_core_index() { candidate_receipt.clone(), Arc::new(pov.clone()), ExecutorParams::default(), - PvfExecKind::BackingSystemParas, + PvfExecKind::BackingSystemParas(dummy_hash()), &Default::default(), Default::default(), )) @@ -782,7 +790,7 @@ fn invalid_session_or_core_index() { candidate_receipt.clone(), Arc::new(pov.clone()), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Some(Default::default()), )) @@ -797,7 +805,7 @@ fn invalid_session_or_core_index() { candidate_receipt.clone(), Arc::new(pov.clone()), ExecutorParams::default(), - PvfExecKind::BackingSystemParas, + PvfExecKind::BackingSystemParas(dummy_hash()), &Default::default(), Some(Default::default()), )) @@ -866,7 +874,7 @@ fn invalid_session_or_core_index() { candidate_receipt.clone(), Arc::new(pov.clone()), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Some(ClaimQueueSnapshot(cq.clone())), )) @@ -889,7 +897,7 @@ fn invalid_session_or_core_index() { candidate_receipt.clone(), Arc::new(pov.clone()), ExecutorParams::default(), - PvfExecKind::BackingSystemParas, + PvfExecKind::BackingSystemParas(dummy_hash()), &Default::default(), Some(ClaimQueueSnapshot(cq)), )) @@ -944,7 +952,7 @@ fn candidate_validation_bad_return_is_invalid() { candidate_receipt, Arc::new(pov), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Default::default(), )) @@ -1102,7 +1110,7 @@ fn candidate_validation_retry_internal_errors() { #[test] fn candidate_validation_dont_retry_internal_errors() { let v = candidate_validation_retry_on_error_helper( - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), vec![ Err(InternalValidationError::HostCommunication("foo".into()).into()), // Throw an AWD error, we should still retry again. @@ -1136,7 +1144,7 @@ fn candidate_validation_retry_panic_errors() { #[test] fn candidate_validation_dont_retry_panic_errors() { let v = candidate_validation_retry_on_error_helper( - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), vec![ Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError("foo".into()))), // Throw an AWD error, we should still retry again. @@ -1233,7 +1241,7 @@ fn candidate_validation_timeout_is_internal_error() { candidate_receipt, Arc::new(pov), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Default::default(), )); @@ -1282,7 +1290,7 @@ fn candidate_validation_commitment_hash_mismatch_is_invalid() { candidate_receipt, Arc::new(pov), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Default::default(), )) @@ -1337,7 +1345,7 @@ fn candidate_validation_code_mismatch_is_invalid() { candidate_receipt, Arc::new(pov), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Default::default(), )) @@ -1397,7 +1405,7 @@ fn compressed_code_works() { candidate_receipt, Arc::new(pov), ExecutorParams::default(), - PvfExecKind::Backing, + PvfExecKind::Backing(dummy_hash()), &Default::default(), Default::default(), )); @@ -1436,6 +1444,14 @@ impl ValidationBackend for MockPreCheckBackend { async fn heads_up(&mut self, _active_pvfs: Vec) -> Result<(), String> { unreachable!() } + + async fn update_active_leaves( + &mut self, + _update: ActiveLeavesUpdate, + _ancestors: Vec, + ) -> Result<(), String> { + unreachable!() + } } #[test] @@ -1592,6 +1608,14 @@ impl ValidationBackend for MockHeadsUp { let _ = self.heads_up_call_count.fetch_add(1, Ordering::SeqCst); Ok(()) } + + async fn update_active_leaves( + &mut self, + _update: ActiveLeavesUpdate, + _ancestors: Vec, + ) -> Result<(), String> { + unreachable!() + } } fn alice_keystore() -> KeystorePtr { diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs index 9708b0871c2a85f7c6aab4bc5565e818728a2653..2f8a5525570c65eaa2cff59b6f42191ed4ecd0cf 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs @@ -71,7 +71,7 @@ fn make_committed_candidate( persisted_validation_data_hash: persisted_validation_data.hash(), pov_hash: Hash::repeat_byte(1), erasure_root: Hash::repeat_byte(1), - signature: test_helpers::dummy_collator_signature(), + signature: test_helpers::zero_collator_signature(), para_head: para_head.hash(), validation_code_hash: Hash::repeat_byte(42).into(), } diff --git a/polkadot/node/core/prospective-parachains/src/lib.rs b/polkadot/node/core/prospective-parachains/src/lib.rs index 34c1d8823bfce77b7f6c90688cf752674e5332b4..92aea8509f8c49805ece2accc2406b67c414b3d7 100644 --- a/polkadot/node/core/prospective-parachains/src/lib.rs +++ b/polkadot/node/core/prospective-parachains/src/lib.rs @@ -524,7 +524,7 @@ async fn handle_introduce_seconded_candidate( }, }; - let mut added = false; + let mut added = Vec::with_capacity(view.per_relay_parent.len()); let mut para_scheduled = false; // We don't iterate only through the active leaves. We also update the deactivated parents in // the implicit view, so that their upcoming children may see these candidates. @@ -536,18 +536,10 @@ async fn handle_introduce_seconded_candidate( match chain.try_adding_seconded_candidate(&candidate_entry) { Ok(()) => { - gum::debug!( - target: LOG_TARGET, - ?para, - ?relay_parent, - ?is_active_leaf, - "Added seconded candidate {:?}", - candidate_hash - ); - added = true; + added.push(*relay_parent); }, Err(FragmentChainError::CandidateAlreadyKnown) => { - gum::debug!( + gum::trace!( target: LOG_TARGET, ?para, ?relay_parent, @@ -555,10 +547,10 @@ async fn handle_introduce_seconded_candidate( "Attempting to introduce an already known candidate: {:?}", candidate_hash ); - added = true; + added.push(*relay_parent); }, Err(err) => { - gum::debug!( + gum::trace!( target: LOG_TARGET, ?para, ?relay_parent, @@ -580,16 +572,24 @@ async fn handle_introduce_seconded_candidate( ); } - if !added { + if added.is_empty() { gum::debug!( target: LOG_TARGET, para = ?para, candidate = ?candidate_hash, "Newly-seconded candidate cannot be kept under any relay parent", ); + } else { + gum::debug!( + target: LOG_TARGET, + ?para, + "Added/Kept seconded candidate {:?} on relay parents: {:?}", + candidate_hash, + added + ); } - let _ = tx.send(added); + let _ = tx.send(!added.is_empty()); } async fn handle_candidate_backed( @@ -779,12 +779,12 @@ fn answer_hypothetical_membership_request( membership.push(*active_leaf); }, Err(err) => { - gum::debug!( + gum::trace!( target: LOG_TARGET, para = ?para_id, leaf = ?active_leaf, candidate = ?candidate.candidate_hash(), - "Candidate is not a hypothetical member: {}", + "Candidate is not a hypothetical member on: {}", err ) }, @@ -792,6 +792,19 @@ fn answer_hypothetical_membership_request( } } + for (candidate, membership) in &response { + if membership.is_empty() { + gum::debug!( + target: LOG_TARGET, + para = ?candidate.candidate_para(), + active_leaves = ?view.active_leaves, + ?required_active_leaf, + candidate = ?candidate.candidate_hash(), + "Candidate is not a hypothetical member on any of the active leaves", + ) + } + } + let _ = tx.send(response); } diff --git a/polkadot/node/core/pvf/Cargo.toml b/polkadot/node/core/pvf/Cargo.toml index 13fcdc69a99a2b925cb054902ce8fddc17c3cf4f..a9f97c308f266fa5feb50cd18018ab6a085b12a5 100644 --- a/polkadot/node/core/pvf/Cargo.toml +++ b/polkadot/node/core/pvf/Cargo.toml @@ -52,6 +52,7 @@ criterion = { features = [ hex-literal = { workspace = true, default-features = true } polkadot-node-core-pvf-common = { features = ["test-utils"], workspace = true, default-features = true } +polkadot-node-subsystem-test-helpers = { workspace = true } # For benches and integration tests, depend on ourselves with the test-utils # feature. polkadot-node-core-pvf = { features = ["test-utils"], workspace = true, default-features = true } diff --git a/polkadot/node/core/pvf/src/error.rs b/polkadot/node/core/pvf/src/error.rs index a0634106052d7fb197c0662854415190a59a0e70..e68ba595ef5a167102085216e0fc092a9d2a48ae 100644 --- a/polkadot/node/core/pvf/src/error.rs +++ b/polkadot/node/core/pvf/src/error.rs @@ -39,6 +39,11 @@ pub enum ValidationError { /// Preparation or execution issue caused by an internal condition. Should not vote against. #[error("candidate validation: internal: {0}")] Internal(#[from] InternalValidationError), + /// The execution deadline of allowed_ancestry_len + 1 has been reached. Jobs like backing have + /// a limited time to execute. Once the deadline is reached, the current candidate cannot be + /// backed, regardless of its validity. + #[error("candidate validation: execution deadline has been reached.")] + ExecutionDeadline, } /// A description of an error raised during executing a PVF and can be attributed to the combination diff --git a/polkadot/node/core/pvf/src/execute/queue.rs b/polkadot/node/core/pvf/src/execute/queue.rs index 2ac5116912eb9eb6374d27802b0a56c5a02e22d3..6d27ab0261d98077ed7b1c24fe0a6260226a2334 100644 --- a/polkadot/node/core/pvf/src/execute/queue.rs +++ b/polkadot/node/core/pvf/src/execute/queue.rs @@ -35,8 +35,8 @@ use polkadot_node_core_pvf_common::{ SecurityStatus, }; use polkadot_node_primitives::PoV; -use polkadot_node_subsystem::messages::PvfExecKind; -use polkadot_primitives::{ExecutorParams, ExecutorParamsHash, PersistedValidationData}; +use polkadot_node_subsystem::{messages::PvfExecKind, ActiveLeavesUpdate}; +use polkadot_primitives::{ExecutorParams, ExecutorParamsHash, Hash, PersistedValidationData}; use slotmap::HopSlotMap; use std::{ collections::{HashMap, VecDeque}, @@ -45,7 +45,7 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; -use strum::IntoEnumIterator; +use strum::{EnumIter, IntoEnumIterator}; /// The amount of time a job for which the queue does not have a compatible worker may wait in the /// queue. After that time passes, the queue will kill the first worker which becomes idle to @@ -58,6 +58,7 @@ slotmap::new_key_type! { struct Worker; } #[derive(Debug)] pub enum ToQueue { + UpdateActiveLeaves { update: ActiveLeavesUpdate, ancestors: Vec }, Enqueue { artifact: ArtifactPathId, pending_execution_request: PendingExecutionRequest }, } @@ -82,6 +83,7 @@ pub struct PendingExecutionRequest { struct ExecuteJob { artifact: ArtifactPathId, exec_timeout: Duration, + exec_kind: PvfExecKind, pvd: Arc, pov: Arc, executor_params: ExecutorParams, @@ -172,6 +174,9 @@ struct Queue { unscheduled: Unscheduled, workers: Workers, mux: Mux, + + /// Active leaves and their ancestors to check the viability of backing jobs. + active_leaves: HashMap>, } impl Queue { @@ -202,6 +207,7 @@ impl Queue { spawn_inflight: 0, capacity: worker_capacity, }, + active_leaves: Default::default(), } } @@ -278,15 +284,74 @@ impl Queue { } let job = queue.remove(job_index).expect("Job is just checked to be in queue; qed"); + let exec_kind = job.exec_kind; if let Some(worker) = worker { assign(self, worker, job); } else { spawn_extra_worker(self, job); } - self.metrics.on_execute_kind(priority); + self.metrics.on_execute_kind(exec_kind); self.unscheduled.mark_scheduled(priority); } + + fn update_active_leaves(&mut self, update: ActiveLeavesUpdate, ancestors: Vec) { + self.prune_deactivated_leaves(&update); + self.insert_active_leaf(update, ancestors); + self.prune_old_jobs(); + } + + fn prune_deactivated_leaves(&mut self, update: &ActiveLeavesUpdate) { + for hash in &update.deactivated { + let _ = self.active_leaves.remove(&hash); + } + + gum::debug!(target: LOG_TARGET, size = ?self.active_leaves.len(), "Active leaves pruned"); + } + + fn insert_active_leaf(&mut self, update: ActiveLeavesUpdate, ancestors: Vec) { + let Some(leaf) = update.activated else { return }; + let _ = self.active_leaves.insert(leaf.hash, ancestors); + } + + fn prune_old_jobs(&mut self) { + for &priority in &[Priority::Backing, Priority::BackingSystemParas] { + let Some(queue) = self.unscheduled.get_mut(priority) else { continue }; + let to_remove: Vec = queue + .iter() + .enumerate() + .filter_map(|(index, job)| { + let relay_parent = match job.exec_kind { + PvfExecKind::Backing(x) | PvfExecKind::BackingSystemParas(x) => x, + _ => return None, + }; + let in_active_fork = self.active_leaves.iter().any(|(hash, ancestors)| { + *hash == relay_parent || ancestors.contains(&relay_parent) + }); + if in_active_fork { + None + } else { + Some(index) + } + }) + .collect(); + + for &index in to_remove.iter().rev() { + if index > queue.len() { + continue + } + + let Some(job) = queue.remove(index) else { continue }; + let _ = job.result_tx.send(Err(ValidationError::ExecutionDeadline)); + gum::warn!( + target: LOG_TARGET, + ?priority, + exec_kind = ?job.exec_kind, + "Job exceeded its deadline and was dropped without execution", + ); + } + } + } } async fn purge_dead(metrics: &Metrics, workers: &mut Workers) { @@ -305,27 +370,40 @@ async fn purge_dead(metrics: &Metrics, workers: &mut Workers) { } fn handle_to_queue(queue: &mut Queue, to_queue: ToQueue) { - let ToQueue::Enqueue { artifact, pending_execution_request } = to_queue; - let PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx, exec_kind } = - pending_execution_request; - gum::debug!( - target: LOG_TARGET, - validation_code_hash = ?artifact.id.code_hash, - "enqueueing an artifact for execution", - ); - queue.metrics.observe_pov_size(pov.block_data.0.len(), true); - queue.metrics.execute_enqueued(); - let job = ExecuteJob { - artifact, - exec_timeout, - pvd, - pov, - executor_params, - result_tx, - waiting_since: Instant::now(), - }; - queue.unscheduled.add(job, exec_kind); - queue.try_assign_next_job(None); + match to_queue { + ToQueue::UpdateActiveLeaves { update, ancestors } => { + queue.update_active_leaves(update, ancestors); + }, + ToQueue::Enqueue { artifact, pending_execution_request } => { + let PendingExecutionRequest { + exec_timeout, + pvd, + pov, + executor_params, + result_tx, + exec_kind, + } = pending_execution_request; + gum::debug!( + target: LOG_TARGET, + validation_code_hash = ?artifact.id.code_hash, + "enqueueing an artifact for execution", + ); + queue.metrics.observe_pov_size(pov.block_data.0.len(), true); + queue.metrics.execute_enqueued(); + let job = ExecuteJob { + artifact, + exec_timeout, + exec_kind, + pvd, + pov, + executor_params, + result_tx, + waiting_since: Instant::now(), + }; + queue.unscheduled.add(job, exec_kind.into()); + queue.try_assign_next_job(None); + }, + } } async fn handle_mux(queue: &mut Queue, event: QueueEvent) { @@ -648,9 +726,32 @@ pub fn start( (to_queue_tx, from_queue_rx, run) } +/// Priority of execution jobs based on PvfExecKind. +/// +/// The order is important, because we iterate through the values and assume it is going from higher +/// to lowest priority. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, EnumIter)] +enum Priority { + Dispute, + Approval, + BackingSystemParas, + Backing, +} + +impl From for Priority { + fn from(kind: PvfExecKind) -> Self { + match kind { + PvfExecKind::Dispute => Priority::Dispute, + PvfExecKind::Approval => Priority::Approval, + PvfExecKind::BackingSystemParas(_) => Priority::BackingSystemParas, + PvfExecKind::Backing(_) => Priority::Backing, + } + } +} + struct Unscheduled { - unscheduled: HashMap>, - counter: HashMap, + unscheduled: HashMap>, + counter: HashMap, } impl Unscheduled { @@ -677,34 +778,34 @@ impl Unscheduled { /// approvals could not exceed 24%, even if there are no disputes. /// - We cannot fully prioritize backing system parachains over backing other parachains based /// on the distribution of the original 100%. - const PRIORITY_ALLOCATION_THRESHOLDS: &'static [(PvfExecKind, usize)] = &[ - (PvfExecKind::Dispute, 70), - (PvfExecKind::Approval, 80), - (PvfExecKind::BackingSystemParas, 100), - (PvfExecKind::Backing, 100), + const PRIORITY_ALLOCATION_THRESHOLDS: &'static [(Priority, usize)] = &[ + (Priority::Dispute, 70), + (Priority::Approval, 80), + (Priority::BackingSystemParas, 100), + (Priority::Backing, 100), ]; fn new() -> Self { Self { - unscheduled: PvfExecKind::iter().map(|priority| (priority, VecDeque::new())).collect(), - counter: PvfExecKind::iter().map(|priority| (priority, 0)).collect(), + unscheduled: Priority::iter().map(|priority| (priority, VecDeque::new())).collect(), + counter: Priority::iter().map(|priority| (priority, 0)).collect(), } } - fn select_next_priority(&self) -> PvfExecKind { + fn select_next_priority(&self) -> Priority { gum::debug!( target: LOG_TARGET, - unscheduled = ?self.unscheduled.iter().map(|(p, q)| (*p, q.len())).collect::>(), + unscheduled = ?self.unscheduled.iter().map(|(p, q)| (*p, q.len())).collect::>(), counter = ?self.counter, "Selecting next execution priority...", ); - let priority = PvfExecKind::iter() + let priority = Priority::iter() .find(|priority| self.has_pending(priority) && !self.has_reached_threshold(priority)) .unwrap_or_else(|| { - PvfExecKind::iter() + Priority::iter() .find(|priority| self.has_pending(priority)) - .unwrap_or(PvfExecKind::Backing) + .unwrap_or(Priority::Backing) }); gum::debug!( @@ -716,19 +817,19 @@ impl Unscheduled { priority } - fn get_mut(&mut self, priority: PvfExecKind) -> Option<&mut VecDeque> { + fn get_mut(&mut self, priority: Priority) -> Option<&mut VecDeque> { self.unscheduled.get_mut(&priority) } - fn add(&mut self, job: ExecuteJob, priority: PvfExecKind) { + fn add(&mut self, job: ExecuteJob, priority: Priority) { self.unscheduled.entry(priority).or_default().push_back(job); } - fn has_pending(&self, priority: &PvfExecKind) -> bool { + fn has_pending(&self, priority: &Priority) -> bool { !self.unscheduled.get(priority).unwrap_or(&VecDeque::new()).is_empty() } - fn priority_allocation_threshold(priority: &PvfExecKind) -> Option { + fn priority_allocation_threshold(priority: &Priority) -> Option { Self::PRIORITY_ALLOCATION_THRESHOLDS.iter().find_map(|&(p, value)| { if p == *priority { Some(value) @@ -740,7 +841,7 @@ impl Unscheduled { /// Checks if a given priority has reached its allocated threshold /// The thresholds are defined in `PRIORITY_ALLOCATION_THRESHOLDS`. - fn has_reached_threshold(&self, priority: &PvfExecKind) -> bool { + fn has_reached_threshold(&self, priority: &Priority) -> bool { let Some(threshold) = Self::priority_allocation_threshold(priority) else { return false }; let Some(count) = self.counter.get(&priority) else { return false }; // Every time we iterate by lower level priorities @@ -769,22 +870,28 @@ impl Unscheduled { has_reached_threshold } - fn mark_scheduled(&mut self, priority: PvfExecKind) { + fn mark_scheduled(&mut self, priority: Priority) { *self.counter.entry(priority).or_default() += 1; if self.counter.values().sum::() >= Self::SCHEDULING_WINDOW_SIZE { self.reset_counter(); } + gum::debug!( + target: LOG_TARGET, + ?priority, + "Job marked as scheduled", + ); } fn reset_counter(&mut self) { - self.counter = PvfExecKind::iter().map(|kind| (kind, 0)).collect(); + self.counter = Priority::iter().map(|kind| (kind, 0)).collect(); } } #[cfg(test)] mod tests { use polkadot_node_primitives::BlockData; + use polkadot_node_subsystem_test_helpers::mock::new_leaf; use sp_core::H256; use super::*; @@ -803,6 +910,7 @@ mod tests { ExecuteJob { artifact: ArtifactPathId { id: artifact_id(0), path: PathBuf::new() }, exec_timeout: Duration::from_secs(10), + exec_kind: PvfExecKind::Approval, pvd, pov, executor_params: ExecutorParams::default(), @@ -815,11 +923,11 @@ mod tests { fn test_unscheduled_add() { let mut unscheduled = Unscheduled::new(); - PvfExecKind::iter().for_each(|priority| { + Priority::iter().for_each(|priority| { unscheduled.add(create_execution_job(), priority); }); - PvfExecKind::iter().for_each(|priority| { + Priority::iter().for_each(|priority| { let queue = unscheduled.unscheduled.get(&priority).unwrap(); assert_eq!(queue.len(), 1); }); @@ -827,7 +935,7 @@ mod tests { #[test] fn test_unscheduled_priority_distribution() { - use PvfExecKind::*; + use Priority::*; let mut priorities = vec![]; @@ -852,7 +960,7 @@ mod tests { #[test] fn test_unscheduled_priority_distribution_without_backing_system_paras() { - use PvfExecKind::*; + use Priority::*; let mut priorities = vec![]; @@ -876,7 +984,7 @@ mod tests { #[test] fn test_unscheduled_priority_distribution_without_disputes() { - use PvfExecKind::*; + use Priority::*; let mut priorities = vec![]; @@ -900,7 +1008,7 @@ mod tests { #[test] fn test_unscheduled_priority_distribution_without_disputes_and_only_one_backing() { - use PvfExecKind::*; + use Priority::*; let mut priorities = vec![]; @@ -922,7 +1030,7 @@ mod tests { #[test] fn test_unscheduled_does_not_postpone_backing() { - use PvfExecKind::*; + use Priority::*; let mut priorities = vec![]; @@ -940,4 +1048,67 @@ mod tests { assert_eq!(&priorities[..4], &[Approval, Backing, Approval, Approval]); } + + #[tokio::test] + async fn test_prunes_old_jobs_on_active_leaves_update() { + // Set up a queue, but without a real worker, we won't execute any jobs. + let (_, to_queue_rx) = mpsc::channel(1); + let (from_queue_tx, _) = mpsc::unbounded(); + let mut queue = Queue::new( + Metrics::default(), + PathBuf::new(), + PathBuf::new(), + 1, + Duration::from_secs(1), + None, + SecurityStatus::default(), + to_queue_rx, + from_queue_tx, + ); + let old_relay_parent = Hash::random(); + let relevant_relay_parent = Hash::random(); + + assert_eq!(queue.unscheduled.unscheduled.values().map(|x| x.len()).sum::(), 0); + let mut result_rxs = vec![]; + let (result_tx, _result_rx) = oneshot::channel(); + let relevant_job = ExecuteJob { + artifact: ArtifactPathId { id: artifact_id(0), path: PathBuf::new() }, + exec_timeout: Duration::from_secs(1), + exec_kind: PvfExecKind::Backing(relevant_relay_parent), + pvd: Arc::new(PersistedValidationData::default()), + pov: Arc::new(PoV { block_data: BlockData(Vec::new()) }), + executor_params: ExecutorParams::default(), + result_tx, + waiting_since: Instant::now(), + }; + queue.unscheduled.add(relevant_job, Priority::Backing); + for _ in 0..10 { + let (result_tx, result_rx) = oneshot::channel(); + let expired_job = ExecuteJob { + artifact: ArtifactPathId { id: artifact_id(0), path: PathBuf::new() }, + exec_timeout: Duration::from_secs(1), + exec_kind: PvfExecKind::Backing(old_relay_parent), + pvd: Arc::new(PersistedValidationData::default()), + pov: Arc::new(PoV { block_data: BlockData(Vec::new()) }), + executor_params: ExecutorParams::default(), + result_tx, + waiting_since: Instant::now(), + }; + queue.unscheduled.add(expired_job, Priority::Backing); + result_rxs.push(result_rx); + } + assert_eq!(queue.unscheduled.unscheduled.values().map(|x| x.len()).sum::(), 11); + + // Add an active leaf + queue.update_active_leaves( + ActiveLeavesUpdate::start_work(new_leaf(Hash::random(), 1)), + vec![relevant_relay_parent], + ); + + // It prunes all old jobs and drops them with an `ExecutionDeadline` error. + for rx in result_rxs { + assert!(matches!(rx.await, Ok(Err(ValidationError::ExecutionDeadline)))); + } + assert_eq!(queue.unscheduled.unscheduled.values().map(|x| x.len()).sum::(), 1); + } } diff --git a/polkadot/node/core/pvf/src/host.rs b/polkadot/node/core/pvf/src/host.rs index 37cd6fcbf74aae3672e60c2de304e535e7018e6f..8252904095b3f5acb0724c2de090a1f1782071ef 100644 --- a/polkadot/node/core/pvf/src/host.rs +++ b/polkadot/node/core/pvf/src/host.rs @@ -37,9 +37,11 @@ use polkadot_node_core_pvf_common::{ pvf::PvfPrepData, }; use polkadot_node_primitives::PoV; -use polkadot_node_subsystem::{messages::PvfExecKind, SubsystemError, SubsystemResult}; +use polkadot_node_subsystem::{ + messages::PvfExecKind, ActiveLeavesUpdate, SubsystemError, SubsystemResult, +}; use polkadot_parachain_primitives::primitives::ValidationResult; -use polkadot_primitives::PersistedValidationData; +use polkadot_primitives::{Hash, PersistedValidationData}; use std::{ collections::HashMap, path::PathBuf, @@ -143,12 +145,27 @@ impl ValidationHost { .await .map_err(|_| "the inner loop hung up".to_string()) } + + /// Sends a signal to the validation host requesting to update best block. + /// + /// Returns an error if the request cannot be sent to the validation host, i.e. if it shut down. + pub async fn update_active_leaves( + &mut self, + update: ActiveLeavesUpdate, + ancestors: Vec, + ) -> Result<(), String> { + self.to_host_tx + .send(ToHost::UpdateActiveLeaves { update, ancestors }) + .await + .map_err(|_| "the inner loop hung up".to_string()) + } } enum ToHost { PrecheckPvf { pvf: PvfPrepData, result_tx: PrecheckResultSender }, ExecutePvf(ExecutePvfInputs), HeadsUp { active_pvfs: Vec }, + UpdateActiveLeaves { update: ActiveLeavesUpdate, ancestors: Vec }, } struct ExecutePvfInputs { @@ -488,6 +505,8 @@ async fn handle_to_host( }, ToHost::HeadsUp { active_pvfs } => handle_heads_up(artifacts, prepare_queue, active_pvfs).await?, + ToHost::UpdateActiveLeaves { update, ancestors } => + handle_update_active_leaves(execute_queue, update, ancestors).await?, } Ok(()) @@ -855,6 +874,14 @@ async fn handle_prepare_done( Ok(()) } +async fn handle_update_active_leaves( + execute_queue: &mut mpsc::Sender, + update: ActiveLeavesUpdate, + ancestors: Vec, +) -> Result<(), Fatal> { + send_execute(execute_queue, execute::ToQueue::UpdateActiveLeaves { update, ancestors }).await +} + async fn send_prepare( prepare_queue: &mut mpsc::Sender, to_queue: prepare::ToQueue, @@ -1255,7 +1282,7 @@ pub(crate) mod tests { pvd.clone(), pov1.clone(), Priority::Normal, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx, ) .await @@ -1268,7 +1295,7 @@ pub(crate) mod tests { pvd.clone(), pov1, Priority::Critical, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx, ) .await @@ -1281,7 +1308,7 @@ pub(crate) mod tests { pvd, pov2, Priority::Normal, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx, ) .await @@ -1431,7 +1458,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx, ) .await @@ -1480,7 +1507,7 @@ pub(crate) mod tests { pvd, pov, Priority::Critical, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx, ) .await @@ -1591,7 +1618,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx, ) .await @@ -1623,7 +1650,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx_2, ) .await @@ -1647,7 +1674,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx_3, ) .await @@ -1706,7 +1733,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx, ) .await @@ -1738,7 +1765,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx_2, ) .await @@ -1762,7 +1789,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx_3, ) .await @@ -1837,7 +1864,7 @@ pub(crate) mod tests { pvd, pov, Priority::Normal, - PvfExecKind::Backing, + PvfExecKind::Backing(H256::default()), result_tx, ) .await diff --git a/polkadot/node/core/pvf/src/priority.rs b/polkadot/node/core/pvf/src/priority.rs index 7aaeacf3622093433594524451cd68761521409b..5a58fbc8ade3ac0098cbb7aab6c142fb0380d785 100644 --- a/polkadot/node/core/pvf/src/priority.rs +++ b/polkadot/node/core/pvf/src/priority.rs @@ -43,8 +43,8 @@ impl From for Priority { match priority { PvfExecKind::Dispute => Priority::Critical, PvfExecKind::Approval => Priority::Critical, - PvfExecKind::BackingSystemParas => Priority::Normal, - PvfExecKind::Backing => Priority::Normal, + PvfExecKind::BackingSystemParas(_) => Priority::Normal, + PvfExecKind::Backing(_) => Priority::Normal, } } } diff --git a/polkadot/node/core/pvf/tests/it/adder.rs b/polkadot/node/core/pvf/tests/it/adder.rs index 1a95a28fe07732f51d5a8bdd988ee51c79e66a3e..924ea71667027bc523db12ea86dcda23d4620aa3 100644 --- a/polkadot/node/core/pvf/tests/it/adder.rs +++ b/polkadot/node/core/pvf/tests/it/adder.rs @@ -46,6 +46,7 @@ async fn execute_good_block_on_parent() { pvd, pov, Default::default(), + H256::default(), ) .await .unwrap(); @@ -82,6 +83,7 @@ async fn execute_good_chain_on_parent() { pvd, pov, Default::default(), + H256::default(), ) .await .unwrap(); @@ -120,6 +122,7 @@ async fn execute_bad_block_on_parent() { pvd, pov, Default::default(), + H256::default(), ) .await .unwrap_err(); @@ -145,6 +148,7 @@ async fn stress_spawn() { pvd, pov, Default::default(), + H256::default(), ) .await .unwrap(); @@ -185,6 +189,7 @@ async fn execute_can_run_serially() { pvd, pov, Default::default(), + H256::default(), ) .await .unwrap(); diff --git a/polkadot/node/core/pvf/tests/it/main.rs b/polkadot/node/core/pvf/tests/it/main.rs index 4cbc6fb04a8efaabbac8d56a5a90db624a3cc842..cfb78fd530d233d43b0495acc780779ade7ce62c 100644 --- a/polkadot/node/core/pvf/tests/it/main.rs +++ b/polkadot/node/core/pvf/tests/it/main.rs @@ -28,8 +28,8 @@ use polkadot_node_primitives::{PoV, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT}; use polkadot_node_subsystem::messages::PvfExecKind; use polkadot_parachain_primitives::primitives::{BlockData, ValidationResult}; use polkadot_primitives::{ - ExecutorParam, ExecutorParams, PersistedValidationData, PvfExecKind as RuntimePvfExecKind, - PvfPrepKind, + ExecutorParam, ExecutorParams, Hash, PersistedValidationData, + PvfExecKind as RuntimePvfExecKind, PvfPrepKind, }; use sp_core::H256; @@ -108,6 +108,7 @@ impl TestHost { pvd: PersistedValidationData, pov: PoV, executor_params: ExecutorParams, + relay_parent: Hash, ) -> Result { let (result_tx, result_rx) = futures::channel::oneshot::channel(); @@ -125,7 +126,7 @@ impl TestHost { Arc::new(pvd), Arc::new(pov), polkadot_node_core_pvf::Priority::Normal, - PvfExecKind::Backing, + PvfExecKind::Backing(relay_parent), result_tx, ) .await @@ -171,7 +172,13 @@ async fn execute_job_terminates_on_timeout() { let start = std::time::Instant::now(); let result = host - .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) + .validate_candidate( + test_parachain_halt::wasm_binary_unwrap(), + pvd, + pov, + Default::default(), + H256::default(), + ) .await; match result { @@ -201,12 +208,14 @@ async fn ensure_parallel_execution() { pvd.clone(), pov.clone(), Default::default(), + H256::default(), ); let execute_pvf_future_2 = host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default(), + H256::default(), ); let start = std::time::Instant::now(); @@ -254,6 +263,7 @@ async fn execute_queue_doesnt_stall_if_workers_died() { pvd.clone(), pov.clone(), Default::default(), + H256::default(), ) })) .await; @@ -303,6 +313,7 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { 0 => executor_params_1.clone(), _ => executor_params_2.clone(), }, + H256::default(), ) })) .await; @@ -359,7 +370,13 @@ async fn deleting_prepared_artifact_does_not_dispute() { // Try to validate, artifact should get recreated. let result = host - .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) + .validate_candidate( + test_parachain_halt::wasm_binary_unwrap(), + pvd, + pov, + Default::default(), + H256::default(), + ) .await; assert_matches!(result, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout))); @@ -410,7 +427,13 @@ async fn corrupted_prepared_artifact_does_not_dispute() { // Try to validate, artifact should get removed because of the corruption. let result = host - .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) + .validate_candidate( + test_parachain_halt::wasm_binary_unwrap(), + pvd, + pov, + Default::default(), + H256::default(), + ) .await; assert_matches!( @@ -684,7 +707,9 @@ async fn invalid_compressed_code_fails_validation() { let validation_code = sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1).unwrap(); - let result = host.validate_candidate(&validation_code, pvd, pov, Default::default()).await; + let result = host + .validate_candidate(&validation_code, pvd, pov, Default::default(), H256::default()) + .await; assert_matches!( result, @@ -708,7 +733,13 @@ async fn invalid_compressed_pov_fails_validation() { let pov = PoV { block_data: BlockData(block_data) }; let result = host - .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) + .validate_candidate( + test_parachain_halt::wasm_binary_unwrap(), + pvd, + pov, + Default::default(), + H256::default(), + ) .await; assert_matches!( diff --git a/polkadot/node/core/pvf/tests/it/process.rs b/polkadot/node/core/pvf/tests/it/process.rs index b3023c8a45c3b3087015e714c22f9bd0474d251e..353367b394f348551bc4ebc93ca384bb30190db9 100644 --- a/polkadot/node/core/pvf/tests/it/process.rs +++ b/polkadot/node/core/pvf/tests/it/process.rs @@ -141,6 +141,7 @@ rusty_fork_test! { pvd, pov, Default::default(), + H256::default(), ) .await .unwrap(); @@ -187,6 +188,7 @@ rusty_fork_test! { pvd, pov, Default::default(), + H256::default(), ), // Send a stop signal to pause the worker. async { @@ -242,6 +244,7 @@ rusty_fork_test! { pvd, pov, Default::default(), + H256::default(), ), // Run a future that kills the job while it's running. async { @@ -301,6 +304,7 @@ rusty_fork_test! { pvd, pov, Default::default(), + H256::default(), ), // Run a future that kills the job while it's running. async { @@ -372,6 +376,7 @@ rusty_fork_test! { pvd, pov, Default::default(), + H256::default(), ), // Run a future that tests the thread count while the worker is running. async { diff --git a/polkadot/node/network/collator-protocol/src/error.rs b/polkadot/node/network/collator-protocol/src/error.rs index 0f5e0699d85c026cb3ad489de919cbb9c82ed50c..ae7f9a8c1fbc823cc0b6b89dc7303de57faed794 100644 --- a/polkadot/node/network/collator-protocol/src/error.rs +++ b/polkadot/node/network/collator-protocol/src/error.rs @@ -23,6 +23,7 @@ use polkadot_node_network_protocol::request_response::incoming; use polkadot_node_primitives::UncheckedSignedFullStatement; use polkadot_node_subsystem::{errors::SubsystemError, RuntimeApiError}; use polkadot_node_subsystem_util::{backing_implicit_view, runtime}; +use polkadot_primitives::vstaging::CandidateDescriptorVersion; use crate::LOG_TARGET; @@ -63,6 +64,12 @@ pub enum Error { #[error("CollationSeconded contained statement with invalid signature")] InvalidStatementSignature(UncheckedSignedFullStatement), + + #[error("Response receiver for session index request cancelled")] + CancelledSessionIndex(oneshot::Canceled), + + #[error("Response receiver for claim queue request cancelled")] + CancelledClaimQueue(oneshot::Canceled), } /// An error happened on the validator side of the protocol when attempting @@ -87,11 +94,23 @@ pub enum SecondingError { #[error("Candidate hash doesn't match the advertisement")] CandidateHashMismatch, + #[error("Relay parent hash doesn't match the advertisement")] + RelayParentMismatch, + #[error("Received duplicate collation from the peer")] Duplicate, #[error("The provided parent head data does not match the hash")] ParentHeadDataMismatch, + + #[error("Core index {0} present in descriptor is different than the assigned core {1}")] + InvalidCoreIndex(u32, u32), + + #[error("Session index {0} present in descriptor is different than the expected one {1}")] + InvalidSessionIndex(u32, u32), + + #[error("Invalid candidate receipt version {0:?}")] + InvalidReceiptVersion(CandidateDescriptorVersion), } impl SecondingError { @@ -102,7 +121,11 @@ impl SecondingError { self, PersistedValidationDataMismatch | CandidateHashMismatch | - Duplicate | ParentHeadDataMismatch + RelayParentMismatch | + Duplicate | ParentHeadDataMismatch | + InvalidCoreIndex(_, _) | + InvalidSessionIndex(_, _) | + InvalidReceiptVersion(_) ) } } diff --git a/polkadot/node/network/collator-protocol/src/validator_side/collation.rs b/polkadot/node/network/collator-protocol/src/validator_side/collation.rs index 0b3e9f4b34317b5d735ff574448c2b88873a1d31..cc0de1cb70f66d22b396e44c3603d224eaf42f9e 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/collation.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/collation.rs @@ -129,9 +129,6 @@ pub struct BlockedCollationId { } /// Performs a sanity check between advertised and fetched collations. -/// -/// Since the persisted validation data is constructed using the advertised -/// parent head data hash, the latter doesn't require an additional check. pub fn fetched_collation_sanity_check( advertised: &PendingCollation, fetched: &CandidateReceipt, @@ -139,17 +136,25 @@ pub fn fetched_collation_sanity_check( maybe_parent_head_and_hash: Option<(HeadData, Hash)>, ) -> Result<(), SecondingError> { if persisted_validation_data.hash() != fetched.descriptor().persisted_validation_data_hash() { - Err(SecondingError::PersistedValidationDataMismatch) - } else if advertised + return Err(SecondingError::PersistedValidationDataMismatch) + } + + if advertised .prospective_candidate .map_or(false, |pc| pc.candidate_hash() != fetched.hash()) { - Err(SecondingError::CandidateHashMismatch) - } else if maybe_parent_head_and_hash.map_or(false, |(head, hash)| head.hash() != hash) { - Err(SecondingError::ParentHeadDataMismatch) - } else { - Ok(()) + return Err(SecondingError::CandidateHashMismatch) + } + + if advertised.relay_parent != fetched.descriptor.relay_parent() { + return Err(SecondingError::RelayParentMismatch) } + + if maybe_parent_head_and_hash.map_or(false, |(head, hash)| head.hash() != hash) { + return Err(SecondingError::ParentHeadDataMismatch) + } + + Ok(()) } /// Identifier for a requested collation and the respective collator that advertised it. diff --git a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs index 51e987d59ce847977a9c2dda13941cea7c139c1c..86358f503d04d58d55fb8ff1f4f2df7bbdd9c4e4 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs @@ -49,11 +49,14 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}, - runtime::{fetch_claim_queue, prospective_parachains_mode, ProspectiveParachainsMode}, + request_claim_queue, request_session_index_for_child, + runtime::{prospective_parachains_mode, request_node_features, ProspectiveParachainsMode}, }; use polkadot_primitives::{ - vstaging::CoreState, CandidateHash, CollatorId, Hash, HeadData, Id as ParaId, - OccupiedCoreAssumption, PersistedValidationData, + node_features, + vstaging::{CandidateDescriptorV2, CandidateDescriptorVersion, CoreState}, + CandidateHash, CollatorId, CoreIndex, Hash, HeadData, Id as ParaId, OccupiedCoreAssumption, + PersistedValidationData, SessionIndex, }; use crate::error::{Error, FetchError, Result, SecondingError}; @@ -369,16 +372,9 @@ struct PerRelayParent { prospective_parachains_mode: ProspectiveParachainsMode, assignment: GroupAssignments, collations: Collations, -} - -impl PerRelayParent { - fn new(mode: ProspectiveParachainsMode) -> Self { - Self { - prospective_parachains_mode: mode, - assignment: GroupAssignments { current: vec![] }, - collations: Collations::default(), - } - } + v2_receipts: bool, + current_core: CoreIndex, + session_index: SessionIndex, } /// All state relevant for the validator side of the protocol lives here. @@ -460,14 +456,15 @@ fn is_relay_parent_in_implicit_view( } } -async fn assign_incoming( +async fn construct_per_relay_parent( sender: &mut Sender, - group_assignment: &mut GroupAssignments, current_assignments: &mut HashMap, keystore: &KeystorePtr, relay_parent: Hash, relay_parent_mode: ProspectiveParachainsMode, -) -> Result<()> + v2_receipts: bool, + session_index: SessionIndex, +) -> Result> where Sender: CollatorProtocolSenderTrait, { @@ -494,25 +491,25 @@ where rotation_info.core_for_group(group, cores.len()) } else { gum::trace!(target: LOG_TARGET, ?relay_parent, "Not a validator"); - return Ok(()) + return Ok(None) }; - let paras_now = match fetch_claim_queue(sender, relay_parent).await.map_err(Error::Runtime)? { - // Runtime supports claim queue - use it - // - // `relay_parent_mode` is not examined here because if the runtime supports claim queue - // then it supports async backing params too (`ASYNC_BACKING_STATE_RUNTIME_REQUIREMENT` - // < `CLAIM_QUEUE_RUNTIME_REQUIREMENT`). - Some(mut claim_queue) => claim_queue.0.remove(&core_now), - // Claim queue is not supported by the runtime - use availability cores instead. - None => cores.get(core_now.0 as usize).and_then(|c| match c { - CoreState::Occupied(core) if relay_parent_mode.is_enabled() => - core.next_up_on_available.as_ref().map(|c| [c.para_id].into_iter().collect()), - CoreState::Scheduled(core) => Some([core.para_id].into_iter().collect()), - CoreState::Occupied(_) | CoreState::Free => None, - }), - } - .unwrap_or_else(|| VecDeque::new()); + let claim_queue = request_claim_queue(relay_parent, sender) + .await + .await + .map_err(Error::CancelledClaimQueue)??; + + let paras_now = cores + .get(core_now.0 as usize) + .and_then(|c| match (c, relay_parent_mode) { + (CoreState::Occupied(_), ProspectiveParachainsMode::Disabled) => None, + ( + CoreState::Occupied(_), + ProspectiveParachainsMode::Enabled { max_candidate_depth: 0, .. }, + ) => None, + _ => claim_queue.get(&core_now).cloned(), + }) + .unwrap_or_else(|| VecDeque::new()); for para_id in paras_now.iter() { let entry = current_assignments.entry(*para_id).or_default(); @@ -527,9 +524,14 @@ where } } - *group_assignment = GroupAssignments { current: paras_now.into_iter().collect() }; - - Ok(()) + Ok(Some(PerRelayParent { + prospective_parachains_mode: relay_parent_mode, + assignment: GroupAssignments { current: paras_now.into_iter().collect() }, + collations: Collations::default(), + v2_receipts, + session_index, + current_core: core_now, + })) } fn remove_outgoing( @@ -1249,18 +1251,31 @@ where let added = view.iter().filter(|h| !current_leaves.contains_key(h)); for leaf in added { + let session_index = request_session_index_for_child(*leaf, sender) + .await + .await + .map_err(Error::CancelledSessionIndex)??; let mode = prospective_parachains_mode(sender, *leaf).await?; - - let mut per_relay_parent = PerRelayParent::new(mode); - assign_incoming( + let v2_receipts = request_node_features(*leaf, session_index, sender) + .await? + .unwrap_or_default() + .get(node_features::FeatureIndex::CandidateReceiptV2 as usize) + .map(|b| *b) + .unwrap_or(false); + + let Some(per_relay_parent) = construct_per_relay_parent( sender, - &mut per_relay_parent.assignment, &mut state.current_assignments, keystore, *leaf, mode, + v2_receipts, + session_index, ) - .await?; + .await? + else { + continue + }; state.active_leaves.insert(*leaf, mode); state.per_relay_parent.insert(*leaf, per_relay_parent); @@ -1279,18 +1294,21 @@ where .unwrap_or_default(); for block_hash in allowed_ancestry { if let Entry::Vacant(entry) = state.per_relay_parent.entry(*block_hash) { - let mut per_relay_parent = PerRelayParent::new(mode); - assign_incoming( + // Safe to use the same v2 receipts config for the allowed relay parents as well + // as the same session index since they must be in the same session. + if let Some(per_relay_parent) = construct_per_relay_parent( sender, - &mut per_relay_parent.assignment, &mut state.current_assignments, keystore, *block_hash, mode, + v2_receipts, + session_index, ) - .await?; - - entry.insert(per_relay_parent); + .await? + { + entry.insert(per_relay_parent); + } } } } @@ -1621,11 +1639,10 @@ async fn run_inner( Ok(FromOrchestra::Signal(OverseerSignal::Conclude)) | Err(_) => break, Ok(FromOrchestra::Signal(_)) => continue, } - } + }, _ = next_inactivity_stream.next() => { disconnect_inactive_peers(ctx.sender(), &eviction_policy, &state.peer_data).await; - } - + }, resp = state.collation_requests.select_next_some() => { let res = match handle_collation_fetch_response( &mut state, @@ -1684,7 +1701,7 @@ async fn run_inner( } Ok(true) => {} } - } + }, res = state.collation_fetch_timeouts.select_next_some() => { let (collator_id, maybe_candidate_hash, relay_parent) = res; gum::debug!( @@ -1814,6 +1831,10 @@ async fn kick_off_seconding( return Ok(false) }, }; + + // Sanity check of the candidate receipt version. + descriptor_version_sanity_check(candidate_receipt.descriptor(), per_relay_parent)?; + let collations = &mut per_relay_parent.collations; let fetched_collation = FetchedCollation::from(&candidate_receipt); @@ -2024,7 +2045,9 @@ async fn handle_collation_fetch_response( }, Ok( request_v1::CollationFetchingResponse::Collation(receipt, _) | - request_v1::CollationFetchingResponse::CollationWithParentHeadData { receipt, .. }, + request_v2::CollationFetchingResponse::Collation(receipt, _) | + request_v1::CollationFetchingResponse::CollationWithParentHeadData { receipt, .. } | + request_v2::CollationFetchingResponse::CollationWithParentHeadData { receipt, .. }, ) if receipt.descriptor().para_id() != pending_collation.para_id => { gum::debug!( target: LOG_TARGET, @@ -2086,3 +2109,35 @@ async fn handle_collation_fetch_response( state.metrics.on_request(metrics_result); result } + +// Sanity check the candidate descriptor version. +fn descriptor_version_sanity_check( + descriptor: &CandidateDescriptorV2, + per_relay_parent: &PerRelayParent, +) -> std::result::Result<(), SecondingError> { + match descriptor.version() { + CandidateDescriptorVersion::V1 => Ok(()), + CandidateDescriptorVersion::V2 if per_relay_parent.v2_receipts => { + if let Some(core_index) = descriptor.core_index() { + if core_index != per_relay_parent.current_core { + return Err(SecondingError::InvalidCoreIndex( + core_index.0, + per_relay_parent.current_core.0, + )) + } + } + + if let Some(session_index) = descriptor.session_index() { + if session_index != per_relay_parent.session_index { + return Err(SecondingError::InvalidSessionIndex( + session_index, + per_relay_parent.session_index, + )) + } + } + + Ok(()) + }, + descriptor_version => Err(SecondingError::InvalidReceiptVersion(descriptor_version)), + } +} diff --git a/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs b/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs index 290c4db901d5b8e7caf3d4f20f687855d6a1811c..7bc61dd4ebec5de5ba21c2feb60284cd74fdbdad 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/tests/mod.rs @@ -42,9 +42,10 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::{reputation::add_reputation, TimeoutExt}; use polkadot_primitives::{ + node_features, vstaging::{CandidateReceiptV2 as CandidateReceipt, CoreState, OccupiedCore}, - CollatorPair, CoreIndex, GroupIndex, GroupRotationInfo, HeadData, PersistedValidationData, - ScheduledCore, ValidatorId, ValidatorIndex, + CollatorPair, CoreIndex, GroupIndex, GroupRotationInfo, HeadData, NodeFeatures, + PersistedValidationData, ScheduledCore, ValidatorId, ValidatorIndex, }; use polkadot_primitives_test_helpers::{ dummy_candidate_descriptor, dummy_candidate_receipt_bad_sig, dummy_hash, @@ -78,6 +79,8 @@ struct TestState { group_rotation_info: GroupRotationInfo, cores: Vec, claim_queue: BTreeMap>, + node_features: NodeFeatures, + session_index: SessionIndex, } impl Default for TestState { @@ -132,6 +135,10 @@ impl Default for TestState { claim_queue.insert(CoreIndex(1), VecDeque::new()); claim_queue.insert(CoreIndex(2), [chain_ids[1]].into_iter().collect()); + let mut node_features = NodeFeatures::EMPTY; + node_features.resize(node_features::FeatureIndex::CandidateReceiptV2 as usize + 1, false); + node_features.set(node_features::FeatureIndex::CandidateReceiptV2 as u8 as usize, true); + Self { chain_ids, relay_parent, @@ -141,6 +148,8 @@ impl Default for TestState { group_rotation_info, cores, claim_queue, + node_features, + session_index: 1, } } } @@ -237,10 +246,44 @@ async fn overseer_signal(overseer: &mut VirtualOverseer, signal: OverseerSignal) .expect(&format!("{:?} is more than enough for sending signals.", TIMEOUT)); } -async fn respond_to_core_info_queries( +async fn respond_to_runtime_api_queries( virtual_overseer: &mut VirtualOverseer, test_state: &TestState, + hash: Hash, ) { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + rp, + RuntimeApiRequest::SessionIndexForChild(tx) + )) => { + assert_eq!(rp, hash); + tx.send(Ok(test_state.session_index)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + rp, + RuntimeApiRequest::AsyncBackingParams(tx) + )) => { + assert_eq!(rp, hash); + tx.send(Err(ASYNC_BACKING_DISABLED_ERROR)).unwrap(); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + rp, + RuntimeApiRequest::NodeFeatures(_, tx) + )) => { + assert_eq!(rp, hash); + tx.send(Ok(test_state.node_features.clone())).unwrap(); + } + ); + assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( @@ -254,9 +297,10 @@ async fn respond_to_core_info_queries( assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _, + rp, RuntimeApiRequest::ValidatorGroups(tx), )) => { + assert_eq!(rp, hash); let _ = tx.send(Ok(( test_state.validator_groups.clone(), test_state.group_rotation_info.clone(), @@ -267,9 +311,10 @@ async fn respond_to_core_info_queries( assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _, + rp, RuntimeApiRequest::AvailabilityCores(tx), )) => { + assert_eq!(rp, hash); let _ = tx.send(Ok(test_state.cores.clone())); } ); @@ -277,19 +322,10 @@ async fn respond_to_core_info_queries( assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _, - RuntimeApiRequest::Version(tx), - )) => { - let _ = tx.send(Ok(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)); - } - ); - - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - _, + rp, RuntimeApiRequest::ClaimQueue(tx), )) => { + assert_eq!(rp, hash); let _ = tx.send(Ok(test_state.claim_queue.clone())); } ); @@ -470,19 +506,6 @@ async fn advertise_collation( .await; } -async fn assert_async_backing_params_request(virtual_overseer: &mut VirtualOverseer, hash: Hash) { - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::AsyncBackingParams(tx) - )) => { - assert_eq!(relay_parent, hash); - tx.send(Err(ASYNC_BACKING_DISABLED_ERROR)).unwrap(); - } - ); -} - // As we receive a relevant advertisement act on it and issue a collation request. #[test] fn act_on_advertisement() { @@ -502,8 +525,8 @@ fn act_on_advertisement() { ) .await; - assert_async_backing_params_request(&mut virtual_overseer, test_state.relay_parent).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, test_state.relay_parent) + .await; let peer_b = PeerId::random(); @@ -550,8 +573,8 @@ fn act_on_advertisement_v2() { ) .await; - assert_async_backing_params_request(&mut virtual_overseer, test_state.relay_parent).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, test_state.relay_parent) + .await; let peer_b = PeerId::random(); @@ -631,9 +654,8 @@ fn collator_reporting_works() { ) .await; - assert_async_backing_params_request(&mut virtual_overseer, test_state.relay_parent).await; - - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, test_state.relay_parent) + .await; let peer_b = PeerId::random(); let peer_c = PeerId::random(); @@ -748,8 +770,7 @@ fn fetch_one_collation_at_a_time() { // Iter over view since the order may change due to sorted invariant. for hash in our_view.iter() { - assert_async_backing_params_request(&mut virtual_overseer, *hash).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, *hash).await; } let peer_b = PeerId::random(); @@ -847,8 +868,7 @@ fn fetches_next_collation() { .await; for hash in our_view.iter() { - assert_async_backing_params_request(&mut virtual_overseer, *hash).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, *hash).await; } let peer_b = PeerId::random(); @@ -972,8 +992,8 @@ fn reject_connection_to_next_group() { ) .await; - assert_async_backing_params_request(&mut virtual_overseer, test_state.relay_parent).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, test_state.relay_parent) + .await; let peer_b = PeerId::random(); @@ -1024,8 +1044,7 @@ fn fetch_next_collation_on_invalid_collation() { .await; for hash in our_view.iter() { - assert_async_backing_params_request(&mut virtual_overseer, *hash).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, *hash).await; } let peer_b = PeerId::random(); @@ -1135,8 +1154,8 @@ fn inactive_disconnected() { ) .await; - assert_async_backing_params_request(&mut virtual_overseer, hash_a).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, test_state.relay_parent) + .await; let peer_b = PeerId::random(); @@ -1189,8 +1208,7 @@ fn activity_extends_life() { .await; for hash in our_view.iter() { - assert_async_backing_params_request(&mut virtual_overseer, *hash).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, *hash).await; } let peer_b = PeerId::random(); @@ -1263,8 +1281,8 @@ fn disconnect_if_no_declare() { ) .await; - assert_async_backing_params_request(&mut virtual_overseer, test_state.relay_parent).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, test_state.relay_parent) + .await; let peer_b = PeerId::random(); @@ -1302,8 +1320,8 @@ fn disconnect_if_wrong_declare() { ) .await; - assert_async_backing_params_request(&mut virtual_overseer, test_state.relay_parent).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, test_state.relay_parent) + .await; let peer_b = PeerId::random(); @@ -1364,8 +1382,8 @@ fn delay_reputation_change() { ) .await; - assert_async_backing_params_request(&mut virtual_overseer, test_state.relay_parent).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, test_state.relay_parent) + .await; let peer_b = PeerId::random(); @@ -1450,8 +1468,8 @@ fn view_change_clears_old_collators() { ) .await; - assert_async_backing_params_request(&mut virtual_overseer, test_state.relay_parent).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, test_state.relay_parent) + .await; let peer_b = PeerId::random(); @@ -1475,8 +1493,7 @@ fn view_change_clears_old_collators() { .await; test_state.group_rotation_info = test_state.group_rotation_info.bump_rotation(); - assert_async_backing_params_request(&mut virtual_overseer, hash_b).await; - respond_to_core_info_queries(&mut virtual_overseer, &test_state).await; + respond_to_runtime_api_queries(&mut virtual_overseer, &test_state, hash_b).await; assert_collator_disconnect(&mut virtual_overseer, peer_b).await; diff --git a/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs b/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs index e040163cd905bda362acae30eb33eec97fc690d9..eda26e8539a1db817c55dc4b70db9a5850e29fc8 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs @@ -20,9 +20,10 @@ use super::*; use polkadot_node_subsystem::messages::ChainApiMessage; use polkadot_primitives::{ - vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, AsyncBackingParams, - BlockNumber, CandidateCommitments, Header, SigningContext, ValidatorId, + vstaging::{CommittedCandidateReceiptV2 as CommittedCandidateReceipt, MutateDescriptorV2}, + AsyncBackingParams, BlockNumber, CandidateCommitments, Header, SigningContext, ValidatorId, }; +use polkadot_primitives_test_helpers::dummy_committed_candidate_receipt_v2; use rstest::rstest; const ASYNC_BACKING_PARAMETERS: AsyncBackingParams = @@ -32,7 +33,7 @@ fn get_parent_hash(hash: Hash) -> Hash { Hash::from_low_u64_be(hash.to_low_u64_be() + 1) } -async fn assert_assign_incoming( +async fn assert_construct_per_relay_parent( virtual_overseer: &mut VirtualOverseer, test_state: &TestState, hash: Hash, @@ -73,16 +74,6 @@ async fn assert_assign_incoming( } ); - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - parent, - RuntimeApiRequest::Version(tx), - )) if parent == hash => { - let _ = tx.send(Ok(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)); - } - ); - assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( @@ -117,14 +108,34 @@ pub(super) async fn update_view( overseer_recv(virtual_overseer).await, AllMessages::RuntimeApi(RuntimeApiMessage::Request( parent, + RuntimeApiRequest::SessionIndexForChild(tx) + )) => { + tx.send(Ok(test_state.session_index)).unwrap(); + (parent, new_view.get(&parent).copied().expect("Unknown parent requested")) + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _, RuntimeApiRequest::AsyncBackingParams(tx), )) => { tx.send(Ok(ASYNC_BACKING_PARAMETERS)).unwrap(); - (parent, new_view.get(&parent).copied().expect("Unknown parent requested")) } ); - assert_assign_incoming( + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + _, + RuntimeApiRequest::NodeFeatures(_, tx) + )) => { + tx.send(Ok(test_state.node_features.clone())).unwrap(); + } + ); + + assert_construct_per_relay_parent( virtual_overseer, test_state, leaf_hash, @@ -193,7 +204,7 @@ pub(super) async fn update_view( // Skip the leaf. for (hash, number) in ancestry_iter.skip(1).take(requested_len.saturating_sub(1)) { - assert_assign_incoming( + assert_construct_per_relay_parent( virtual_overseer, test_state, hash, @@ -406,7 +417,7 @@ fn v1_advertisement_accepted_and_seconded() { } #[test] -fn v1_advertisement_rejected_on_non_active_leave() { +fn v1_advertisement_rejected_on_non_active_leaf() { let test_state = TestState::default(); test_harness(ReputationAggregator::new(|_| true), |test_harness| async move { @@ -1314,3 +1325,223 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { virtual_overseer }); } + +#[rstest] +#[case(true)] +#[case(false)] +fn v2_descriptor(#[case] v2_feature_enabled: bool) { + let mut test_state = TestState::default(); + + if !v2_feature_enabled { + test_state.node_features = NodeFeatures::EMPTY; + } + + test_harness(ReputationAggregator::new(|_| true), |test_harness| async move { + let TestHarness { mut virtual_overseer, keystore } = test_harness; + + let pair_a = CollatorPair::generate().0; + + let head_b = Hash::from_low_u64_be(128); + let head_b_num: u32 = 0; + + update_view(&mut virtual_overseer, &test_state, vec![(head_b, head_b_num)], 1).await; + + let peer_a = PeerId::random(); + + connect_and_declare_collator( + &mut virtual_overseer, + peer_a, + pair_a.clone(), + test_state.chain_ids[0], + CollationVersion::V2, + ) + .await; + + let mut committed_candidate = dummy_committed_candidate_receipt_v2(head_b); + committed_candidate.descriptor.set_para_id(test_state.chain_ids[0]); + committed_candidate + .descriptor + .set_persisted_validation_data_hash(dummy_pvd().hash()); + // First para is assigned to core 0. + committed_candidate.descriptor.set_core_index(CoreIndex(0)); + committed_candidate.descriptor.set_session_index(test_state.session_index); + + let candidate: CandidateReceipt = committed_candidate.clone().to_plain(); + let pov = PoV { block_data: BlockData(vec![1]) }; + + let candidate_hash = candidate.hash(); + let parent_head_data_hash = Hash::zero(); + + advertise_collation( + &mut virtual_overseer, + peer_a, + head_b, + Some((candidate_hash, parent_head_data_hash)), + ) + .await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::CandidateBacking( + CandidateBackingMessage::CanSecond(request, tx), + ) => { + assert_eq!(request.candidate_hash, candidate_hash); + assert_eq!(request.candidate_para_id, test_state.chain_ids[0]); + assert_eq!(request.parent_head_data_hash, parent_head_data_hash); + tx.send(true).expect("receiving side should be alive"); + } + ); + + let response_channel = assert_fetch_collation_request( + &mut virtual_overseer, + head_b, + test_state.chain_ids[0], + Some(candidate_hash), + ) + .await; + + response_channel + .send(Ok(( + request_v2::CollationFetchingResponse::Collation(candidate.clone(), pov.clone()) + .encode(), + ProtocolName::from(""), + ))) + .expect("Sending response should succeed"); + + if v2_feature_enabled { + assert_candidate_backing_second( + &mut virtual_overseer, + head_b, + test_state.chain_ids[0], + &pov, + CollationVersion::V2, + ) + .await; + + send_seconded_statement(&mut virtual_overseer, keystore.clone(), &committed_candidate) + .await; + + assert_collation_seconded(&mut virtual_overseer, head_b, peer_a, CollationVersion::V2) + .await; + } else { + // Reported malicious. Used v2 descriptor without the feature being enabled + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::NetworkBridgeTx( + NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(peer_id, rep)), + ) => { + assert_eq!(peer_a, peer_id); + assert_eq!(rep.value, COST_REPORT_BAD.cost_or_benefit()); + } + ); + } + + virtual_overseer + }); +} + +#[test] +fn invalid_v2_descriptor() { + let test_state = TestState::default(); + + test_harness(ReputationAggregator::new(|_| true), |test_harness| async move { + let TestHarness { mut virtual_overseer, .. } = test_harness; + + let pair_a = CollatorPair::generate().0; + + let head_b = Hash::from_low_u64_be(128); + let head_b_num: u32 = 0; + + update_view(&mut virtual_overseer, &test_state, vec![(head_b, head_b_num)], 1).await; + + let peer_a = PeerId::random(); + + connect_and_declare_collator( + &mut virtual_overseer, + peer_a, + pair_a.clone(), + test_state.chain_ids[0], + CollationVersion::V2, + ) + .await; + + let mut candidates = vec![]; + + let mut committed_candidate = dummy_committed_candidate_receipt_v2(head_b); + committed_candidate.descriptor.set_para_id(test_state.chain_ids[0]); + committed_candidate + .descriptor + .set_persisted_validation_data_hash(dummy_pvd().hash()); + // First para is assigned to core 0, set an invalid core index. + committed_candidate.descriptor.set_core_index(CoreIndex(10)); + committed_candidate.descriptor.set_session_index(test_state.session_index); + + candidates.push(committed_candidate.clone()); + + // Invalid session index. + committed_candidate.descriptor.set_core_index(CoreIndex(0)); + committed_candidate.descriptor.set_session_index(10); + + candidates.push(committed_candidate); + + for committed_candidate in candidates { + let candidate: CandidateReceipt = committed_candidate.clone().to_plain(); + let pov = PoV { block_data: BlockData(vec![1]) }; + + let candidate_hash = candidate.hash(); + let parent_head_data_hash = Hash::zero(); + + advertise_collation( + &mut virtual_overseer, + peer_a, + head_b, + Some((candidate_hash, parent_head_data_hash)), + ) + .await; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::CandidateBacking( + CandidateBackingMessage::CanSecond(request, tx), + ) => { + assert_eq!(request.candidate_hash, candidate_hash); + assert_eq!(request.candidate_para_id, test_state.chain_ids[0]); + assert_eq!(request.parent_head_data_hash, parent_head_data_hash); + tx.send(true).expect("receiving side should be alive"); + } + ); + + let response_channel = assert_fetch_collation_request( + &mut virtual_overseer, + head_b, + test_state.chain_ids[0], + Some(candidate_hash), + ) + .await; + + response_channel + .send(Ok(( + request_v2::CollationFetchingResponse::Collation( + candidate.clone(), + pov.clone(), + ) + .encode(), + ProtocolName::from(""), + ))) + .expect("Sending response should succeed"); + + // Reported malicious. Invalid core index + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::NetworkBridgeTx( + NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(peer_id, rep)), + ) => { + assert_eq!(peer_a, peer_id); + assert_eq!(rep.value, COST_REPORT_BAD.cost_or_benefit()); + } + ); + } + + virtual_overseer + }); +} diff --git a/polkadot/node/network/statement-distribution/Cargo.toml b/polkadot/node/network/statement-distribution/Cargo.toml index 08059353033ddc8d0df65abac7f3c877d4f4b0ec..de07937ffb0a5c32f2357781ca675a4ad200d3ab 100644 --- a/polkadot/node/network/statement-distribution/Cargo.toml +++ b/polkadot/node/network/statement-distribution/Cargo.toml @@ -44,6 +44,7 @@ polkadot-primitives = { workspace = true, features = ["test"] } polkadot-primitives-test-helpers = { workspace = true } rand_chacha = { workspace = true, default-features = true } polkadot-subsystem-bench = { workspace = true } +rstest = { workspace = true } [[bench]] name = "statement-distribution-regression-bench" diff --git a/polkadot/node/network/statement-distribution/src/error.rs b/polkadot/node/network/statement-distribution/src/error.rs index d7f52162fe2370481d7dd76717ea0f977be98a24..cff9afbf86675093879072ab4c5e134fdb92571a 100644 --- a/polkadot/node/network/statement-distribution/src/error.rs +++ b/polkadot/node/network/statement-distribution/src/error.rs @@ -72,9 +72,6 @@ pub enum Error { #[error("Fetching session info failed {0:?}")] FetchSessionInfo(RuntimeApiError), - #[error("Fetching availability cores failed {0:?}")] - FetchAvailabilityCores(RuntimeApiError), - #[error("Fetching disabled validators failed {0:?}")] FetchDisabledValidators(runtime::Error), @@ -82,7 +79,7 @@ pub enum Error { FetchValidatorGroups(RuntimeApiError), #[error("Fetching claim queue failed {0:?}")] - FetchClaimQueue(runtime::Error), + FetchClaimQueue(RuntimeApiError), #[error("Attempted to share statement when not a validator or not assigned")] InvalidShare, diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index c79ae3953ad9fdeb9cd75074dfa8596f69a17f35..6bb49e5de13dd35ed0d285dd2789b6f12d9e702c 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -46,12 +46,15 @@ use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, reputation::ReputationAggregator, runtime::{ - fetch_claim_queue, request_min_backing_votes, ClaimQueueSnapshot, ProspectiveParachainsMode, + request_min_backing_votes, request_node_features, ClaimQueueSnapshot, + ProspectiveParachainsMode, }, }; use polkadot_primitives::{ - vstaging::CoreState, AuthorityDiscoveryId, CandidateHash, CompactStatement, CoreIndex, - GroupIndex, GroupRotationInfo, Hash, Id as ParaId, IndexedVec, SessionIndex, SessionInfo, + node_features::FeatureIndex, + vstaging::{transpose_claim_queue, CandidateDescriptorVersion, TransposedClaimQueue}, + AuthorityDiscoveryId, CandidateHash, CompactStatement, CoreIndex, GroupIndex, + GroupRotationInfo, Hash, Id as ParaId, IndexedVec, NodeFeatures, SessionIndex, SessionInfo, SignedStatement, SigningContext, UncheckedSignedStatement, ValidatorId, ValidatorIndex, }; @@ -137,6 +140,12 @@ const COST_UNREQUESTED_RESPONSE_STATEMENT: Rep = Rep::CostMajor("Un-requested Statement In Response"); const COST_INACCURATE_ADVERTISEMENT: Rep = Rep::CostMajor("Peer advertised a candidate inaccurately"); +const COST_UNSUPPORTED_DESCRIPTOR_VERSION: Rep = + Rep::CostMajor("Candidate Descriptor version is not supported"); +const COST_INVALID_CORE_INDEX: Rep = + Rep::CostMajor("Candidate Descriptor contains an invalid core index"); +const COST_INVALID_SESSION_INDEX: Rep = + Rep::CostMajor("Candidate Descriptor contains an invalid session index"); const COST_INVALID_REQUEST: Rep = Rep::CostMajor("Peer sent unparsable request"); const COST_INVALID_REQUEST_BITFIELD_SIZE: Rep = @@ -156,6 +165,7 @@ struct PerRelayParentState { statement_store: StatementStore, seconding_limit: usize, session: SessionIndex, + transposed_cq: TransposedClaimQueue, groups_per_para: HashMap>, disabled_validators: HashSet, } @@ -219,10 +229,17 @@ struct PerSessionState { // getting the topology from the gossip-support subsystem grid_view: Option, local_validator: Option, + // `true` if v2 candidate receipts are allowed by the runtime + allow_v2_descriptors: bool, } impl PerSessionState { - fn new(session_info: SessionInfo, keystore: &KeystorePtr, backing_threshold: u32) -> Self { + fn new( + session_info: SessionInfo, + keystore: &KeystorePtr, + backing_threshold: u32, + allow_v2_descriptors: bool, + ) -> Self { let groups = Groups::new(session_info.validator_groups.clone(), backing_threshold); let mut authority_lookup = HashMap::new(); for (i, ad) in session_info.discovery_keys.iter().cloned().enumerate() { @@ -235,7 +252,14 @@ impl PerSessionState { ) .map(|(_, index)| LocalValidatorIndex::Active(index)); - PerSessionState { session_info, groups, authority_lookup, grid_view: None, local_validator } + PerSessionState { + session_info, + groups, + authority_lookup, + grid_view: None, + local_validator, + allow_v2_descriptors, + } } fn supply_topology( @@ -271,6 +295,11 @@ impl PerSessionState { fn is_not_validator(&self) -> bool { self.grid_view.is_some() && self.local_validator.is_none() } + + /// Returns `true` if v2 candidate receipts are enabled + fn candidate_receipt_v2_enabled(&self) -> bool { + self.allow_v2_descriptors + } } pub(crate) struct State { @@ -615,8 +644,18 @@ pub(crate) async fn handle_active_leaves_update( let minimum_backing_votes = request_min_backing_votes(new_relay_parent, session_index, ctx.sender()).await?; - let mut per_session_state = - PerSessionState::new(session_info, &state.keystore, minimum_backing_votes); + let node_features = + request_node_features(new_relay_parent, session_index, ctx.sender()).await?; + let mut per_session_state = PerSessionState::new( + session_info, + &state.keystore, + minimum_backing_votes, + node_features + .unwrap_or(NodeFeatures::EMPTY) + .get(FeatureIndex::CandidateReceiptV2 as usize) + .map(|b| *b) + .unwrap_or(false), + ); if let Some(topology) = state.unused_topologies.remove(&session_index) { per_session_state.supply_topology(&topology.topology, topology.local_index); } @@ -642,18 +681,6 @@ pub(crate) async fn handle_active_leaves_update( continue } - // New leaf: fetch info from runtime API and initialize - // `per_relay_parent`. - - let availability_cores = polkadot_node_subsystem_util::request_availability_cores( - new_relay_parent, - ctx.sender(), - ) - .await - .await - .map_err(JfyiError::RuntimeApiUnavailable)? - .map_err(JfyiError::FetchAvailabilityCores)?; - let group_rotation_info = polkadot_node_subsystem_util::request_validator_groups(new_relay_parent, ctx.sender()) .await @@ -662,23 +689,22 @@ pub(crate) async fn handle_active_leaves_update( .map_err(JfyiError::FetchValidatorGroups)? .1; - let maybe_claim_queue = fetch_claim_queue(ctx.sender(), new_relay_parent) - .await - .unwrap_or_else(|err| { - gum::debug!(target: LOG_TARGET, ?new_relay_parent, ?err, "handle_active_leaves_update: `claim_queue` API not available"); - None - }); + let claim_queue = ClaimQueueSnapshot( + polkadot_node_subsystem_util::request_claim_queue(new_relay_parent, ctx.sender()) + .await + .await + .map_err(JfyiError::RuntimeApiUnavailable)? + .map_err(JfyiError::FetchClaimQueue)?, + ); let local_validator = per_session.local_validator.and_then(|v| { if let LocalValidatorIndex::Active(idx) = v { find_active_validator_state( idx, &per_session.groups, - &availability_cores, &group_rotation_info, - &maybe_claim_queue, + &claim_queue, seconding_limit, - max_candidate_depth, ) } else { Some(LocalValidatorState { grid_tracker: GridTracker::default(), active: None }) @@ -686,13 +712,14 @@ pub(crate) async fn handle_active_leaves_update( }); let groups_per_para = determine_groups_per_para( - availability_cores, + per_session.groups.all().len(), group_rotation_info, - &maybe_claim_queue, - max_candidate_depth, + &claim_queue, ) .await; + let transposed_cq = transpose_claim_queue(claim_queue.0); + state.per_relay_parent.insert( new_relay_parent, PerRelayParentState { @@ -702,6 +729,7 @@ pub(crate) async fn handle_active_leaves_update( session: session_index, groups_per_para, disabled_validators, + transposed_cq, }, ); } @@ -741,11 +769,9 @@ pub(crate) async fn handle_active_leaves_update( fn find_active_validator_state( validator_index: ValidatorIndex, groups: &Groups, - availability_cores: &[CoreState], group_rotation_info: &GroupRotationInfo, - maybe_claim_queue: &Option, + claim_queue: &ClaimQueueSnapshot, seconding_limit: usize, - max_candidate_depth: usize, ) -> Option { if groups.all().is_empty() { return None @@ -753,23 +779,8 @@ fn find_active_validator_state( let our_group = groups.by_validator_index(validator_index)?; - let core_index = group_rotation_info.core_for_group(our_group, availability_cores.len()); - let paras_assigned_to_core = if let Some(claim_queue) = maybe_claim_queue { - claim_queue.iter_claims_for_core(&core_index).copied().collect() - } else { - availability_cores - .get(core_index.0 as usize) - .and_then(|core_state| match core_state { - CoreState::Scheduled(scheduled_core) => Some(scheduled_core.para_id), - CoreState::Occupied(occupied_core) if max_candidate_depth >= 1 => occupied_core - .next_up_on_available - .as_ref() - .map(|scheduled_core| scheduled_core.para_id), - CoreState::Free | CoreState::Occupied(_) => None, - }) - .into_iter() - .collect() - }; + let core_index = group_rotation_info.core_for_group(our_group, groups.all().len()); + let paras_assigned_to_core = claim_queue.iter_claims_for_core(&core_index).copied().collect(); let group_validators = groups.get(our_group)?.to_owned(); Some(LocalValidatorState { @@ -2174,39 +2185,16 @@ async fn provide_candidate_to_grid( // Utility function to populate per relay parent `ParaId` to `GroupIndex` mappings. async fn determine_groups_per_para( - availability_cores: Vec, + n_cores: usize, group_rotation_info: GroupRotationInfo, - maybe_claim_queue: &Option, - max_candidate_depth: usize, + claim_queue: &ClaimQueueSnapshot, ) -> HashMap> { - let n_cores = availability_cores.len(); - // Determine the core indices occupied by each para at the current relay parent. To support // on-demand parachains we also consider the core indices at next blocks. - let schedule: HashMap> = if let Some(claim_queue) = maybe_claim_queue { - claim_queue - .iter_all_claims() - .map(|(core_index, paras)| (*core_index, paras.iter().copied().collect())) - .collect() - } else { - availability_cores - .into_iter() - .enumerate() - .filter_map(|(index, core)| match core { - CoreState::Scheduled(scheduled_core) => - Some((CoreIndex(index as u32), vec![scheduled_core.para_id])), - CoreState::Occupied(occupied_core) => - if max_candidate_depth >= 1 { - occupied_core.next_up_on_available.map(|scheduled_core| { - (CoreIndex(index as u32), vec![scheduled_core.para_id]) - }) - } else { - None - }, - CoreState::Free => None, - }) - .collect() - }; + let schedule: HashMap> = claim_queue + .iter_all_claims() + .map(|(core_index, paras)| (*core_index, paras.iter().copied().collect())) + .collect(); let mut groups_per_para = HashMap::new(); // Map from `CoreIndex` to `GroupIndex` and collect as `HashMap`. @@ -3106,11 +3094,12 @@ pub(crate) async fn handle_response( ) { let &requests::CandidateIdentifier { relay_parent, candidate_hash, group_index } = response.candidate_identifier(); + let peer = *response.requested_peer(); gum::trace!( target: LOG_TARGET, ?candidate_hash, - peer = ?response.requested_peer(), + ?peer, "Received response", ); @@ -3145,6 +3134,8 @@ pub(crate) async fn handle_response( expected_groups.iter().any(|g| g == &g_index) }, disabled_mask, + &relay_parent_state.transposed_cq, + per_session.candidate_receipt_v2_enabled(), ); for (peer, rep) in res.reputation_changes { diff --git a/polkadot/node/network/statement-distribution/src/v2/requests.rs b/polkadot/node/network/statement-distribution/src/v2/requests.rs index 74f29710956f58f758c94373ebb298b0e604bed7..3b46922c2297255e588a16828e85a596cba5be6b 100644 --- a/polkadot/node/network/statement-distribution/src/v2/requests.rs +++ b/polkadot/node/network/statement-distribution/src/v2/requests.rs @@ -30,9 +30,11 @@ //! (which requires state not owned by the request manager). use super::{ - seconded_and_sufficient, BENEFIT_VALID_RESPONSE, BENEFIT_VALID_STATEMENT, - COST_IMPROPERLY_DECODED_RESPONSE, COST_INVALID_RESPONSE, COST_INVALID_SIGNATURE, - COST_UNREQUESTED_RESPONSE_STATEMENT, REQUEST_RETRY_DELAY, + seconded_and_sufficient, CandidateDescriptorVersion, TransposedClaimQueue, + BENEFIT_VALID_RESPONSE, BENEFIT_VALID_STATEMENT, COST_IMPROPERLY_DECODED_RESPONSE, + COST_INVALID_CORE_INDEX, COST_INVALID_RESPONSE, COST_INVALID_SESSION_INDEX, + COST_INVALID_SIGNATURE, COST_UNREQUESTED_RESPONSE_STATEMENT, + COST_UNSUPPORTED_DESCRIPTOR_VERSION, REQUEST_RETRY_DELAY, }; use crate::LOG_TARGET; @@ -566,6 +568,8 @@ impl UnhandledResponse { validator_key_lookup: impl Fn(ValidatorIndex) -> Option, allowed_para_lookup: impl Fn(ParaId, GroupIndex) -> bool, disabled_mask: BitVec, + transposed_cq: &TransposedClaimQueue, + allow_v2_descriptors: bool, ) -> ResponseValidationOutput { let UnhandledResponse { response: TaggedResponse { identifier, requested_peer, props, response }, @@ -650,6 +654,8 @@ impl UnhandledResponse { validator_key_lookup, allowed_para_lookup, disabled_mask, + transposed_cq, + allow_v2_descriptors, ); if let CandidateRequestStatus::Complete { .. } = output.request_status { @@ -670,6 +676,8 @@ fn validate_complete_response( validator_key_lookup: impl Fn(ValidatorIndex) -> Option, allowed_para_lookup: impl Fn(ParaId, GroupIndex) -> bool, disabled_mask: BitVec, + transposed_cq: &TransposedClaimQueue, + allow_v2_descriptors: bool, ) -> ResponseValidationOutput { let RequestProperties { backing_threshold, mut unwanted_mask } = props; @@ -687,39 +695,83 @@ fn validate_complete_response( unwanted_mask.validated_in_group.resize(group.len(), true); } - let invalid_candidate_output = || ResponseValidationOutput { + let invalid_candidate_output = |cost: Rep| ResponseValidationOutput { request_status: CandidateRequestStatus::Incomplete, - reputation_changes: vec![(requested_peer, COST_INVALID_RESPONSE)], + reputation_changes: vec![(requested_peer, cost)], requested_peer, }; + let mut rep_changes = Vec::new(); + // sanity-check candidate response. // note: roughly ascending cost of operations { if response.candidate_receipt.descriptor.relay_parent() != identifier.relay_parent { - return invalid_candidate_output() + return invalid_candidate_output(COST_INVALID_RESPONSE) } if response.candidate_receipt.descriptor.persisted_validation_data_hash() != response.persisted_validation_data.hash() { - return invalid_candidate_output() + return invalid_candidate_output(COST_INVALID_RESPONSE) } if !allowed_para_lookup( response.candidate_receipt.descriptor.para_id(), identifier.group_index, ) { - return invalid_candidate_output() + return invalid_candidate_output(COST_INVALID_RESPONSE) } if response.candidate_receipt.hash() != identifier.candidate_hash { - return invalid_candidate_output() + return invalid_candidate_output(COST_INVALID_RESPONSE) + } + + let candidate_hash = response.candidate_receipt.hash(); + + // V2 descriptors are invalid if not enabled by runtime. + if !allow_v2_descriptors && + response.candidate_receipt.descriptor.version() == CandidateDescriptorVersion::V2 + { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + peer = ?requested_peer, + "Version 2 candidate receipts are not enabled by the runtime" + ); + return invalid_candidate_output(COST_UNSUPPORTED_DESCRIPTOR_VERSION) + } + // Validate the core index. + if let Err(err) = response.candidate_receipt.check_core_index(transposed_cq) { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + ?err, + peer = ?requested_peer, + "Received candidate has invalid core index" + ); + return invalid_candidate_output(COST_INVALID_CORE_INDEX) + } + + // Check if `session_index` of relay parent matches candidate descriptor + // `session_index`. + if let Some(candidate_session_index) = response.candidate_receipt.descriptor.session_index() + { + if candidate_session_index != session { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + peer = ?requested_peer, + session_index = session, + candidate_session_index, + "Received candidate has invalid session index" + ); + return invalid_candidate_output(COST_INVALID_SESSION_INDEX) + } } } // statement checks. - let mut rep_changes = Vec::new(); let statements = { let mut statements = Vec::with_capacity(std::cmp::min(response.statements.len(), group.len() * 2)); @@ -815,7 +867,7 @@ fn validate_complete_response( // Only accept responses which are sufficient, according to our // required backing threshold. if !seconded_and_sufficient(&received_filter, backing_threshold) { - return invalid_candidate_output() + return invalid_candidate_output(COST_INVALID_RESPONSE) } statements @@ -1091,6 +1143,8 @@ mod tests { validator_key_lookup, allowed_para_lookup, disabled_mask.clone(), + &Default::default(), + false, ); assert_eq!( output, @@ -1130,6 +1184,8 @@ mod tests { validator_key_lookup, allowed_para_lookup, disabled_mask, + &Default::default(), + false, ); assert_eq!( output, @@ -1214,6 +1270,8 @@ mod tests { validator_key_lookup, allowed_para_lookup, disabled_mask, + &Default::default(), + false, ); assert_eq!( output, @@ -1296,6 +1354,8 @@ mod tests { validator_key_lookup, allowed_para_lookup, disabled_mask, + &Default::default(), + false, ); assert_eq!( output, @@ -1434,6 +1494,8 @@ mod tests { validator_key_lookup, allowed_para_lookup, disabled_mask.clone(), + &Default::default(), + false, ); // First request served successfully diff --git a/polkadot/node/network/statement-distribution/src/v2/tests/cluster.rs b/polkadot/node/network/statement-distribution/src/v2/tests/cluster.rs index fe51f953e244a560d7fe2e0bf414279fd0b8bf89..040123f1774cf727341be0bd0a1899b2430b51bd 100644 --- a/polkadot/node/network/statement-distribution/src/v2/tests/cluster.rs +++ b/polkadot/node/network/statement-distribution/src/v2/tests/cluster.rs @@ -25,6 +25,7 @@ fn share_seconded_circulated_to_cluster() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -125,6 +126,7 @@ fn cluster_valid_statement_before_seconded_ignored() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -185,6 +187,7 @@ fn cluster_statement_bad_signature() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -258,6 +261,7 @@ fn useful_cluster_statement_from_non_cluster_peer_rejected() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -320,6 +324,7 @@ fn elastic_scaling_useful_cluster_statement_from_non_cluster_peer_rejected() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -379,6 +384,7 @@ fn statement_from_non_cluster_originator_unexpected() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -434,6 +440,7 @@ fn seconded_statement_leads_to_request() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -522,6 +529,7 @@ fn cluster_statements_shared_seconded_first() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -636,6 +644,7 @@ fn cluster_accounts_for_implicit_view() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -772,6 +781,7 @@ fn cluster_messages_imported_after_confirmed_candidate_importable_check() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -895,6 +905,7 @@ fn cluster_messages_imported_after_new_leaf_importable_check() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -1031,6 +1042,7 @@ fn ensure_seconding_limit_is_respected() { max_candidate_depth: 1, allowed_ancestry_len: 3, }), + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); diff --git a/polkadot/node/network/statement-distribution/src/v2/tests/grid.rs b/polkadot/node/network/statement-distribution/src/v2/tests/grid.rs index d2bf031368c14a13f5da44dd29ba28376109f9bf..0133d9e219f6eafacf0d1f73f6c261eb550a9af2 100644 --- a/polkadot/node/network/statement-distribution/src/v2/tests/grid.rs +++ b/polkadot/node/network/statement-distribution/src/v2/tests/grid.rs @@ -31,6 +31,7 @@ fn backed_candidate_leads_to_advertisement() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -240,6 +241,7 @@ fn received_advertisement_before_confirmation_leads_to_request() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -412,6 +414,7 @@ fn received_advertisement_after_backing_leads_to_acknowledgement() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; test_harness(config, |state, mut overseer| async move { @@ -593,6 +596,7 @@ fn receive_ack_for_unconfirmed_candidate() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; test_harness(config, |state, mut overseer| async move { @@ -654,6 +658,7 @@ fn received_acknowledgements_for_locally_confirmed() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; test_harness(config, |state, mut overseer| async move { @@ -816,6 +821,7 @@ fn received_acknowledgements_for_externally_confirmed() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; test_harness(config, |state, mut overseer| async move { @@ -951,6 +957,7 @@ fn received_advertisement_after_confirmation_before_backing() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -1129,6 +1136,7 @@ fn additional_statements_are_shared_after_manifest_exchange() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -1416,6 +1424,7 @@ fn advertisement_sent_when_peer_enters_relay_parent_view() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -1629,6 +1638,7 @@ fn advertisement_not_re_sent_when_peer_re_enters_view() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -1840,6 +1850,7 @@ fn inner_grid_statements_imported_to_backing(groups_for_first_para: usize) { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -2048,6 +2059,7 @@ fn advertisements_rejected_from_incorrect_peers() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -2184,6 +2196,7 @@ fn manifest_rejected_with_unknown_relay_parent() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -2281,6 +2294,7 @@ fn manifest_rejected_when_not_a_validator() { group_size, local_validator: LocalRole::None, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -2374,6 +2388,7 @@ fn manifest_rejected_when_group_does_not_match_para() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -2472,6 +2487,7 @@ fn peer_reported_for_advertisement_conflicting_with_confirmed_candidate() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -2662,6 +2678,7 @@ fn inactive_local_participates_in_grid() { group_size, local_validator: LocalRole::InactiveValidator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); diff --git a/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs b/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs index 9f2c36ad1018e9d3c22129fc9e1a39a700590181..46b72f5adac9859f05f3ee9733a9b960b991715b 100644 --- a/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/tests/mod.rs @@ -33,9 +33,9 @@ use polkadot_node_subsystem::messages::{ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{ - vstaging::{CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState}, - AssignmentPair, AsyncBackingParams, Block, BlockNumber, GroupRotationInfo, HeadData, Header, - IndexedVec, PersistedValidationData, ScheduledCore, SessionIndex, SessionInfo, ValidatorPair, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, AssignmentPair, + AsyncBackingParams, Block, BlockNumber, GroupRotationInfo, HeadData, Header, IndexedVec, + PersistedValidationData, SessionIndex, SessionInfo, ValidatorPair, }; use sc_keystore::LocalKeystore; use sc_network::ProtocolName; @@ -82,6 +82,8 @@ struct TestConfig { // whether the local node should be a validator local_validator: LocalRole, async_backing_params: Option, + // allow v2 descriptors (feature bit) + allow_v2_descriptors: bool, } #[derive(Debug, Clone)] @@ -96,6 +98,7 @@ struct TestState { validators: Vec, session_info: SessionInfo, req_sender: async_channel::Sender, + node_features: NodeFeatures, } impl TestState { @@ -174,7 +177,13 @@ impl TestState { random_seed: [0u8; 32], }; - TestState { config, local, validators, session_info, req_sender } + let mut node_features = NodeFeatures::new(); + if config.allow_v2_descriptors { + node_features.resize(FeatureIndex::FirstUnassigned as usize, false); + node_features.set(FeatureIndex::CandidateReceiptV2 as usize, true); + } + + TestState { config, local, validators, session_info, req_sender, node_features } } fn make_dummy_leaf(&self, relay_parent: Hash) -> TestLeaf { @@ -186,20 +195,23 @@ impl TestState { relay_parent: Hash, groups_for_first_para: usize, ) -> TestLeaf { + let mut cq = std::collections::BTreeMap::new(); + + for i in 0..self.session_info.validator_groups.len() { + if i < groups_for_first_para { + cq.entry(CoreIndex(i as u32)) + .or_insert_with(|| vec![ParaId::from(0u32), ParaId::from(0u32)].into()); + } else { + cq.entry(CoreIndex(i as u32)) + .or_insert_with(|| vec![ParaId::from(i), ParaId::from(i)].into()); + }; + } + TestLeaf { number: 1, hash: relay_parent, parent_hash: Hash::repeat_byte(0), session: 1, - availability_cores: self.make_availability_cores(|i| { - let para_id = if i < groups_for_first_para { - ParaId::from(0u32) - } else { - ParaId::from(i as u32) - }; - - CoreState::Scheduled(ScheduledCore { para_id, collator: None }) - }), disabled_validators: Default::default(), para_data: (0..self.session_info.validator_groups.len()) .map(|i| { @@ -213,6 +225,7 @@ impl TestState { }) .collect(), minimum_backing_votes: 2, + claim_queue: ClaimQueueSnapshot(cq), } } @@ -232,10 +245,6 @@ impl TestState { TestLeaf { minimum_backing_votes, ..self.make_dummy_leaf(relay_parent) } } - fn make_availability_cores(&self, f: impl Fn(usize) -> CoreState) -> Vec { - (0..self.session_info.validator_groups.len()).map(f).collect() - } - fn make_dummy_topology(&self) -> NewGossipTopology { let validator_count = self.config.validator_count; let is_local_inactive = matches!(self.config.local_validator, LocalRole::InactiveValidator); @@ -423,10 +432,10 @@ struct TestLeaf { hash: Hash, parent_hash: Hash, session: SessionIndex, - availability_cores: Vec, pub disabled_validators: Vec, para_data: Vec<(ParaId, PerParaData)>, minimum_backing_votes: u32, + claim_queue: ClaimQueueSnapshot, } impl TestLeaf { @@ -574,9 +583,9 @@ async fn handle_leaf_activation( parent_hash, para_data, session, - availability_cores, disabled_validators, minimum_backing_votes, + claim_queue, } = leaf; assert_matches!( @@ -623,7 +632,7 @@ async fn handle_leaf_activation( _parent, RuntimeApiRequest::Version(tx), )) => { - tx.send(Ok(RuntimeApiRequest::DISABLED_VALIDATORS_RUNTIME_REQUIREMENT)).unwrap(); + tx.send(Ok(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)).unwrap(); }, AllMessages::RuntimeApi(RuntimeApiMessage::Request( parent, @@ -657,12 +666,6 @@ async fn handle_leaf_activation( assert!(is_new_session, "only expecting this call in a new session"); tx.send(Ok(*minimum_backing_votes)).unwrap(); }, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - parent, - RuntimeApiRequest::AvailabilityCores(tx), - )) if parent == *hash => { - tx.send(Ok(availability_cores.clone())).unwrap(); - }, AllMessages::RuntimeApi(RuntimeApiMessage::Request( parent, RuntimeApiRequest::ValidatorGroups(tx), @@ -675,6 +678,18 @@ async fn handle_leaf_activation( }; tx.send(Ok((validator_groups, group_rotation_info))).unwrap(); }, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + parent, + RuntimeApiRequest::NodeFeatures(_session_index, tx), + )) if parent == *hash => { + tx.send(Ok(test_state.node_features.clone())).unwrap(); + }, + AllMessages::RuntimeApi(RuntimeApiMessage::Request( + parent, + RuntimeApiRequest::ClaimQueue(tx), + )) if parent == *hash => { + tx.send(Ok(claim_queue.0.clone())).unwrap(); + }, AllMessages::ProspectiveParachains( ProspectiveParachainsMessage::GetHypotheticalMembership(req, tx), ) => { diff --git a/polkadot/node/network/statement-distribution/src/v2/tests/requests.rs b/polkadot/node/network/statement-distribution/src/v2/tests/requests.rs index dcb90bacdcde2cfcff065396cb353b95fa504e7c..fc880c1d9a836d9ece1475c687d81b1e5b8fea37 100644 --- a/polkadot/node/network/statement-distribution/src/v2/tests/requests.rs +++ b/polkadot/node/network/statement-distribution/src/v2/tests/requests.rs @@ -21,19 +21,25 @@ use codec::{Decode, Encode}; use polkadot_node_network_protocol::{ request_response::v2 as request_v2, v2::BackedCandidateManifest, }; -use polkadot_primitives_test_helpers::make_candidate; +use polkadot_primitives_test_helpers::{make_candidate, make_candidate_v2}; use sc_network::config::{ IncomingRequest as RawIncomingRequest, OutgoingResponse as RawOutgoingResponse, }; -#[test] -fn cluster_peer_allowed_to_send_incomplete_statements() { +use polkadot_primitives::vstaging::MutateDescriptorV2; +use rstest::rstest; + +#[rstest] +#[case(false)] +#[case(true)] +fn cluster_peer_allowed_to_send_incomplete_statements(#[case] allow_v2_descriptors: bool) { let group_size = 3; let config = TestConfig { validator_count: 20, group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors, }; let relay_parent = Hash::repeat_byte(1); @@ -48,14 +54,28 @@ fn cluster_peer_allowed_to_send_incomplete_statements() { let test_leaf = state.make_dummy_leaf(relay_parent); - let (candidate, pvd) = make_candidate( - relay_parent, - 1, - local_para, - test_leaf.para_data(local_para).head_data.clone(), - vec![4, 5, 6].into(), - Hash::repeat_byte(42).into(), - ); + let (candidate, pvd) = if allow_v2_descriptors { + let (mut candidate, pvd) = make_candidate_v2( + relay_parent, + 1, + local_para, + test_leaf.para_data(local_para).head_data.clone(), + vec![4, 5, 6].into(), + Hash::repeat_byte(42).into(), + ); + candidate.descriptor.set_core_index(CoreIndex(local_group_index.0)); + (candidate, pvd) + } else { + make_candidate( + relay_parent, + 1, + local_para, + test_leaf.para_data(local_para).head_data.clone(), + vec![4, 5, 6].into(), + Hash::repeat_byte(42).into(), + ) + }; + let candidate_hash = candidate.hash(); let other_group_validators = state.group_validators(local_group_index, true); @@ -187,6 +207,7 @@ fn peer_reported_for_providing_statements_meant_to_be_masked_out() { max_candidate_depth: 1, allowed_ancestry_len: 3, }), + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -462,6 +483,7 @@ fn peer_reported_for_not_enough_statements() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -649,6 +671,7 @@ fn peer_reported_for_duplicate_statements() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -802,6 +825,7 @@ fn peer_reported_for_providing_statements_with_invalid_signatures() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -925,6 +949,415 @@ fn peer_reported_for_providing_statements_with_invalid_signatures() { }); } +#[test] +fn peer_reported_for_invalid_v2_descriptor() { + let group_size = 3; + let config = TestConfig { + validator_count: 20, + group_size, + local_validator: LocalRole::Validator, + async_backing_params: None, + allow_v2_descriptors: true, + }; + + let relay_parent = Hash::repeat_byte(1); + let peer_a = PeerId::random(); + let peer_b = PeerId::random(); + let peer_c = PeerId::random(); + + test_harness(config, |state, mut overseer| async move { + let local_validator = state.local.clone().unwrap(); + let local_group_index = local_validator.group_index.unwrap(); + let local_para = ParaId::from(local_group_index.0); + + let test_leaf = state.make_dummy_leaf(relay_parent); + + let (mut candidate, pvd) = make_candidate_v2( + relay_parent, + 1, + local_para, + test_leaf.para_data(local_para).head_data.clone(), + vec![4, 5, 6].into(), + Hash::repeat_byte(42).into(), + ); + + candidate.descriptor.set_core_index(CoreIndex(100)); + + let candidate_hash = candidate.hash(); + + let other_group_validators = state.group_validators(local_group_index, true); + let v_a = other_group_validators[0]; + let v_b = other_group_validators[1]; + let v_c = other_group_validators[1]; + + // peer A is in group, has relay parent in view. + // peer B is in group, has no relay parent in view. + // peer C is not in group, has relay parent in view. + { + connect_peer( + &mut overseer, + peer_a.clone(), + Some(vec![state.discovery_id(other_group_validators[0])].into_iter().collect()), + ) + .await; + + connect_peer( + &mut overseer, + peer_b.clone(), + Some(vec![state.discovery_id(other_group_validators[1])].into_iter().collect()), + ) + .await; + + connect_peer(&mut overseer, peer_c.clone(), None).await; + + send_peer_view_change(&mut overseer, peer_a.clone(), view![relay_parent]).await; + send_peer_view_change(&mut overseer, peer_c.clone(), view![relay_parent]).await; + } + + activate_leaf(&mut overseer, &test_leaf, &state, true, vec![]).await; + + // Peer in cluster sends a statement, triggering a request. + { + let a_seconded = state + .sign_statement( + v_a, + CompactStatement::Seconded(candidate_hash), + &SigningContext { parent_hash: relay_parent, session_index: 1 }, + ) + .as_unchecked() + .clone(); + + send_peer_message( + &mut overseer, + peer_a.clone(), + protocol_v2::StatementDistributionMessage::Statement(relay_parent, a_seconded), + ) + .await; + + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) + if p == peer_a && r == BENEFIT_VALID_STATEMENT_FIRST.into() => { } + ); + } + + // Send a request to peer and mock its response to include a candidate with invalid core + // index. + { + let b_seconded_invalid = state + .sign_statement( + v_b, + CompactStatement::Seconded(candidate_hash), + &SigningContext { parent_hash: relay_parent, session_index: 1 }, + ) + .as_unchecked() + .clone(); + let statements = vec![b_seconded_invalid.clone()]; + + handle_sent_request( + &mut overseer, + peer_a, + candidate_hash, + StatementFilter::blank(group_size), + candidate.clone(), + pvd.clone(), + statements, + ) + .await; + + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) + if p == peer_a && r == COST_INVALID_CORE_INDEX.into() => { } + ); + } + + // Test invalid session index + candidate.descriptor.set_session_index(100); + // Set good core index + candidate.descriptor.set_core_index(CoreIndex(local_group_index.0)); + + let candidate_hash = candidate.hash(); + + // Peer in cluster sends a statement, triggering a request. + { + let a_seconded = state + .sign_statement( + v_a, + CompactStatement::Seconded(candidate_hash), + &SigningContext { parent_hash: relay_parent, session_index: 1 }, + ) + .as_unchecked() + .clone(); + + send_peer_message( + &mut overseer, + peer_a.clone(), + protocol_v2::StatementDistributionMessage::Statement(relay_parent, a_seconded), + ) + .await; + + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) + if p == peer_a && r == BENEFIT_VALID_STATEMENT_FIRST.into() => { } + ); + } + + // Send a request to peer and mock its response to include a candidate with invalid session + // index. + { + let b_seconded_invalid = state + .sign_statement( + v_b, + CompactStatement::Seconded(candidate_hash), + &SigningContext { parent_hash: relay_parent, session_index: 1 }, + ) + .as_unchecked() + .clone(); + let statements = vec![b_seconded_invalid.clone()]; + + handle_sent_request( + &mut overseer, + peer_a, + candidate_hash, + StatementFilter::blank(group_size), + candidate.clone(), + pvd.clone(), + statements, + ) + .await; + + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) + if p == peer_a && r == COST_INVALID_SESSION_INDEX.into() => { } + ); + } + + // Test valid candidate does not lead to punishment + candidate.descriptor.set_session_index(1); + + let candidate_hash = candidate.hash(); + + // Peer in cluster sends a statement, triggering a request. + { + let a_seconded = state + .sign_statement( + v_a, + CompactStatement::Seconded(candidate_hash), + &SigningContext { parent_hash: relay_parent, session_index: 1 }, + ) + .as_unchecked() + .clone(); + + send_peer_message( + &mut overseer, + peer_a.clone(), + protocol_v2::StatementDistributionMessage::Statement(relay_parent, a_seconded), + ) + .await; + + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) + if p == peer_a && r == BENEFIT_VALID_STATEMENT_FIRST.into() => { } + ); + } + + // Send a request to peer and mock its response to include a valid candidate. + { + let b_seconded_invalid = state + .sign_statement( + v_b, + CompactStatement::Seconded(candidate_hash), + &SigningContext { parent_hash: relay_parent, session_index: 1 }, + ) + .as_unchecked() + .clone(); + let statements = vec![b_seconded_invalid.clone()]; + + handle_sent_request( + &mut overseer, + peer_a, + candidate_hash, + StatementFilter::blank(group_size), + candidate.clone(), + pvd.clone(), + statements, + ) + .await; + + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) + if p == peer_a && r == BENEFIT_VALID_STATEMENT.into() => { } + ); + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) + if p == peer_a && r == BENEFIT_VALID_RESPONSE.into() => { } + ); + + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V2(protocol_v2::ValidationProtocol::StatementDistribution( + protocol_v2::StatementDistributionMessage::Statement( + r, + s, + ) + )) + )) => { + assert_eq!(peers, vec![peer_a.clone()]); + assert_eq!(r, relay_parent); + assert_eq!(s.unchecked_payload(), &CompactStatement::Seconded(candidate_hash)); + assert_eq!(s.unchecked_validator_index(), v_c); + } + ); + + answer_expected_hypothetical_membership_request(&mut overseer, vec![]).await; + } + overseer + }); +} + +#[rstest] +#[case(false)] +#[case(true)] +// Test if v2 descriptors are filtered and peers punished if the node feature is disabled. +// Also test if the peer is rewarded for providing v2 descriptor if the node feature is enabled. +fn v2_descriptors_filtered(#[case] allow_v2_descriptors: bool) { + let group_size = 3; + let config = TestConfig { + validator_count: 20, + group_size, + local_validator: LocalRole::Validator, + async_backing_params: None, + allow_v2_descriptors, + }; + + let relay_parent = Hash::repeat_byte(1); + let peer_a = PeerId::random(); + let peer_b = PeerId::random(); + let peer_c = PeerId::random(); + + test_harness(config, |state, mut overseer| async move { + let local_validator = state.local.clone().unwrap(); + let local_group_index = local_validator.group_index.unwrap(); + let local_para = ParaId::from(local_group_index.0); + + let test_leaf = state.make_dummy_leaf(relay_parent); + + let (mut candidate, pvd) = make_candidate_v2( + relay_parent, + 1, + local_para, + test_leaf.para_data(local_para).head_data.clone(), + vec![4, 5, 6].into(), + Hash::repeat_byte(42).into(), + ); + + // Makes the candidate invalid. + candidate.descriptor.set_core_index(CoreIndex(100)); + + let candidate_hash = candidate.hash(); + + let other_group_validators = state.group_validators(local_group_index, true); + let v_a = other_group_validators[0]; + let v_b = other_group_validators[1]; + + // peer A is in group, has relay parent in view. + // peer B is in group, has no relay parent in view. + // peer C is not in group, has relay parent in view. + { + connect_peer( + &mut overseer, + peer_a.clone(), + Some(vec![state.discovery_id(other_group_validators[0])].into_iter().collect()), + ) + .await; + + connect_peer( + &mut overseer, + peer_b.clone(), + Some(vec![state.discovery_id(other_group_validators[1])].into_iter().collect()), + ) + .await; + + connect_peer(&mut overseer, peer_c.clone(), None).await; + + send_peer_view_change(&mut overseer, peer_a.clone(), view![relay_parent]).await; + send_peer_view_change(&mut overseer, peer_c.clone(), view![relay_parent]).await; + } + + activate_leaf(&mut overseer, &test_leaf, &state, true, vec![]).await; + + // Peer in cluster sends a statement, triggering a request. + { + let a_seconded = state + .sign_statement( + v_a, + CompactStatement::Seconded(candidate_hash), + &SigningContext { parent_hash: relay_parent, session_index: 1 }, + ) + .as_unchecked() + .clone(); + + send_peer_message( + &mut overseer, + peer_a.clone(), + protocol_v2::StatementDistributionMessage::Statement(relay_parent, a_seconded), + ) + .await; + + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) + if p == peer_a && r == BENEFIT_VALID_STATEMENT_FIRST.into() => { } + ); + } + + // Send a request to peer and mock its response to include a candidate with invalid core + // index. + { + let b_seconded_invalid = state + .sign_statement( + v_b, + CompactStatement::Seconded(candidate_hash), + &SigningContext { parent_hash: relay_parent, session_index: 1 }, + ) + .as_unchecked() + .clone(); + let statements = vec![b_seconded_invalid.clone()]; + + handle_sent_request( + &mut overseer, + peer_a, + candidate_hash, + StatementFilter::blank(group_size), + candidate.clone(), + pvd.clone(), + statements, + ) + .await; + + let expected_rep_change = if allow_v2_descriptors { + COST_INVALID_CORE_INDEX.into() + } else { + COST_UNSUPPORTED_DESCRIPTOR_VERSION.into() + }; + assert_matches!( + overseer.recv().await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(p, r))) + if p == peer_a && r == expected_rep_change => { } + ); + } + + overseer + }); +} #[test] fn peer_reported_for_providing_statements_with_wrong_validator_id() { let group_size = 3; @@ -933,6 +1366,7 @@ fn peer_reported_for_providing_statements_with_wrong_validator_id() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -1063,6 +1497,7 @@ fn disabled_validators_added_to_unwanted_mask() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -1229,6 +1664,7 @@ fn disabling_works_from_relay_parent_not_the_latest_state() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_1 = Hash::repeat_byte(1); @@ -1428,6 +1864,7 @@ fn local_node_sanity_checks_incoming_requests() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -1629,6 +2066,7 @@ fn local_node_checks_that_peer_can_request_before_responding() { group_size: 3, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -1828,6 +2266,7 @@ fn local_node_respects_statement_mask() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); @@ -2070,6 +2509,7 @@ fn should_delay_before_retrying_dropped_requests() { group_size, local_validator: LocalRole::Validator, async_backing_params: None, + allow_v2_descriptors: false, }; let relay_parent = Hash::repeat_byte(1); diff --git a/polkadot/node/overseer/examples/minimal-example.rs b/polkadot/node/overseer/examples/minimal-example.rs index e1b2af733b47afc2ae2233d766a10a77ddca3896..f2cf60280b7232a4ae92ebaa6edd63aea3a6b836 100644 --- a/polkadot/node/overseer/examples/minimal-example.rs +++ b/polkadot/node/overseer/examples/minimal-example.rs @@ -83,7 +83,7 @@ impl Subsystem1 { candidate_receipt, pov: PoV { block_data: BlockData(Vec::new()) }.into(), executor_params: Default::default(), - exec_kind: PvfExecKind::Backing, + exec_kind: PvfExecKind::Backing(dummy_hash()), response_sender: tx, }; ctx.send_message(msg).await; diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index 87ef63d8a5d70ae8b053a2c8ff2e98bb377fd826..3881ddbcc9046dfb1f805c82a46c824d169e19f6 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -468,6 +468,7 @@ pub async fn forward_events>(client: Arc

, mut hand )] pub struct Overseer { #[subsystem(CandidateValidationMessage, sends: [ + ChainApiMessage, RuntimeApiMessage, ])] candidate_validation: CandidateValidation, diff --git a/polkadot/node/overseer/src/tests.rs b/polkadot/node/overseer/src/tests.rs index c3c47335cd3e2d3b0d07506b4f5ffe334baf7bff..0b9b783ef9b15b92e2993c00e931d846c64542f1 100644 --- a/polkadot/node/overseer/src/tests.rs +++ b/polkadot/node/overseer/src/tests.rs @@ -111,7 +111,7 @@ where candidate_receipt, pov: PoV { block_data: BlockData(Vec::new()) }.into(), executor_params: Default::default(), - exec_kind: PvfExecKind::Backing, + exec_kind: PvfExecKind::Backing(dummy_hash()), response_sender: tx, }) .await; @@ -811,7 +811,7 @@ fn test_candidate_validation_msg() -> CandidateValidationMessage { candidate_receipt, pov, executor_params: Default::default(), - exec_kind: PvfExecKind::Backing, + exec_kind: PvfExecKind::Backing(dummy_hash()), response_sender, } } diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index 3edb3f4dadbed8ac45ebd71814fb552a93227203..6e8eade21a4389ea1815f2e6463722b7ba896823 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -140,7 +140,6 @@ polkadot-node-subsystem-test-helpers = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } sp-tracing = { workspace = true } assert_matches = { workspace = true } -serial_test = { workspace = true } tempfile = { workspace = true } [features] diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index abba91a38a97c43bca584edb38efd00b6d4654d3..d2424474302a6314295f928f110a8e1487aeeab1 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -80,7 +80,7 @@ use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration}; use prometheus_endpoint::Registry; #[cfg(feature = "full-node")] use sc_service::KeystoreContainer; -use sc_service::{build_polkadot_syncing_strategy, RpcHandlers, SpawnTaskHandle}; +use sc_service::{RpcHandlers, SpawnTaskHandle}; use sc_telemetry::TelemetryWorker; #[cfg(feature = "full-node")] use sc_telemetry::{Telemetry, TelemetryWorkerHandle}; @@ -1003,16 +1003,6 @@ pub fn new_full< }) }; - let syncing_strategy = build_polkadot_syncing_strategy( - config.protocol_id(), - config.chain_spec.fork_id(), - &mut net_config, - Some(WarpSyncConfig::WithProvider(warp_sync)), - client.clone(), - &task_manager.spawn_handle(), - config.prometheus_config.as_ref().map(|config| &config.registry), - )?; - let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -1022,7 +1012,7 @@ pub fn new_full< spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - syncing_strategy, + warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), block_relay: None, metrics, })?; diff --git a/polkadot/node/service/src/workers.rs b/polkadot/node/service/src/workers.rs index b35bb8302fdc4405f542fd0cc119066653c76cc9..73c3aa466608f108e3fc245e91dcfcf800692813 100644 --- a/polkadot/node/service/src/workers.rs +++ b/polkadot/node/service/src/workers.rs @@ -21,19 +21,20 @@ use is_executable::IsExecutable; use std::path::PathBuf; #[cfg(test)] -use std::sync::{Mutex, OnceLock}; +thread_local! { + static TMP_DIR: std::cell::RefCell> = std::cell::RefCell::new(None); +} /// Override the workers polkadot binary directory path, used for testing. #[cfg(test)] -fn workers_exe_path_override() -> &'static Mutex> { - static OVERRIDE: OnceLock>> = OnceLock::new(); - OVERRIDE.get_or_init(|| Mutex::new(None)) +fn workers_exe_path_override() -> Option { + TMP_DIR.with_borrow(|t| t.as_ref().map(|t| t.path().join("usr/bin"))) } + /// Override the workers lib directory path, used for testing. #[cfg(test)] -fn workers_lib_path_override() -> &'static Mutex> { - static OVERRIDE: OnceLock>> = OnceLock::new(); - OVERRIDE.get_or_init(|| Mutex::new(None)) +fn workers_lib_path_override() -> Option { + TMP_DIR.with_borrow(|t| t.as_ref().map(|t| t.path().join("usr/lib/polkadot"))) } /// Determines the final set of paths to use for the PVF workers. @@ -147,12 +148,9 @@ fn list_workers_paths( // Consider the /usr/lib/polkadot/ directory. { - #[allow(unused_mut)] - let mut lib_path = PathBuf::from("/usr/lib/polkadot"); + let lib_path = PathBuf::from("/usr/lib/polkadot"); #[cfg(test)] - if let Some(ref path_override) = *workers_lib_path_override().lock().unwrap() { - lib_path = path_override.clone(); - } + let lib_path = if let Some(o) = workers_lib_path_override() { o } else { lib_path }; let (prep_worker, exec_worker) = build_worker_paths(lib_path, workers_names); @@ -175,9 +173,10 @@ fn get_exe_path() -> Result { let mut exe_path = std::env::current_exe()?; let _ = exe_path.pop(); // executable file will always have a parent directory. #[cfg(test)] - if let Some(ref path_override) = *workers_exe_path_override().lock().unwrap() { - exe_path = path_override.clone(); + if let Some(o) = workers_exe_path_override() { + exe_path = o; } + Ok(exe_path) } @@ -205,8 +204,7 @@ mod tests { use super::*; use assert_matches::assert_matches; - use serial_test::serial; - use std::{env::temp_dir, fs, os::unix::fs::PermissionsExt, path::Path}; + use std::{fs, os::unix::fs::PermissionsExt, path::Path}; const TEST_NODE_VERSION: &'static str = "v0.1.2"; @@ -228,7 +226,7 @@ mod tests { fn get_program(version: &str) -> String { format!( - "#!/bin/bash + "#!/usr/bin/env bash if [[ $# -ne 1 ]] ; then echo \"unexpected number of arguments: $#\" @@ -253,27 +251,21 @@ echo {} ) -> Result<(), Box> { // Set up /usr/lib/polkadot and /usr/bin, both empty. - let tempdir = temp_dir(); - let lib_path = tempdir.join("usr/lib/polkadot"); - let _ = fs::remove_dir_all(&lib_path); - fs::create_dir_all(&lib_path)?; - *workers_lib_path_override().lock()? = Some(lib_path); + let tempdir = tempfile::tempdir().unwrap(); + let tmp_dir = tempdir.path().to_path_buf(); + TMP_DIR.with_borrow_mut(|t| *t = Some(tempdir)); - let exe_path = tempdir.join("usr/bin"); - let _ = fs::remove_dir_all(&exe_path); - fs::create_dir_all(&exe_path)?; - *workers_exe_path_override().lock()? = Some(exe_path.clone()); + fs::create_dir_all(workers_lib_path_override().unwrap()).unwrap(); + fs::create_dir_all(workers_exe_path_override().unwrap()).unwrap(); + let custom_path = tmp_dir.join("usr/local/bin"); // Set up custom path at /usr/local/bin. - let custom_path = tempdir.join("usr/local/bin"); - let _ = fs::remove_dir_all(&custom_path); - fs::create_dir_all(&custom_path)?; + fs::create_dir_all(&custom_path).unwrap(); - f(tempdir, exe_path) + f(tmp_dir, workers_exe_path_override().unwrap()) } #[test] - #[serial] fn test_given_worker_path() { with_temp_dir_structure(|tempdir, exe_path| { let given_workers_path = tempdir.join("usr/local/bin"); @@ -318,7 +310,6 @@ echo {} } #[test] - #[serial] fn missing_workers_paths_throws_error() { with_temp_dir_structure(|tempdir, exe_path| { // Try with both binaries missing. @@ -368,7 +359,6 @@ echo {} } #[test] - #[serial] fn should_find_workers_at_all_locations() { with_temp_dir_structure(|tempdir, _| { let prepare_worker_bin_path = tempdir.join("usr/bin/polkadot-prepare-worker"); @@ -394,7 +384,6 @@ echo {} } #[test] - #[serial] fn should_find_workers_with_custom_names_at_all_locations() { with_temp_dir_structure(|tempdir, _| { let (prep_worker_name, exec_worker_name) = ("test-prepare", "test-execute"); @@ -422,7 +411,6 @@ echo {} } #[test] - #[serial] fn workers_version_mismatch_throws_error() { let bad_version = "v9.9.9.9"; @@ -474,7 +462,6 @@ echo {} } #[test] - #[serial] fn should_find_valid_workers() { // Test bin location. with_temp_dir_structure(|tempdir, _| { diff --git a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs index 8ac9796acb62ceb5605388e926df9b49c3813cc5..23dc6bd1caf9e391a51c3e3f1c8282ff88c6dbbc 100644 --- a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs @@ -22,9 +22,7 @@ use crate::{ av_store::{MockAvailabilityStore, NetworkAvailabilityState}, chain_api::{ChainApiState, MockChainApi}, network_bridge::{self, MockNetworkBridgeRx, MockNetworkBridgeTx}, - runtime_api::{ - node_features_with_chunk_mapping_enabled, MockRuntimeApi, MockRuntimeApiCoreState, - }, + runtime_api::{default_node_features, MockRuntimeApi, MockRuntimeApiCoreState}, AlwaysSupportsParachains, }, network::new_network, @@ -394,7 +392,7 @@ pub async fn benchmark_availability_write( expected_erasure_root: backed_candidate.descriptor().erasure_root(), tx, core_index: CoreIndex(core_index as u32), - node_features: node_features_with_chunk_mapping_enabled(), + node_features: default_node_features(), }, )) .await; diff --git a/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs b/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs index 511795970e6cf4e4e7a829c5696e0f88a7dfc325..764572ffe192f576b7d18ca9986e1f01ff73df82 100644 --- a/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs @@ -17,7 +17,7 @@ use crate::{ configuration::{TestAuthorities, TestConfiguration}, environment::GENESIS_HASH, - mock::runtime_api::node_features_with_chunk_mapping_enabled, + mock::runtime_api::default_node_features, }; use bitvec::bitvec; use codec::Encode; @@ -118,7 +118,7 @@ impl TestState { test_state.chunk_indices = (0..config.n_cores) .map(|core_index| { availability_chunk_indices( - Some(&node_features_with_chunk_mapping_enabled()), + Some(&default_node_features()), config.n_validators, CoreIndex(core_index as u32), ) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs index 6c54d14448a1f0d476774a85929dbf135e9ddd9e..69e838eb864eb96103f4e02eadb48289e5e38aec 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -28,12 +28,13 @@ use polkadot_node_subsystem_types::OverseerSignal; use polkadot_primitives::{ node_features, vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt, CoreState, OccupiedCore}, - ApprovalVotingParams, AsyncBackingParams, GroupIndex, GroupRotationInfo, IndexedVec, - NodeFeatures, ScheduledCore, SessionIndex, SessionInfo, ValidationCode, ValidatorIndex, + ApprovalVotingParams, AsyncBackingParams, CoreIndex, GroupIndex, GroupRotationInfo, + Id as ParaId, IndexedVec, NodeFeatures, ScheduledCore, SessionIndex, SessionInfo, + ValidationCode, ValidatorIndex, }; use sp_consensus_babe::Epoch as BabeEpoch; use sp_core::H256; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap, VecDeque}; const LOG_TARGET: &str = "subsystem-bench::runtime-api-mock"; @@ -51,6 +52,8 @@ pub struct RuntimeApiState { babe_epoch: Option, // The session child index, session_index: SessionIndex, + // The claim queue + claim_queue: BTreeMap>, } #[derive(Clone)] @@ -80,7 +83,25 @@ impl MockRuntimeApi { core_state: MockRuntimeApiCoreState, ) -> MockRuntimeApi { // Enable chunk mapping feature to make systematic av-recovery possible. - let node_features = node_features_with_chunk_mapping_enabled(); + let node_features = default_node_features(); + let validator_group_count = + session_info_for_peers(&config, &authorities).validator_groups.len(); + + // Each para gets one core assigned and there is only one candidate per + // parachain per relay chain block (no elastic scaling). + let claim_queue = candidate_hashes + .iter() + .next() + .expect("Candidates are generated at test start") + .1 + .iter() + .enumerate() + .map(|(index, candidate_receipt)| { + // Ensure test breaks if badly configured. + assert!(index < validator_group_count); + (CoreIndex(index as u32), vec![candidate_receipt.descriptor.para_id()].into()) + }) + .collect(); Self { state: RuntimeApiState { @@ -90,6 +111,7 @@ impl MockRuntimeApi { babe_epoch, session_index, node_features, + claim_queue, }, config, core_state, @@ -305,6 +327,9 @@ impl MockRuntimeApi { if let Err(err) = tx.send(Ok(ApprovalVotingParams::default())) { gum::error!(target: LOG_TARGET, ?err, "Voting params weren't received"); }, + RuntimeApiMessage::Request(_parent, RuntimeApiRequest::ClaimQueue(tx)) => { + tx.send(Ok(self.state.claim_queue.clone())).unwrap(); + }, // Long term TODO: implement more as needed. message => { unimplemented!("Unexpected runtime-api message: {:?}", message) @@ -316,9 +341,12 @@ impl MockRuntimeApi { } } -pub fn node_features_with_chunk_mapping_enabled() -> NodeFeatures { +pub fn default_node_features() -> NodeFeatures { let mut node_features = NodeFeatures::new(); - node_features.resize(node_features::FeatureIndex::AvailabilityChunkMapping as usize + 1, false); + node_features.resize(node_features::FeatureIndex::FirstUnassigned as usize, false); node_features.set(node_features::FeatureIndex::AvailabilityChunkMapping as u8 as usize, true); + node_features.set(node_features::FeatureIndex::ElasticScalingMVP as u8 as usize, true); + node_features.set(node_features::FeatureIndex::CandidateReceiptV2 as u8 as usize, true); + node_features } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 2d2e9434b767a00b0a71b47eee7dbf5e0fcc252b..e9b586522d2bcedb120d75f07fa2c8921db363fc 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -45,8 +45,9 @@ use polkadot_primitives::{ CandidateReceiptV2 as CandidateReceipt, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, MutateDescriptorV2, }, - BlockNumber, CandidateHash, CompactStatement, Hash, Header, Id, PersistedValidationData, - SessionInfo, SignedStatement, SigningContext, UncheckedSigned, ValidatorIndex, ValidatorPair, + BlockNumber, CandidateHash, CompactStatement, CoreIndex, Hash, Header, Id, + PersistedValidationData, SessionInfo, SignedStatement, SigningContext, UncheckedSigned, + ValidatorIndex, ValidatorPair, }; use polkadot_primitives_test_helpers::{ dummy_committed_candidate_receipt_v2, dummy_hash, dummy_head_data, dummy_pvd, @@ -61,6 +62,8 @@ use std::{ }, }; +const SESSION_INDEX: u32 = 0; + #[derive(Clone)] pub struct TestState { // Full test config @@ -130,6 +133,8 @@ impl TestState { let mut receipt = receipt_templates[candidate_index].clone(); receipt.descriptor.set_para_id(Id::new(core_idx as u32 + 1)); receipt.descriptor.set_relay_parent(block_info.hash); + receipt.descriptor.set_core_index(CoreIndex(core_idx as u32)); + receipt.descriptor.set_session_index(SESSION_INDEX); state.candidate_receipts.entry(block_info.hash).or_default().push( CandidateReceipt { @@ -193,7 +198,7 @@ fn sign_statement( validator_index: ValidatorIndex, pair: &ValidatorPair, ) -> UncheckedSigned { - let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; + let context = SigningContext { parent_hash: relay_parent, session_index: SESSION_INDEX }; let payload = statement.signing_payload(&context); SignedStatement::new( @@ -320,7 +325,8 @@ impl HandleNetworkMessage for TestState { } let statement = CompactStatement::Valid(candidate_hash); - let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; + let context = + SigningContext { parent_hash: relay_parent, session_index: SESSION_INDEX }; let payload = statement.signing_payload(&context); let pair = self.test_authorities.validator_pairs.get(index).unwrap(); let signature = pair.sign(&payload[..]); diff --git a/polkadot/node/subsystem-types/Cargo.toml b/polkadot/node/subsystem-types/Cargo.toml index b8bad8f8a2953a8327ff79f656f3af83ddb638ea..b5686ec96be127b9c3f33151278366844a362648 100644 --- a/polkadot/node/subsystem-types/Cargo.toml +++ b/polkadot/node/subsystem-types/Cargo.toml @@ -32,4 +32,3 @@ prometheus-endpoint = { workspace = true, default-features = true } thiserror = { workspace = true } async-trait = { workspace = true } bitvec = { features = ["alloc"], workspace = true } -strum = { features = ["derive"], workspace = true, default-features = true } diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index ba1ba5755be0fe53de9e07b287c84e182968bbc8..28a3a1ab82ab20bdb92e023e56a93ca12b846391 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -24,7 +24,6 @@ use futures::channel::oneshot; use sc_network::{Multiaddr, ReputationChange}; -use strum::EnumIter; use thiserror::Error; pub use sc_network::IfDisconnected; @@ -189,18 +188,16 @@ pub enum CandidateValidationMessage { /// Extends primitives::PvfExecKind, which is a runtime parameter we don't want to change, /// to separate and prioritize execution jobs by request type. -/// The order is important, because we iterate through the values and assume it is going from higher -/// to lowest priority. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, EnumIter)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PvfExecKind { /// For dispute requests Dispute, /// For approval requests Approval, - /// For backing requests from system parachains. - BackingSystemParas, - /// For backing requests. - Backing, + /// For backing requests from system parachains. With relay parent hash + BackingSystemParas(Hash), + /// For backing requests. With relay parent hash + Backing(Hash), } impl PvfExecKind { @@ -209,8 +206,8 @@ impl PvfExecKind { match *self { Self::Dispute => "dispute", Self::Approval => "approval", - Self::BackingSystemParas => "backing_system_paras", - Self::Backing => "backing", + Self::BackingSystemParas(_) => "backing_system_paras", + Self::Backing(_) => "backing", } } } @@ -220,8 +217,8 @@ impl From for RuntimePvfExecKind { match exec { PvfExecKind::Dispute => RuntimePvfExecKind::Approval, PvfExecKind::Approval => RuntimePvfExecKind::Approval, - PvfExecKind::BackingSystemParas => RuntimePvfExecKind::Backing, - PvfExecKind::Backing => RuntimePvfExecKind::Backing, + PvfExecKind::BackingSystemParas(_) => RuntimePvfExecKind::Backing, + PvfExecKind::Backing(_) => RuntimePvfExecKind::Backing, } } } diff --git a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs index a2a6095b765cd89c84befe67876f978cc0ed890c..48d3f27b1fa6d0b99a30515de26ffaa3e793ce1e 100644 --- a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs +++ b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs @@ -82,9 +82,9 @@ /// in practice at most once every few weeks. use polkadot_node_subsystem::messages::HypotheticalCandidate; use polkadot_primitives::{ - async_backing::Constraints as PrimitiveConstraints, BlockNumber, CandidateCommitments, - CandidateHash, Hash, HeadData, Id as ParaId, PersistedValidationData, UpgradeRestriction, - ValidationCodeHash, + async_backing::Constraints as PrimitiveConstraints, vstaging::skip_ump_signals, BlockNumber, + CandidateCommitments, CandidateHash, Hash, HeadData, Id as ParaId, PersistedValidationData, + UpgradeRestriction, ValidationCodeHash, }; use std::{collections::HashMap, sync::Arc}; @@ -431,9 +431,9 @@ pub struct ConstraintModifications { pub hrmp_watermark: Option, /// Outbound HRMP channel modifications. pub outbound_hrmp: HashMap, - /// The amount of UMP messages sent. + /// The amount of UMP XCM messages sent. `UMPSignal` and separator are excluded. pub ump_messages_sent: usize, - /// The amount of UMP bytes sent. + /// The amount of UMP XCM bytes sent. `UMPSignal` and separator are excluded. pub ump_bytes_sent: usize, /// The amount of DMP messages processed. pub dmp_messages_processed: usize, @@ -600,6 +600,13 @@ impl Fragment { validation_code_hash: &ValidationCodeHash, persisted_validation_data: &PersistedValidationData, ) -> Result { + // Filter UMP signals and the separator. + let upward_messages = + skip_ump_signals(commitments.upward_messages.iter()).collect::>(); + + let ump_messages_sent = upward_messages.len(); + let ump_bytes_sent = upward_messages.iter().map(|msg| msg.len()).sum(); + let modifications = { ConstraintModifications { required_parent: Some(commitments.head_data.clone()), @@ -632,8 +639,8 @@ impl Fragment { outbound_hrmp }, - ump_messages_sent: commitments.upward_messages.len(), - ump_bytes_sent: commitments.upward_messages.iter().map(|msg| msg.len()).sum(), + ump_messages_sent, + ump_bytes_sent, dmp_messages_processed: commitments.processed_downward_messages as _, code_upgrade_applied: operating_constraints .future_validation_code @@ -750,7 +757,7 @@ fn validate_against_constraints( }) } - if commitments.upward_messages.len() > constraints.max_ump_num_per_candidate { + if modifications.ump_messages_sent > constraints.max_ump_num_per_candidate { return Err(FragmentValidityError::UmpMessagesPerCandidateOverflow { messages_allowed: constraints.max_ump_num_per_candidate, messages_submitted: commitments.upward_messages.len(), @@ -814,7 +821,11 @@ impl HypotheticalOrConcreteCandidate for HypotheticalCandidate { #[cfg(test)] mod tests { use super::*; - use polkadot_primitives::{HorizontalMessages, OutboundHrmpMessage, ValidationCode}; + use codec::Encode; + use polkadot_primitives::{ + vstaging::{ClaimQueueOffset, CoreSelector, UMPSignal, UMP_SEPARATOR}, + HorizontalMessages, OutboundHrmpMessage, ValidationCode, + }; #[test] fn stack_modifications() { @@ -1267,6 +1278,35 @@ mod tests { ); } + #[test] + fn ump_signals_ignored() { + let relay_parent = RelayChainBlockInfo { + number: 6, + hash: Hash::repeat_byte(0xbe), + storage_root: Hash::repeat_byte(0xff), + }; + + let constraints = make_constraints(); + let mut candidate = make_candidate(&constraints, &relay_parent); + let max_ump = constraints.max_ump_num_per_candidate; + + // Fill ump queue to the limit. + candidate + .commitments + .upward_messages + .try_extend((0..max_ump).map(|i| vec![i as u8])) + .unwrap(); + + // Add ump signals. + candidate.commitments.upward_messages.force_push(UMP_SEPARATOR); + candidate + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + Fragment::new(relay_parent, constraints, Arc::new(candidate)).unwrap(); + } + #[test] fn fragment_relay_parent_too_old() { let relay_parent = RelayChainBlockInfo { diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index a8cd6cb5f4e00c36a3236f58c3ec72922d8b4d43..dd269caa2d607ccc9721022d63f35d1029d15063 100644 --- a/polkadot/primitives/Cargo.toml +++ b/polkadot/primitives/Cargo.toml @@ -16,6 +16,7 @@ codec = { features = ["bit-vec", "derive"], workspace = true } scale-info = { features = ["bit-vec", "derive", "serde"], workspace = true } log = { workspace = true } serde = { features = ["alloc", "derive"], workspace = true } +thiserror = { workspace = true, optional = true } sp-application-crypto = { features = ["serde"], workspace = true } sp-inherents = { workspace = true } @@ -59,6 +60,7 @@ std = [ "sp-runtime/std", "sp-staking/std", "sp-std/std", + "thiserror", ] runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 21aab41902be42296cc3205abd8705b41a287e9d..271f78efe090112993329fd7952184909b15058e 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -39,6 +39,10 @@ use sp_staking::SessionIndex; /// Async backing primitives pub mod async_backing; +/// The default claim queue offset to be used if it's not configured/accessible in the parachain +/// runtime +pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 0; + /// A type representing the version of the candidate descriptor and internal version number. #[derive(PartialEq, Eq, Encode, Decode, Clone, TypeInfo, RuntimeDebug, Copy)] #[cfg_attr(feature = "std", derive(Hash))] @@ -204,6 +208,8 @@ pub trait MutateDescriptorV2 { fn set_version(&mut self, version: InternalVersion); /// Set the PVD of the descriptor. fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash); + /// Set the validation code hash of the descriptor. + fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash); /// Set the erasure root of the descriptor. fn set_erasure_root(&mut self, erasure_root: Hash); /// Set the para head of the descriptor. @@ -244,6 +250,10 @@ impl MutateDescriptorV2 for CandidateDescriptorV2 { self.persisted_validation_data_hash = persisted_validation_data_hash; } + fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash) { + self.validation_code_hash = validation_code_hash; + } + fn set_erasure_root(&mut self, erasure_root: Hash) { self.erasure_root = erasure_root; } @@ -424,61 +434,77 @@ pub enum UMPSignal { /// Separator between `XCM` and `UMPSignal`. pub const UMP_SEPARATOR: Vec = vec![]; -impl CandidateCommitments { - /// Returns the core selector and claim queue offset the candidate has committed to, if any. - pub fn selected_core(&self) -> Option<(CoreSelector, ClaimQueueOffset)> { - // We need at least 2 messages for the separator and core selector - if self.upward_messages.len() < 2 { - return None - } - - let separator_pos = - self.upward_messages.iter().rposition(|message| message == &UMP_SEPARATOR)?; - - // Use first commitment - let message = self.upward_messages.get(separator_pos + 1)?; +/// Utility function for skipping the ump signals. +pub fn skip_ump_signals<'a>( + upward_messages: impl Iterator>, +) -> impl Iterator> { + upward_messages.take_while(|message| *message != &UMP_SEPARATOR) +} - match UMPSignal::decode(&mut message.as_slice()).ok()? { - UMPSignal::SelectCore(core_selector, cq_offset) => Some((core_selector, cq_offset)), - } - } +impl CandidateCommitments { + /// Returns the core selector and claim queue offset determined by `UMPSignal::SelectCore` + /// commitment, if present. + pub fn core_selector( + &self, + ) -> Result, CommittedCandidateReceiptError> { + let mut signals_iter = + self.upward_messages.iter().skip_while(|message| *message != &UMP_SEPARATOR); + + if signals_iter.next().is_some() { + let Some(core_selector_message) = signals_iter.next() else { return Ok(None) }; + // We should have exactly one signal beyond the separator + if signals_iter.next().is_some() { + return Err(CommittedCandidateReceiptError::TooManyUMPSignals) + } - /// Returns the core index determined by `UMPSignal::SelectCore` commitment - /// and `assigned_cores`. - /// - /// Returns `None` if there is no `UMPSignal::SelectCore` commitment or - /// assigned cores is empty. - /// - /// `assigned_cores` must be a sorted vec of all core indices assigned to a parachain. - pub fn committed_core_index(&self, assigned_cores: &[&CoreIndex]) -> Option { - if assigned_cores.is_empty() { - return None + match UMPSignal::decode(&mut core_selector_message.as_slice()) + .map_err(|_| CommittedCandidateReceiptError::UmpSignalDecode)? + { + UMPSignal::SelectCore(core_index_selector, cq_offset) => + Ok(Some((core_index_selector, cq_offset))), + } + } else { + Ok(None) } - - self.selected_core().and_then(|(core_selector, _cq_offset)| { - let core_index = - **assigned_cores.get(core_selector.0 as usize % assigned_cores.len())?; - Some(core_index) - }) } } -/// CandidateReceipt construction errors. +/// CommittedCandidateReceiptError construction errors. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] -pub enum CandidateReceiptError { +#[cfg_attr(feature = "std", derive(thiserror::Error))] +pub enum CommittedCandidateReceiptError { /// The specified core index is invalid. + #[cfg_attr(feature = "std", error("The specified core index is invalid"))] InvalidCoreIndex, /// The core index in commitments doesn't match the one in descriptor + #[cfg_attr( + feature = "std", + error("The core index in commitments doesn't match the one in descriptor") + )] CoreIndexMismatch, /// The core selector or claim queue offset is invalid. + #[cfg_attr(feature = "std", error("The core selector or claim queue offset is invalid"))] InvalidSelectedCore, + #[cfg_attr(feature = "std", error("Could not decode UMP signal"))] + /// Could not decode UMP signal. + UmpSignalDecode, /// The parachain is not assigned to any core at specified claim queue offset. + #[cfg_attr( + feature = "std", + error("The parachain is not assigned to any core at specified claim queue offset") + )] NoAssignment, /// No core was selected. The `SelectCore` commitment is mandatory for /// v2 receipts if parachains has multiple cores assigned. + #[cfg_attr(feature = "std", error("Core selector not present"))] NoCoreSelected, /// Unknown version. + #[cfg_attr(feature = "std", error("Unknown internal version"))] UnknownVersion(InternalVersion), + /// The allowed number of `UMPSignal` messages in the queue was exceeded. + /// Currenly only one such message is allowed. + #[cfg_attr(feature = "std", error("Too many UMP signals"))] + TooManyUMPSignals, } macro_rules! impl_getter { @@ -571,57 +597,63 @@ impl CandidateDescriptorV2 { impl CommittedCandidateReceiptV2 { /// Checks if descriptor core index is equal to the committed core index. - /// Input `cores_per_para` is a claim queue snapshot stored as a mapping - /// between `ParaId` and the cores assigned per depth. + /// Input `cores_per_para` is a claim queue snapshot at the candidate's relay parent, stored as + /// a mapping between `ParaId` and the cores assigned per depth. pub fn check_core_index( &self, cores_per_para: &TransposedClaimQueue, - ) -> Result<(), CandidateReceiptError> { + ) -> Result<(), CommittedCandidateReceiptError> { match self.descriptor.version() { // Don't check v1 descriptors. CandidateDescriptorVersion::V1 => return Ok(()), CandidateDescriptorVersion::V2 => {}, CandidateDescriptorVersion::Unknown => - return Err(CandidateReceiptError::UnknownVersion(self.descriptor.version)), - } - - if cores_per_para.is_empty() { - return Err(CandidateReceiptError::NoAssignment) + return Err(CommittedCandidateReceiptError::UnknownVersion(self.descriptor.version)), } - let (offset, core_selected) = - if let Some((_core_selector, cq_offset)) = self.commitments.selected_core() { - (cq_offset.0, true) - } else { - // If no core has been selected then we use offset 0 (top of claim queue) - (0, false) - }; + let (maybe_core_index_selector, cq_offset) = self.commitments.core_selector()?.map_or_else( + || (None, ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)), + |(sel, off)| (Some(sel), off), + ); - // The cores assigned to the parachain at above computed offset. let assigned_cores = cores_per_para .get(&self.descriptor.para_id()) - .ok_or(CandidateReceiptError::NoAssignment)? - .get(&offset) - .ok_or(CandidateReceiptError::NoAssignment)? - .into_iter() - .collect::>(); - - let core_index = if core_selected { - self.commitments - .committed_core_index(assigned_cores.as_slice()) - .ok_or(CandidateReceiptError::NoAssignment)? - } else { - // `SelectCore` commitment is mandatory for elastic scaling parachains. - if assigned_cores.len() > 1 { - return Err(CandidateReceiptError::NoCoreSelected) - } + .ok_or(CommittedCandidateReceiptError::NoAssignment)? + .get(&cq_offset.0) + .ok_or(CommittedCandidateReceiptError::NoAssignment)?; - **assigned_cores.get(0).ok_or(CandidateReceiptError::NoAssignment)? - }; + if assigned_cores.is_empty() { + return Err(CommittedCandidateReceiptError::NoAssignment) + } let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32); + + let core_index_selector = if let Some(core_index_selector) = maybe_core_index_selector { + // We have a committed core selector, we can use it. + core_index_selector + } else if assigned_cores.len() > 1 { + // We got more than one assigned core and no core selector. Special care is needed. + if !assigned_cores.contains(&descriptor_core_index) { + // core index in the descriptor is not assigned to the para. Error. + return Err(CommittedCandidateReceiptError::InvalidCoreIndex) + } else { + // the descriptor core index is indeed assigned to the para. This is the most we can + // check for now + return Ok(()) + } + } else { + // No core selector but there's only one assigned core, use it. + CoreSelector(0) + }; + + let core_index = assigned_cores + .iter() + .nth(core_index_selector.0 as usize % assigned_cores.len()) + .ok_or(CommittedCandidateReceiptError::InvalidSelectedCore) + .copied()?; + if core_index != descriptor_core_index { - return Err(CandidateReceiptError::CoreIndexMismatch) + return Err(CommittedCandidateReceiptError::CoreIndexMismatch) } Ok(()) @@ -1018,7 +1050,7 @@ mod tests { assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown); assert_eq!( new_ccr.check_core_index(&BTreeMap::new()), - Err(CandidateReceiptError::UnknownVersion(InternalVersion(100))) + Err(CommittedCandidateReceiptError::UnknownVersion(InternalVersion(100))) ) } @@ -1056,7 +1088,6 @@ mod tests { new_ccr.descriptor.core_index = 0; new_ccr.descriptor.para_id = ParaId::new(1000); - new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); let mut cq = BTreeMap::new(); @@ -1070,7 +1101,14 @@ mod tests { new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode()); // No `SelectCore` can be decoded. - assert_eq!(new_ccr.commitments.selected_core(), None); + assert_eq!( + new_ccr.commitments.core_selector(), + Err(CommittedCandidateReceiptError::UmpSignalDecode) + ); + + // Has two cores assigned but no core commitment. Will pass the check if the descriptor core + // index is indeed assigned to the para. + new_ccr.commitments.upward_messages.clear(); let mut cq = BTreeMap::new(); cq.insert( @@ -1081,28 +1119,46 @@ mod tests { CoreIndex(100), vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(), ); + assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq.clone())), Ok(())); + new_ccr.descriptor.set_core_index(CoreIndex(1)); assert_eq!( new_ccr.check_core_index(&transpose_claim_queue(cq.clone())), - Err(CandidateReceiptError::NoCoreSelected) + Err(CommittedCandidateReceiptError::InvalidCoreIndex) ); + new_ccr.descriptor.set_core_index(CoreIndex(0)); new_ccr.commitments.upward_messages.clear(); new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); - new_ccr .commitments .upward_messages .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); - // Duplicate + // No assignments. + assert_eq!( + new_ccr.check_core_index(&transpose_claim_queue(Default::default())), + Err(CommittedCandidateReceiptError::NoAssignment) + ); + + // Mismatch between descriptor index and commitment. + new_ccr.descriptor.set_core_index(CoreIndex(1)); + assert_eq!( + new_ccr.check_core_index(&transpose_claim_queue(cq.clone())), + Err(CommittedCandidateReceiptError::CoreIndexMismatch) + ); + new_ccr.descriptor.set_core_index(CoreIndex(0)); + + // Too many UMP signals. new_ccr .commitments .upward_messages .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode()); - // Duplicate doesn't override first signal. - assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(())); + assert_eq!( + new_ccr.check_core_index(&transpose_claim_queue(cq)), + Err(CommittedCandidateReceiptError::TooManyUMPSignals) + ); } #[test] @@ -1172,7 +1228,7 @@ mod tests { Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); assert_eq!(v1_ccr.descriptor.version(), CandidateDescriptorVersion::V1); - assert!(v1_ccr.commitments.selected_core().is_some()); + assert!(v1_ccr.commitments.core_selector().unwrap().is_some()); let mut cq = BTreeMap::new(); cq.insert(CoreIndex(0), vec![v1_ccr.descriptor.para_id()].into()); @@ -1180,11 +1236,6 @@ mod tests { assert!(v1_ccr.check_core_index(&transpose_claim_queue(cq)).is_ok()); - assert_eq!( - v1_ccr.commitments.committed_core_index(&vec![&CoreIndex(10), &CoreIndex(5)]), - Some(CoreIndex(5)), - ); - assert_eq!(v1_ccr.descriptor.core_index(), None); } @@ -1209,11 +1260,9 @@ mod tests { cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into()); cq.insert(CoreIndex(1), vec![new_ccr.descriptor.para_id()].into()); - // Should fail because 2 cores are assigned, - assert_eq!( - new_ccr.check_core_index(&transpose_claim_queue(cq)), - Err(CandidateReceiptError::NoCoreSelected) - ); + // Passes even if 2 cores are assigned, because elastic scaling MVP could still inject the + // core index in the `BackedCandidate`. + assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(())); // Adding collator signature should make it decode as v1. old_ccr.descriptor.signature = dummy_collator_signature(); diff --git a/polkadot/primitives/test-helpers/Cargo.toml b/polkadot/primitives/test-helpers/Cargo.toml index a44996ad6ef2dfdbeeea6c75fadb397d500eafec..27de3c4b9c56c6fbd182c3c0126325b5e80f7131 100644 --- a/polkadot/primitives/test-helpers/Cargo.toml +++ b/polkadot/primitives/test-helpers/Cargo.toml @@ -14,5 +14,5 @@ sp-keyring = { workspace = true, default-features = true } sp-application-crypto = { workspace = true } sp-runtime = { workspace = true, default-features = true } sp-core = { features = ["std"], workspace = true, default-features = true } -polkadot-primitives = { workspace = true, default-features = true } +polkadot-primitives = { features = ["test"], workspace = true, default-features = true } rand = { workspace = true, default-features = true } diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index c2eccafef788ecebfef954338f44b11a92cbb246..1717dd5b0edae7ee3c2d67c0da8a04403e79972a 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -23,13 +23,15 @@ //! Note that `dummy_` prefixed values are meant to be fillers, that should not matter, and will //! contain randomness based data. use polkadot_primitives::{ - vstaging::{CandidateDescriptorV2, CandidateReceiptV2, CommittedCandidateReceiptV2}, + vstaging::{ + CandidateDescriptorV2, CandidateReceiptV2, CommittedCandidateReceiptV2, MutateDescriptorV2, + }, CandidateCommitments, CandidateDescriptor, CandidateReceipt, CollatorId, CollatorSignature, CommittedCandidateReceipt, CoreIndex, Hash, HeadData, Id as ParaId, PersistedValidationData, SessionIndex, ValidationCode, ValidationCodeHash, ValidatorId, }; pub use rand; -use sp_application_crypto::sr25519; +use sp_application_crypto::{sr25519, ByteArray}; use sp_keyring::Sr25519Keyring; use sp_runtime::generic::Digest; @@ -199,8 +201,15 @@ pub fn dummy_collator() -> CollatorId { CollatorId::from(sr25519::Public::default()) } -/// Create a meaningless collator signature. +/// Create a meaningless collator signature. It is important to not be 0, as we'd confuse +/// v1 and v2 descriptors. pub fn dummy_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) + .expect("64 bytes; qed") +} + +/// Create a zeroed collator signature. +pub fn zero_collator_signature() -> CollatorSignature { CollatorSignature::from(sr25519::Signature::default()) } @@ -250,6 +259,34 @@ pub fn make_candidate( (candidate, pvd) } +/// Create a meaningless v2 candidate, returning its receipt and PVD. +pub fn make_candidate_v2( + relay_parent_hash: Hash, + relay_parent_number: u32, + para_id: ParaId, + parent_head: HeadData, + head_data: HeadData, + validation_code_hash: ValidationCodeHash, +) -> (CommittedCandidateReceiptV2, PersistedValidationData) { + let pvd = dummy_pvd(parent_head, relay_parent_number); + let commitments = CandidateCommitments { + head_data, + horizontal_messages: Default::default(), + upward_messages: Default::default(), + new_validation_code: None, + processed_downward_messages: 0, + hrmp_watermark: relay_parent_number, + }; + + let mut descriptor = dummy_candidate_descriptor_v2(relay_parent_hash); + descriptor.set_para_id(para_id); + descriptor.set_persisted_validation_data_hash(pvd.hash()); + descriptor.set_validation_code_hash(validation_code_hash); + let candidate = CommittedCandidateReceiptV2 { descriptor, commitments }; + + (candidate, pvd) +} + /// Create a new candidate descriptor, and apply a valid signature /// using the provided `collator` key. pub fn make_valid_candidate_descriptor>( diff --git a/polkadot/runtime/common/src/claims.rs b/polkadot/runtime/common/src/claims.rs index 2b36c19efce7811b6824640ae3b677c5c8700098..b77cbfeff77ca605aa9a49b38a1cac37ef81b4ac 100644 --- a/polkadot/runtime/common/src/claims.rs +++ b/polkadot/runtime/common/src/claims.rs @@ -19,7 +19,7 @@ #[cfg(not(feature = "std"))] use alloc::{format, string::String}; use alloc::{vec, vec::Vec}; -use codec::{Decode, Encode}; +use codec::{Decode, Encode, MaxEncodedLen}; use core::fmt::Debug; use frame_support::{ ensure, @@ -82,7 +82,17 @@ impl WeightInfo for TestWeightInfo { /// The kind of statement an account needs to make for a claim to be valid. #[derive( - Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug, TypeInfo, Serialize, Deserialize, + Encode, + Decode, + Clone, + Copy, + Eq, + PartialEq, + RuntimeDebug, + TypeInfo, + Serialize, + Deserialize, + MaxEncodedLen, )] pub enum StatementKind { /// Statement required to be made by non-SAFT holders. @@ -116,7 +126,9 @@ impl Default for StatementKind { /// An Ethereum address (i.e. 20 bytes, used to represent an Ethereum account). /// /// This gets serialized to the 0x-prefixed hex representation. -#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Default, RuntimeDebug, TypeInfo)] +#[derive( + Clone, Copy, PartialEq, Eq, Encode, Decode, Default, RuntimeDebug, TypeInfo, MaxEncodedLen, +)] pub struct EthereumAddress([u8; 20]); impl Serialize for EthereumAddress { @@ -150,7 +162,7 @@ impl<'de> Deserialize<'de> for EthereumAddress { } } -#[derive(Encode, Decode, Clone, TypeInfo)] +#[derive(Encode, Decode, Clone, TypeInfo, MaxEncodedLen)] pub struct EcdsaSignature(pub [u8; 65]); impl PartialEq for EcdsaSignature { @@ -172,7 +184,6 @@ pub mod pallet { use frame_system::pallet_prelude::*; #[pallet::pallet] - #[pallet::without_storage_info] pub struct Pallet(_); /// Configuration trait. @@ -1440,7 +1451,7 @@ mod tests { mod benchmarking { use super::*; use crate::claims::Call; - use frame_benchmarking::{account, benchmarks}; + use frame_benchmarking::v2::*; use frame_support::{ dispatch::{DispatchInfo, GetDispatchInfo}, traits::UnfilteredDispatchable, @@ -1485,136 +1496,168 @@ mod benchmarking { Ok(()) } - benchmarks! { - where_clause { where ::RuntimeCall: IsSubType> + From>, + #[benchmarks( + where + ::RuntimeCall: IsSubType> + From>, ::RuntimeCall: Dispatchable + GetDispatchInfo, <::RuntimeCall as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner + AsTransactionAuthorizedOrigin + Clone, <::RuntimeCall as Dispatchable>::PostInfo: Default, - } + )] + mod benchmarks { + use super::*; // Benchmark `claim` including `validate_unsigned` logic. - claim { + #[benchmark] + fn claim() -> Result<(), BenchmarkError> { let c = MAX_CLAIMS; - - for i in 0 .. c / 2 { + for _ in 0..c / 2 { create_claim::(c)?; create_claim_attest::(u32::MAX - c)?; } - let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&c.encode())).unwrap(); let eth_address = eth(&secret_key); let account: T::AccountId = account("user", c, SEED); let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into())); let signature = sig::(&secret_key, &account.encode(), &[][..]); - super::Pallet::::mint_claim(RawOrigin::Root.into(), eth_address, VALUE.into(), vesting, None)?; + super::Pallet::::mint_claim( + RawOrigin::Root.into(), + eth_address, + VALUE.into(), + vesting, + None, + )?; assert_eq!(Claims::::get(eth_address), Some(VALUE.into())); let source = sp_runtime::transaction_validity::TransactionSource::External; - let call_enc = Call::::claim { - dest: account.clone(), - ethereum_signature: signature.clone() - }.encode(); - }: { - let call = as Decode>::decode(&mut &*call_enc) - .expect("call is encoded above, encoding must be correct"); - super::Pallet::::validate_unsigned(source, &call).map_err(|e| -> &'static str { e.into() })?; - call.dispatch_bypass_filter(RawOrigin::None.into())?; - } - verify { + let call_enc = + Call::::claim { dest: account.clone(), ethereum_signature: signature.clone() } + .encode(); + + #[block] + { + let call = as Decode>::decode(&mut &*call_enc) + .expect("call is encoded above, encoding must be correct"); + super::Pallet::::validate_unsigned(source, &call) + .map_err(|e| -> &'static str { e.into() })?; + call.dispatch_bypass_filter(RawOrigin::None.into())?; + } + assert_eq!(Claims::::get(eth_address), None); + Ok(()) } // Benchmark `mint_claim` when there already exists `c` claims in storage. - mint_claim { + #[benchmark] + fn mint_claim() -> Result<(), BenchmarkError> { let c = MAX_CLAIMS; - - for i in 0 .. c / 2 { + for _ in 0..c / 2 { create_claim::(c)?; create_claim_attest::(u32::MAX - c)?; } - let eth_address = account("eth_address", 0, SEED); let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into())); let statement = StatementKind::Regular; - }: _(RawOrigin::Root, eth_address, VALUE.into(), vesting, Some(statement)) - verify { + + #[extrinsic_call] + _(RawOrigin::Root, eth_address, VALUE.into(), vesting, Some(statement)); + assert_eq!(Claims::::get(eth_address), Some(VALUE.into())); + Ok(()) } // Benchmark `claim_attest` including `validate_unsigned` logic. - claim_attest { + #[benchmark] + fn claim_attest() -> Result<(), BenchmarkError> { let c = MAX_CLAIMS; - - for i in 0 .. c / 2 { + for _ in 0..c / 2 { create_claim::(c)?; create_claim_attest::(u32::MAX - c)?; } - // Crate signature let attest_c = u32::MAX - c; - let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap(); + let secret_key = + libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap(); let eth_address = eth(&secret_key); let account: T::AccountId = account("user", c, SEED); let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into())); let statement = StatementKind::Regular; let signature = sig::(&secret_key, &account.encode(), statement.to_text()); - super::Pallet::::mint_claim(RawOrigin::Root.into(), eth_address, VALUE.into(), vesting, Some(statement))?; + super::Pallet::::mint_claim( + RawOrigin::Root.into(), + eth_address, + VALUE.into(), + vesting, + Some(statement), + )?; assert_eq!(Claims::::get(eth_address), Some(VALUE.into())); let call_enc = Call::::claim_attest { dest: account.clone(), ethereum_signature: signature.clone(), - statement: StatementKind::Regular.to_text().to_vec() - }.encode(); + statement: StatementKind::Regular.to_text().to_vec(), + } + .encode(); let source = sp_runtime::transaction_validity::TransactionSource::External; - }: { - let call = as Decode>::decode(&mut &*call_enc) - .expect("call is encoded above, encoding must be correct"); - super::Pallet::::validate_unsigned(source, &call).map_err(|e| -> &'static str { e.into() })?; - call.dispatch_bypass_filter(RawOrigin::None.into())?; - } - verify { + + #[block] + { + let call = as Decode>::decode(&mut &*call_enc) + .expect("call is encoded above, encoding must be correct"); + super::Pallet::::validate_unsigned(source, &call) + .map_err(|e| -> &'static str { e.into() })?; + call.dispatch_bypass_filter(RawOrigin::None.into())?; + } + assert_eq!(Claims::::get(eth_address), None); + Ok(()) } // Benchmark `attest` including prevalidate logic. - attest { + #[benchmark] + fn attest() -> Result<(), BenchmarkError> { let c = MAX_CLAIMS; - - for i in 0 .. c / 2 { + for _ in 0..c / 2 { create_claim::(c)?; create_claim_attest::(u32::MAX - c)?; } - let attest_c = u32::MAX - c; - let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap(); + let secret_key = + libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap(); let eth_address = eth(&secret_key); let account: T::AccountId = account("user", c, SEED); let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into())); let statement = StatementKind::Regular; - let signature = sig::(&secret_key, &account.encode(), statement.to_text()); - super::Pallet::::mint_claim(RawOrigin::Root.into(), eth_address, VALUE.into(), vesting, Some(statement))?; + super::Pallet::::mint_claim( + RawOrigin::Root.into(), + eth_address, + VALUE.into(), + vesting, + Some(statement), + )?; Preclaims::::insert(&account, eth_address); assert_eq!(Claims::::get(eth_address), Some(VALUE.into())); let stmt = StatementKind::Regular.to_text().to_vec(); - }: - _(RawOrigin::Signed(account), stmt) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(account), stmt); + assert_eq!(Claims::::get(eth_address), None); + Ok(()) } - move_claim { + #[benchmark] + fn move_claim() -> Result<(), BenchmarkError> { let c = MAX_CLAIMS; - - for i in 0 .. c / 2 { + for _ in 0..c / 2 { create_claim::(c)?; create_claim_attest::(u32::MAX - c)?; } - let attest_c = u32::MAX - c; - let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap(); + let secret_key = + libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap(); let eth_address = eth(&secret_key); - let new_secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&(u32::MAX/2).encode())).unwrap(); + let new_secret_key = + libsecp256k1::SecretKey::parse(&keccak_256(&(u32::MAX / 2).encode())).unwrap(); let new_eth_address = eth(&new_secret_key); let account: T::AccountId = account("user", c, SEED); @@ -1622,73 +1665,85 @@ mod benchmarking { assert!(Claims::::contains_key(eth_address)); assert!(!Claims::::contains_key(new_eth_address)); - }: _(RawOrigin::Root, eth_address, new_eth_address, Some(account)) - verify { + + #[extrinsic_call] + _(RawOrigin::Root, eth_address, new_eth_address, Some(account)); + assert!(!Claims::::contains_key(eth_address)); assert!(Claims::::contains_key(new_eth_address)); + Ok(()) } // Benchmark the time it takes to do `repeat` number of keccak256 hashes - #[extra] - keccak256 { - let i in 0 .. 10_000; + #[benchmark(extra)] + fn keccak256(i: Linear<0, 10_000>) { let bytes = (i).encode(); - }: { - for index in 0 .. i { - let _hash = keccak_256(&bytes); + + #[block] + { + for _ in 0..i { + let _hash = keccak_256(&bytes); + } } } // Benchmark the time it takes to do `repeat` number of `eth_recover` - #[extra] - eth_recover { - let i in 0 .. 1_000; + #[benchmark(extra)] + fn eth_recover(i: Linear<0, 1_000>) { // Crate signature let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&i.encode())).unwrap(); let account: T::AccountId = account("user", i, SEED); let signature = sig::(&secret_key, &account.encode(), &[][..]); let data = account.using_encoded(to_ascii_hex); let extra = StatementKind::default().to_text(); - }: { - for _ in 0 .. i { - assert!(super::Pallet::::eth_recover(&signature, &data, extra).is_some()); + + #[block] + { + for _ in 0..i { + assert!(super::Pallet::::eth_recover(&signature, &data, extra).is_some()); + } } } - prevalidate_attests { + #[benchmark] + fn prevalidate_attests() -> Result<(), BenchmarkError> { let c = MAX_CLAIMS; - - for i in 0 .. c / 2 { + for _ in 0..c / 2 { create_claim::(c)?; create_claim_attest::(u32::MAX - c)?; } - let ext = PrevalidateAttests::::new(); - let call = super::Call::attest { - statement: StatementKind::Regular.to_text().to_vec(), - }; + let call = super::Call::attest { statement: StatementKind::Regular.to_text().to_vec() }; let call: ::RuntimeCall = call.into(); let info = call.get_dispatch_info(); let attest_c = u32::MAX - c; - let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap(); + let secret_key = + libsecp256k1::SecretKey::parse(&keccak_256(&attest_c.encode())).unwrap(); let eth_address = eth(&secret_key); let account: T::AccountId = account("user", c, SEED); let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into())); let statement = StatementKind::Regular; - let signature = sig::(&secret_key, &account.encode(), statement.to_text()); - super::Pallet::::mint_claim(RawOrigin::Root.into(), eth_address, VALUE.into(), vesting, Some(statement))?; + super::Pallet::::mint_claim( + RawOrigin::Root.into(), + eth_address, + VALUE.into(), + vesting, + Some(statement), + )?; Preclaims::::insert(&account, eth_address); assert_eq!(Claims::::get(eth_address), Some(VALUE.into())); - }: { - assert!(ext.test_run( - RawOrigin::Signed(account).into(), - &call, - &info, - 0, - |_| { - Ok(Default::default()) - } - ).unwrap().is_ok()); + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(account).into(), &call, &info, 0, |_| { + Ok(Default::default()) + }) + .unwrap() + .is_ok()); + } + + Ok(()) } impl_benchmark_test_suite!( diff --git a/polkadot/runtime/common/src/impls.rs b/polkadot/runtime/common/src/impls.rs index 2f79d223d3c06381326cf73d5269b7e476795995..9a290f08609abe45e801b9770dcc428ee0e5250a 100644 --- a/polkadot/runtime/common/src/impls.rs +++ b/polkadot/runtime/common/src/impls.rs @@ -138,6 +138,15 @@ pub enum VersionedLocatableAsset { V3 { location: xcm::v3::Location, asset_id: xcm::v3::AssetId }, #[codec(index = 4)] V4 { location: xcm::v4::Location, asset_id: xcm::v4::AssetId }, + #[codec(index = 5)] + V5 { location: xcm::v5::Location, asset_id: xcm::v5::AssetId }, +} + +/// A conversion from latest xcm to `VersionedLocatableAsset`. +impl From<(xcm::latest::Location, xcm::latest::AssetId)> for VersionedLocatableAsset { + fn from(value: (xcm::latest::Location, xcm::latest::AssetId)) -> Self { + VersionedLocatableAsset::V5 { location: value.0, asset_id: value.1 } + } } /// Converts the [`VersionedLocatableAsset`] to the [`xcm_builder::LocatableAssetId`]. @@ -149,12 +158,22 @@ impl TryConvert asset: VersionedLocatableAsset, ) -> Result { match asset { - VersionedLocatableAsset::V3 { location, asset_id } => + VersionedLocatableAsset::V3 { location, asset_id } => { + let v4_location: xcm::v4::Location = + location.try_into().map_err(|_| asset.clone())?; + let v4_asset_id: xcm::v4::AssetId = + asset_id.try_into().map_err(|_| asset.clone())?; + Ok(xcm_builder::LocatableAssetId { + location: v4_location.try_into().map_err(|_| asset.clone())?, + asset_id: v4_asset_id.try_into().map_err(|_| asset.clone())?, + }) + }, + VersionedLocatableAsset::V4 { ref location, ref asset_id } => Ok(xcm_builder::LocatableAssetId { - location: location.try_into().map_err(|_| asset.clone())?, - asset_id: asset_id.try_into().map_err(|_| asset.clone())?, + location: location.clone().try_into().map_err(|_| asset.clone())?, + asset_id: asset_id.clone().try_into().map_err(|_| asset.clone())?, }), - VersionedLocatableAsset::V4 { location, asset_id } => + VersionedLocatableAsset::V5 { location, asset_id } => Ok(xcm_builder::LocatableAssetId { location, asset_id }), } } @@ -167,12 +186,12 @@ impl TryConvert<&VersionedLocation, xcm::latest::Location> for VersionedLocation location: &VersionedLocation, ) -> Result { let latest = match location.clone() { - VersionedLocation::V2(l) => { - let v3: xcm::v3::Location = l.try_into().map_err(|_| location)?; - v3.try_into().map_err(|_| location)? + VersionedLocation::V3(l) => { + let v4_location: xcm::v4::Location = l.try_into().map_err(|_| location)?; + v4_location.try_into().map_err(|_| location)? }, - VersionedLocation::V3(l) => l.try_into().map_err(|_| location)?, - VersionedLocation::V4(l) => l, + VersionedLocation::V4(l) => l.try_into().map_err(|_| location)?, + VersionedLocation::V5(l) => l, }; Ok(latest) } @@ -188,11 +207,25 @@ where fn contains(asset: &VersionedLocatableAsset) -> bool { use VersionedLocatableAsset::*; let (location, asset_id) = match asset.clone() { - V3 { location, asset_id } => match (location.try_into(), asset_id.try_into()) { + V3 { location, asset_id } => { + let v4_location: xcm::v4::Location = match location.try_into() { + Ok(l) => l, + Err(_) => return false, + }; + let v4_asset_id: xcm::v4::AssetId = match asset_id.try_into() { + Ok(a) => a, + Err(_) => return false, + }; + match (v4_location.try_into(), v4_asset_id.try_into()) { + (Ok(l), Ok(a)) => (l, a), + _ => return false, + } + }, + V4 { location, asset_id } => match (location.try_into(), asset_id.try_into()) { (Ok(l), Ok(a)) => (l, a), _ => return false, }, - V4 { location, asset_id } => (location, asset_id), + V5 { location, asset_id } => (location, asset_id), }; C::contains(&location, &asset_id.0) } @@ -213,17 +246,14 @@ pub mod benchmarks { pub struct AssetRateArguments; impl AssetKindFactory for AssetRateArguments { fn create_asset_kind(seed: u32) -> VersionedLocatableAsset { - VersionedLocatableAsset::V4 { - location: xcm::v4::Location::new(0, [xcm::v4::Junction::Parachain(seed)]), - asset_id: xcm::v4::Location::new( + ( + Location::new(0, [Parachain(seed)]), + AssetId(Location::new( 0, - [ - xcm::v4::Junction::PalletInstance(seed.try_into().unwrap()), - xcm::v4::Junction::GeneralIndex(seed.into()), - ], - ) - .into(), - } + [PalletInstance(seed.try_into().unwrap()), GeneralIndex(seed.into())], + )), + ) + .into() } } @@ -238,26 +268,17 @@ pub mod benchmarks { for TreasuryArguments { fn create_asset_kind(seed: u32) -> VersionedLocatableAsset { - VersionedLocatableAsset::V3 { - location: xcm::v3::Location::new( - Parents::get(), - [xcm::v3::Junction::Parachain(ParaId::get())], - ), - asset_id: xcm::v3::Location::new( + ( + Location::new(Parents::get(), [Junction::Parachain(ParaId::get())]), + AssetId(Location::new( 0, - [ - xcm::v3::Junction::PalletInstance(seed.try_into().unwrap()), - xcm::v3::Junction::GeneralIndex(seed.into()), - ], - ) - .into(), - } + [PalletInstance(seed.try_into().unwrap()), GeneralIndex(seed.into())], + )), + ) + .into() } fn create_beneficiary(seed: [u8; 32]) -> VersionedLocation { - VersionedLocation::V4(xcm::v4::Location::new( - 0, - [xcm::v4::Junction::AccountId32 { network: None, id: seed }], - )) + VersionedLocation::from(Location::new(0, [AccountId32 { network: None, id: seed }])) } } } diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index 37fe7f0b59e979a7812bbbf4d7f9e7cb7541c386..7ff7f69faf148e9dde971f62d33c7e7a74609bf7 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -157,7 +157,7 @@ impl InspectMessageQueues for ChildParachainRouter { AssignmentsEmpty, - /// Assignments together exceeded 57600. - OverScheduled, - /// Assignments together less than 57600 - UnderScheduled, /// assign_core is only allowed to append new assignments at the end of already existing - /// ones. + /// ones or update the last entry. DisallowedInsert, - /// Tried to insert a schedule for the same core and block number as an existing schedule - DuplicateInsert, - /// Tried to add an unsorted set of assignments - AssignmentsNotSorted, } } @@ -387,67 +379,56 @@ impl Pallet { /// Append another assignment for a core. /// - /// Important only appending is allowed. Meaning, all already existing assignments must have a - /// begin smaller than the one passed here. This restriction exists, because it makes the - /// insertion O(1) and the author could not think of a reason, why this restriction should be - /// causing any problems. Inserting arbitrarily causes a `DispatchError::DisallowedInsert` - /// error. This restriction could easily be lifted if need be and in fact an implementation is - /// available - /// [here](https://github.com/paritytech/polkadot-sdk/pull/1694/commits/c0c23b01fd2830910cde92c11960dad12cdff398#diff-0c85a46e448de79a5452395829986ee8747e17a857c27ab624304987d2dde8baR386). - /// The problem is that insertion complexity then depends on the size of the existing queue, - /// which makes determining weights hard and could lead to issues like overweight blocks (at - /// least in theory). + /// Important: Only appending is allowed or insertion into the last item. Meaning, + /// all already existing assignments must have a `begin` smaller or equal than the one passed + /// here. + /// Updating the last entry is supported to allow for making a core assignment multiple calls to + /// assign_core. Thus if you have too much interlacing for e.g. a single UMP message you can + /// split that up into multiple messages, each triggering a call to `assign_core`, together + /// forming the total assignment. + /// + /// Inserting arbitrarily causes a `DispatchError::DisallowedInsert` error. + // With this restriction this function allows for O(1) complexity. It could easily be lifted, if + // need be and in fact an implementation is available + // [here](https://github.com/paritytech/polkadot-sdk/pull/1694/commits/c0c23b01fd2830910cde92c11960dad12cdff398#diff-0c85a46e448de79a5452395829986ee8747e17a857c27ab624304987d2dde8baR386). + // The problem is that insertion complexity then depends on the size of the existing queue, + // which makes determining weights hard and could lead to issues like overweight blocks (at + // least in theory). pub fn assign_core( core_idx: CoreIndex, begin: BlockNumberFor, - assignments: Vec<(CoreAssignment, PartsOf57600)>, + mut assignments: Vec<(CoreAssignment, PartsOf57600)>, end_hint: Option>, ) -> Result<(), DispatchError> { // There should be at least one assignment. ensure!(!assignments.is_empty(), Error::::AssignmentsEmpty); - // Checking for sort and unique manually, since we don't have access to iterator tools. - // This way of checking uniqueness only works since we also check sortedness. - assignments.iter().map(|x| &x.0).try_fold(None, |prev, cur| { - if prev.map_or(false, |p| p >= cur) { - Err(Error::::AssignmentsNotSorted) - } else { - Ok(Some(cur)) - } - })?; - - // Check that the total parts between all assignments are equal to 57600 - let parts_sum = assignments - .iter() - .map(|assignment| assignment.1) - .try_fold(PartsOf57600::ZERO, |sum, parts| { - sum.checked_add(parts).ok_or(Error::::OverScheduled) - })?; - ensure!(parts_sum.is_full(), Error::::UnderScheduled); - CoreDescriptors::::mutate(core_idx, |core_descriptor| { let new_queue = match core_descriptor.queue { Some(queue) => { - ensure!(begin > queue.last, Error::::DisallowedInsert); - - CoreSchedules::::try_mutate((queue.last, core_idx), |schedule| { - if let Some(schedule) = schedule.as_mut() { - debug_assert!(schedule.next_schedule.is_none(), "queue.end was supposed to be the end, so the next item must be `None`!"); - schedule.next_schedule = Some(begin); + ensure!(begin >= queue.last, Error::::DisallowedInsert); + + // Update queue if we are appending: + if begin > queue.last { + CoreSchedules::::mutate((queue.last, core_idx), |schedule| { + if let Some(schedule) = schedule.as_mut() { + debug_assert!(schedule.next_schedule.is_none(), "queue.end was supposed to be the end, so the next item must be `None`!"); + schedule.next_schedule = Some(begin); + } else { + defensive!("Queue end entry does not exist?"); + } + }); + } + + CoreSchedules::::mutate((begin, core_idx), |schedule| { + let assignments = if let Some(mut old_schedule) = schedule.take() { + old_schedule.assignments.append(&mut assignments); + old_schedule.assignments } else { - defensive!("Queue end entry does not exist?"); - } - CoreSchedules::::try_mutate((begin, core_idx), |schedule| { - // It should already be impossible to overwrite an existing schedule due - // to strictly increasing block number. But we check here for safety and - // in case the design changes. - ensure!(schedule.is_none(), Error::::DuplicateInsert); - *schedule = - Some(Schedule { assignments, end_hint, next_schedule: None }); - Ok::<(), DispatchError>(()) - })?; - Ok::<(), DispatchError>(()) - })?; + assignments + }; + *schedule = Some(Schedule { assignments, end_hint, next_schedule: None }); + }); QueueDescriptor { first: queue.first, last: begin } }, diff --git a/polkadot/runtime/parachains/src/assigner_coretime/tests.rs b/polkadot/runtime/parachains/src/assigner_coretime/tests.rs index 25007f0eed6aac023836456b95fef41f6d205044..ab011bfc4ae126050f39e9b0d3fffaab04e3c8e4 100644 --- a/polkadot/runtime/parachains/src/assigner_coretime/tests.rs +++ b/polkadot/runtime/parachains/src/assigner_coretime/tests.rs @@ -235,10 +235,7 @@ fn assign_core_works_with_prior_schedule() { } #[test] -// Invariants: We assume that CoreSchedules is append only and consumed. In other words new -// schedules inserted for a core must have a higher block number than all of the already existing -// schedules. -fn assign_core_enforces_higher_block_number() { +fn assign_core_enforces_higher_or_equal_block_number() { let core_idx = CoreIndex(0); new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { @@ -255,7 +252,7 @@ fn assign_core_enforces_higher_block_number() { assert_ok!(CoretimeAssigner::assign_core( core_idx, BlockNumberFor::::from(15u32), - default_test_assignments(), + vec![(CoreAssignment::Idle, PartsOf57600(28800))], None, )); @@ -281,32 +278,27 @@ fn assign_core_enforces_higher_block_number() { ), Error::::DisallowedInsert ); + // Call assign core again on last entry should work: + assert_eq!( + CoretimeAssigner::assign_core( + core_idx, + BlockNumberFor::::from(15u32), + vec![(CoreAssignment::Pool, PartsOf57600(28800))], + None, + ), + Ok(()) + ); }); } #[test] fn assign_core_enforces_well_formed_schedule() { - let para_id = ParaId::from(1u32); let core_idx = CoreIndex(0); new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { run_to_block(1, |n| if n == 1 { Some(Default::default()) } else { None }); let empty_assignments: Vec<(CoreAssignment, PartsOf57600)> = vec![]; - let overscheduled = vec![ - (CoreAssignment::Pool, PartsOf57600::FULL), - (CoreAssignment::Task(para_id.into()), PartsOf57600::FULL), - ]; - let underscheduled = vec![(CoreAssignment::Pool, PartsOf57600(30000))]; - let not_unique = vec![ - (CoreAssignment::Pool, PartsOf57600::FULL / 2), - (CoreAssignment::Pool, PartsOf57600::FULL / 2), - ]; - let not_sorted = vec![ - (CoreAssignment::Task(para_id.into()), PartsOf57600(19200)), - (CoreAssignment::Pool, PartsOf57600(19200)), - (CoreAssignment::Idle, PartsOf57600(19200)), - ]; // Attempting assign_core with malformed assignments such that all error cases // are tested @@ -319,42 +311,6 @@ fn assign_core_enforces_well_formed_schedule() { ), Error::::AssignmentsEmpty ); - assert_noop!( - CoretimeAssigner::assign_core( - core_idx, - BlockNumberFor::::from(11u32), - overscheduled, - None, - ), - Error::::OverScheduled - ); - assert_noop!( - CoretimeAssigner::assign_core( - core_idx, - BlockNumberFor::::from(11u32), - underscheduled, - None, - ), - Error::::UnderScheduled - ); - assert_noop!( - CoretimeAssigner::assign_core( - core_idx, - BlockNumberFor::::from(11u32), - not_unique, - None, - ), - Error::::AssignmentsNotSorted - ); - assert_noop!( - CoretimeAssigner::assign_core( - core_idx, - BlockNumberFor::::from(11u32), - not_sorted, - None, - ), - Error::::AssignmentsNotSorted - ); }); } @@ -374,7 +330,14 @@ fn next_schedule_always_points_to_next_work_plan_item() { Schedule { next_schedule: Some(start_4), ..default_test_schedule() }; let expected_schedule_4 = Schedule { next_schedule: Some(start_5), ..default_test_schedule() }; - let expected_schedule_5 = default_test_schedule(); + let expected_schedule_5 = Schedule { + next_schedule: None, + end_hint: None, + assignments: vec![ + (CoreAssignment::Pool, PartsOf57600(28800)), + (CoreAssignment::Idle, PartsOf57600(28800)), + ], + }; // Call assign_core for each of five schedules assert_ok!(CoretimeAssigner::assign_core( @@ -408,7 +371,14 @@ fn next_schedule_always_points_to_next_work_plan_item() { assert_ok!(CoretimeAssigner::assign_core( core_idx, BlockNumberFor::::from(start_5), - default_test_assignments(), + vec![(CoreAssignment::Pool, PartsOf57600(28800))], + None, + )); + // Test updating last entry once more: + assert_ok!(CoretimeAssigner::assign_core( + core_idx, + BlockNumberFor::::from(start_5), + vec![(CoreAssignment::Idle, PartsOf57600(28800))], None, )); diff --git a/polkadot/runtime/parachains/src/coretime/migration.rs b/polkadot/runtime/parachains/src/coretime/migration.rs index 52189be3d247bc57d32260cfdc1133ca28337b88..c3a1ebe82432486cf3ce4fc7c3b6d1959f9701ba 100644 --- a/polkadot/runtime/parachains/src/coretime/migration.rs +++ b/polkadot/runtime/parachains/src/coretime/migration.rs @@ -42,7 +42,9 @@ mod v_coretime { use sp_arithmetic::traits::SaturatedConversion; use sp_core::Get; use sp_runtime::BoundedVec; - use xcm::prelude::{send_xcm, Instruction, Junction, Location, SendError, WeightLimit, Xcm}; + use xcm::prelude::{ + send_xcm, Instruction, Junction, Location, SendError, SendXcm, WeightLimit, Xcm, + }; /// Return information about a legacy lease of a parachain. pub trait GetLegacyLease { @@ -62,10 +64,10 @@ mod v_coretime { impl< T: Config, - SendXcm: xcm::v4::SendXcm, + XcmSender: SendXcm, LegacyLease: GetLegacyLease>, const TIMESLICE_PERIOD: u32, - > MigrateToCoretime + > MigrateToCoretime { fn already_migrated() -> bool { // We are using the assigner coretime because the coretime pallet doesn't has any @@ -95,10 +97,10 @@ mod v_coretime { impl< T: Config + crate::dmp::Config, - SendXcm: xcm::v4::SendXcm, + XcmSender: SendXcm, LegacyLease: GetLegacyLease>, const TIMESLICE_PERIOD: u32, - > OnRuntimeUpgrade for MigrateToCoretime + > OnRuntimeUpgrade for MigrateToCoretime { fn on_runtime_upgrade() -> Weight { if Self::already_migrated() { @@ -106,7 +108,7 @@ mod v_coretime { } log::info!("Migrating existing parachains to coretime."); - migrate_to_coretime::() + migrate_to_coretime::() } #[cfg(feature = "try-runtime")] @@ -157,7 +159,7 @@ mod v_coretime { // NOTE: Also migrates `num_cores` config value in configuration::ActiveConfig. fn migrate_to_coretime< T: Config, - SendXcm: xcm::v4::SendXcm, + XcmSender: SendXcm, LegacyLease: GetLegacyLease>, const TIMESLICE_PERIOD: u32, >() -> Weight { @@ -198,9 +200,12 @@ mod v_coretime { c.scheduler_params.num_cores = total_cores; }); - if let Err(err) = - migrate_send_assignments_to_coretime_chain::( - ) { + if let Err(err) = migrate_send_assignments_to_coretime_chain::< + T, + XcmSender, + LegacyLease, + TIMESLICE_PERIOD, + >() { log::error!("Sending legacy chain data to coretime chain failed: {:?}", err); } @@ -215,7 +220,7 @@ mod v_coretime { fn migrate_send_assignments_to_coretime_chain< T: Config, - SendXcm: xcm::v4::SendXcm, + XcmSender: SendXcm, LegacyLease: GetLegacyLease>, const TIMESLICE_PERIOD: u32, >() -> result::Result<(), SendError> { @@ -300,7 +305,7 @@ mod v_coretime { }; for message in messages { - send_xcm::( + send_xcm::( Location::new(0, Junction::Parachain(T::BrokerId::get())), message, )?; diff --git a/polkadot/runtime/parachains/src/coretime/mod.rs b/polkadot/runtime/parachains/src/coretime/mod.rs index 9b9bdb86878f3bc30545ed58e4504ae5ef5b4582..966b7997a2775fc5111a243d5dd67f7e8b433cec 100644 --- a/polkadot/runtime/parachains/src/coretime/mod.rs +++ b/polkadot/runtime/parachains/src/coretime/mod.rs @@ -30,20 +30,7 @@ use pallet_broker::{CoreAssignment, CoreIndex as BrokerCoreIndex}; use polkadot_primitives::{Balance, BlockNumber, CoreIndex, Id as ParaId}; use sp_arithmetic::traits::SaturatedConversion; use sp_runtime::traits::TryConvert; -use xcm::{ - prelude::{send_xcm, Instruction, Junction, Location, OriginKind, SendXcm, WeightLimit, Xcm}, - v4::{ - Asset, - AssetFilter::Wild, - AssetId, Assets, Error as XcmError, - Fungibility::Fungible, - Instruction::{DepositAsset, ReceiveTeleportedAsset}, - Junctions::Here, - Reanchorable, - WildAsset::AllCounted, - XcmContext, - }, -}; +use xcm::prelude::*; use xcm_executor::traits::TransactAsset; use crate::{ @@ -119,7 +106,7 @@ pub mod pallet { use crate::configuration; use sp_runtime::traits::TryConvert; - use xcm::v4::InteriorLocation; + use xcm::latest::InteriorLocation; use xcm_executor::traits::TransactAsset; use super::*; @@ -149,11 +136,6 @@ pub mod pallet { type AssetTransactor: TransactAsset; /// AccountId to Location converter type AccountToLocation: for<'a> TryConvert<&'a Self::AccountId, Location>; - - /// Maximum weight for any XCM transact call that should be executed on the coretime chain. - /// - /// Basically should be `max_weight(set_leases, reserve, notify_core_count)`. - type MaxXcmTransactWeight: Get; } #[pallet::event] @@ -351,7 +333,6 @@ impl OnNewSession> for Pallet { fn mk_coretime_call(call: crate::coretime::CoretimeCalls) -> Instruction<()> { Instruction::Transact { origin_kind: OriginKind::Superuser, - require_weight_at_most: T::MaxXcmTransactWeight::get(), call: BrokerRuntimePallets::Broker(call).encode().into(), } } @@ -362,7 +343,7 @@ fn do_notify_revenue(when: BlockNumber, raw_revenue: Balance) -> Resu weight_limit: WeightLimit::Unlimited, check_origin: None, }]; - let asset = Asset { id: AssetId(Location::here()), fun: Fungible(raw_revenue) }; + let asset = Asset { id: Location::here().into(), fun: Fungible(raw_revenue) }; let dummy_xcm_context = XcmContext { origin: None, message_id: [0; 32], topic: None }; if raw_revenue > 0 { diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index ea3a5d3cdda9f4f5881fa9c2470cb20e3f256332..8ad9711a0f388ab7782848111df42e6e8a1003c4 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -46,7 +46,7 @@ use pallet_message_queue::OnQueueChanged; use polkadot_primitives::{ effective_minimum_backing_votes, supermajority_threshold, vstaging::{ - BackedCandidate, CandidateDescriptorV2 as CandidateDescriptor, + skip_ump_signals, BackedCandidate, CandidateDescriptorV2 as CandidateDescriptor, CandidateReceiptV2 as CandidateReceipt, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, }, @@ -412,11 +412,6 @@ pub(crate) enum UmpAcceptanceCheckErr { TotalSizeExceeded { total_size: u64, limit: u64 }, /// A para-chain cannot send UMP messages while it is offboarding. IsOffboarding, - /// The allowed number of `UMPSignal` messages in the queue was exceeded. - /// Currenly only one such message is allowed. - TooManyUMPSignals { count: u32 }, - /// The UMP queue contains an invalid `UMPSignal` - NoUmpSignal, } impl fmt::Debug for UmpAcceptanceCheckErr { @@ -445,12 +440,6 @@ impl fmt::Debug for UmpAcceptanceCheckErr { UmpAcceptanceCheckErr::IsOffboarding => { write!(fmt, "upward message rejected because the para is off-boarding") }, - UmpAcceptanceCheckErr::TooManyUMPSignals { count } => { - write!(fmt, "the ump queue has too many `UMPSignal` messages ({} > 1 )", count) - }, - UmpAcceptanceCheckErr::NoUmpSignal => { - write!(fmt, "Required UMP signal not found") - }, } } } @@ -925,25 +914,7 @@ impl Pallet { upward_messages: &[UpwardMessage], ) -> Result<(), UmpAcceptanceCheckErr> { // Filter any pending UMP signals and the separator. - let upward_messages = if let Some(separator_index) = - upward_messages.iter().position(|message| message.is_empty()) - { - let (upward_messages, ump_signals) = upward_messages.split_at(separator_index); - - if ump_signals.len() > 2 { - return Err(UmpAcceptanceCheckErr::TooManyUMPSignals { - count: ump_signals.len() as u32, - }) - } - - if ump_signals.len() == 1 { - return Err(UmpAcceptanceCheckErr::NoUmpSignal) - } - - upward_messages - } else { - upward_messages - }; + let upward_messages = skip_ump_signals(upward_messages.iter()).collect::>(); // Cannot send UMP messages while off-boarding. if paras::Pallet::::is_offboarding(para) { @@ -997,10 +968,7 @@ impl Pallet { /// to deal with the messages as given. Messages that are too long will be ignored since such /// candidates should have already been rejected in [`Self::check_upward_messages`]. pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: &[Vec]) { - let bounded = upward_messages - .iter() - // Stop once we hit the `UMPSignal` separator. - .take_while(|message| !message.is_empty()) + let bounded = skip_ump_signals(upward_messages.iter()) .filter_map(|d| { BoundedSlice::try_from(&d[..]) .inspect_err(|_| { diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index 9ef3922f0f8c51d49c6b38d8bd7cc9ad68032172..d701e1f9bd80f4a9d5979e30086de645685aefac 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -30,7 +30,9 @@ use polkadot_primitives::CoreIndex; use codec::Decode; use frame_support::{ - assert_ok, derive_impl, parameter_types, + assert_ok, derive_impl, + dispatch::GetDispatchInfo, + parameter_types, traits::{ Currency, ProcessMessage, ProcessMessageError, ValidatorSet, ValidatorSetWithIdentification, }, @@ -56,7 +58,7 @@ use std::{ }; use xcm::{ prelude::XcmVersion, - v4::{Assets, InteriorLocation, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}, + v5::{Assets, InteriorLocation, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}, IntoVersion, VersionedXcm, WrapVersion, }; @@ -260,7 +262,7 @@ thread_local! { /// versions in the `VERSION_WRAPPER`. pub struct TestUsesOnlyStoredVersionWrapper; impl WrapVersion for TestUsesOnlyStoredVersionWrapper { - fn wrap_version( + fn wrap_version( dest: &Location, xcm: impl Into>, ) -> Result, ()> { @@ -419,7 +421,6 @@ impl assigner_coretime::Config for Test {} parameter_types! { pub const BrokerId: u32 = 10u32; - pub MaxXcmTransactWeight: Weight = Weight::from_parts(10_000_000, 10_000); } pub struct BrokerPot; @@ -436,7 +437,6 @@ impl coretime::Config for Test { type BrokerId = BrokerId; type WeightInfo = crate::coretime::TestWeightInfo; type SendXcm = DummyXcmSender; - type MaxXcmTransactWeight = MaxXcmTransactWeight; type BrokerPotLocation = BrokerPot; type AssetTransactor = (); type AccountToLocation = (); diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index eef26b83368f5d9fa6f362b99c22cdbd0f448186..146be0ee0aadc533f1b155a18785c95b635226f9 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -1864,11 +1864,8 @@ mod enter { v2_descriptor: true, candidate_modifier: Some(|mut candidate: CommittedCandidateReceiptV2| { if candidate.descriptor.para_id() == 1.into() { - // Drop the core selector to make it invalid - candidate - .commitments - .upward_messages - .truncate(candidate.commitments.upward_messages.len() - 1); + // Make the core selector invalid + candidate.commitments.upward_messages[1].truncate(0); } candidate }), diff --git a/polkadot/runtime/parachains/src/scheduler.rs b/polkadot/runtime/parachains/src/scheduler.rs index 329df3a8a9deb3c06a337d9cf3e4e43e55c100ae..9c111c2d28e79c9df6c3b3efc9e8b36fe5e4b1e9 100644 --- a/polkadot/runtime/parachains/src/scheduler.rs +++ b/polkadot/runtime/parachains/src/scheduler.rs @@ -45,7 +45,8 @@ use frame_support::{pallet_prelude::*, traits::Defensive}; use frame_system::pallet_prelude::BlockNumberFor; pub use polkadot_core_primitives::v2::BlockNumber; use polkadot_primitives::{ - CoreIndex, GroupIndex, GroupRotationInfo, Id as ParaId, ScheduledCore, ValidatorIndex, + CoreIndex, GroupIndex, GroupRotationInfo, Id as ParaId, ScheduledCore, SchedulerParams, + ValidatorIndex, }; use sp_runtime::traits::One; @@ -131,7 +132,7 @@ impl Pallet { pub(crate) fn initializer_on_new_session( notification: &SessionChangeNotification>, ) { - let SessionChangeNotification { validators, new_config, prev_config, .. } = notification; + let SessionChangeNotification { validators, new_config, .. } = notification; let config = new_config; let assigner_cores = config.scheduler_params.num_cores; @@ -186,7 +187,7 @@ impl Pallet { } // Resize and populate claim queue. - Self::maybe_resize_claim_queue(prev_config.scheduler_params.num_cores, assigner_cores); + Self::maybe_resize_claim_queue(); Self::populate_claim_queue_after_session_change(); let now = frame_system::Pallet::::block_number() + One::one(); @@ -203,6 +204,12 @@ impl Pallet { ValidatorGroups::::decode_len().unwrap_or(0) } + /// Expected claim queue len. Can be different than the real length if for example we don't have + /// assignments for a core. + fn expected_claim_queue_len(config: &SchedulerParams>) -> u32 { + core::cmp::min(config.num_cores, Self::num_availability_cores() as u32) + } + /// Get the group assigned to a specific core by index at the current block number. Result /// undefined if the core index is unknown or the block number is less than the session start /// index. @@ -325,11 +332,11 @@ impl Pallet { /// and fill the queue from the assignment provider. pub(crate) fn advance_claim_queue(except_for: &BTreeSet) { let config = configuration::ActiveConfig::::get(); - let num_assigner_cores = config.scheduler_params.num_cores; + let expected_claim_queue_len = Self::expected_claim_queue_len(&config.scheduler_params); // Extra sanity, config should already never be smaller than 1: let n_lookahead = config.scheduler_params.lookahead.max(1); - for core_idx in 0..num_assigner_cores { + for core_idx in 0..expected_claim_queue_len { let core_idx = CoreIndex::from(core_idx); if !except_for.contains(&core_idx) { @@ -345,13 +352,16 @@ impl Pallet { } // on new session - fn maybe_resize_claim_queue(old_core_count: u32, new_core_count: u32) { - if new_core_count < old_core_count { + fn maybe_resize_claim_queue() { + let cq = ClaimQueue::::get(); + let Some((old_max_core, _)) = cq.last_key_value() else { return }; + let config = configuration::ActiveConfig::::get(); + let new_core_count = Self::expected_claim_queue_len(&config.scheduler_params); + + if new_core_count < (old_max_core.0 + 1) { ClaimQueue::::mutate(|cq| { - let to_remove: Vec<_> = cq - .range(CoreIndex(new_core_count)..CoreIndex(old_core_count)) - .map(|(k, _)| *k) - .collect(); + let to_remove: Vec<_> = + cq.range(CoreIndex(new_core_count)..=*old_max_core).map(|(k, _)| *k).collect(); for key in to_remove { if let Some(dropped_assignments) = cq.remove(&key) { Self::push_back_to_assignment_provider(dropped_assignments.into_iter()); @@ -367,9 +377,9 @@ impl Pallet { let config = configuration::ActiveConfig::::get(); // Extra sanity, config should already never be smaller than 1: let n_lookahead = config.scheduler_params.lookahead.max(1); - let new_core_count = config.scheduler_params.num_cores; + let expected_claim_queue_len = Self::expected_claim_queue_len(&config.scheduler_params); - for core_idx in 0..new_core_count { + for core_idx in 0..expected_claim_queue_len { let core_idx = CoreIndex::from(core_idx); Self::fill_claim_queue(core_idx, n_lookahead); } diff --git a/polkadot/runtime/parachains/src/scheduler/tests.rs b/polkadot/runtime/parachains/src/scheduler/tests.rs index 5be7e084f3bca3588301fa3970d0873fe83ba687..431562c6e6fb78f758e99df366323bcd2a886fb9 100644 --- a/polkadot/runtime/parachains/src/scheduler/tests.rs +++ b/polkadot/runtime/parachains/src/scheduler/tests.rs @@ -726,8 +726,26 @@ fn session_change_decreasing_number_of_cores() { [assignment_b.clone()].into_iter().collect::>() ); - // No more assignments now. Scheduler::advance_claim_queue(&Default::default()); + // No more assignments now. + assert_eq!(Scheduler::claim_queue_len(), 0); + + // Retain number of cores to 1 but remove all validator groups. The claim queue length + // should be the minimum of these two. + + // Add an assignment. + MockAssigner::add_test_assignment(assignment_b.clone()); + + run_to_block(4, |number| match number { + 4 => Some(SessionChangeNotification { + new_config: new_config.clone(), + prev_config: new_config.clone(), + validators: vec![], + ..Default::default() + }), + _ => None, + }); + assert_eq!(Scheduler::claim_queue_len(), 0); }); } diff --git a/polkadot/runtime/rococo/src/genesis_config_presets.rs b/polkadot/runtime/rococo/src/genesis_config_presets.rs index d609548aed27c580b511ff235f9b52d4ba06e80d..bdbf6f37d92c221df09fc8d7c98f333089e68a6f 100644 --- a/polkadot/runtime/rococo/src/genesis_config_presets.rs +++ b/polkadot/runtime/rococo/src/genesis_config_presets.rs @@ -23,6 +23,7 @@ use crate::{ #[cfg(not(feature = "std"))] use alloc::format; use alloc::{vec, vec::Vec}; +use frame_support::build_struct_json_patch; use polkadot_primitives::{AccountId, AssignmentId, SchedulerParams, ValidatorId}; use rococo_runtime_constants::currency::UNITS as ROC; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; @@ -163,7 +164,7 @@ fn rococo_testnet_genesis( const ENDOWMENT: u128 = 1_000_000 * ROC; - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::>(), }, @@ -185,9 +186,8 @@ fn rococo_testnet_genesis( ) }) .collect::>(), - ..Default::default() }, - babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, + babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG }, sudo: SudoConfig { key: Some(root_key.clone()) }, configuration: ConfigurationConfig { config: polkadot_runtime_parachains::configuration::HostConfiguration { @@ -198,14 +198,8 @@ fn rococo_testnet_genesis( ..default_parachains_host_configuration() }, }, - registrar: RegistrarConfig { - next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, - ..Default::default() - }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + registrar: RegistrarConfig { next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID }, + }) } // staging_testnet @@ -427,7 +421,7 @@ fn rococo_staging_testnet_config_genesis() -> serde_json::Value { const ENDOWMENT: u128 = 1_000_000 * ROC; const STASH: u128 = 100 * ROC; - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts .iter() @@ -440,19 +434,12 @@ fn rococo_staging_testnet_config_genesis() -> serde_json::Value { .into_iter() .map(|x| (x.0.clone(), x.0, rococo_session_keys(x.2, x.3, x.4, x.5, x.6, x.7))) .collect::>(), - ..Default::default() }, - babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, + babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG }, sudo: SudoConfig { key: Some(endowed_accounts[0].clone()) }, configuration: ConfigurationConfig { config: default_parachains_host_configuration() }, - registrar: RegistrarConfig { - next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, - ..Default::default() - }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + registrar: RegistrarConfig { next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID }, + }) } //development @@ -490,11 +477,11 @@ fn versi_local_testnet_genesis() -> serde_json::Value { /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &PresetId) -> Option> { - let patch = match id.try_into() { - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => rococo_local_testnet_genesis(), - Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => rococo_development_config_genesis(), - Ok("staging_testnet") => rococo_staging_testnet_config_genesis(), - Ok("versi_local_testnet") => versi_local_testnet_genesis(), + let patch = match id.as_ref() { + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => rococo_local_testnet_genesis(), + sp_genesis_builder::DEV_RUNTIME_PRESET => rococo_development_config_genesis(), + "staging_testnet" => rococo_staging_testnet_config_genesis(), + "versi_local_testnet" => versi_local_testnet_genesis(), _ => return None, }; Some( diff --git a/polkadot/runtime/rococo/src/impls.rs b/polkadot/runtime/rococo/src/impls.rs index f01440ea02bc7c15377c732d22041e60d3425d66..ab796edc54b1a3a5b8e12549f2b047e034846322 100644 --- a/polkadot/runtime/rococo/src/impls.rs +++ b/polkadot/runtime/rococo/src/impls.rs @@ -21,7 +21,7 @@ use core::marker::PhantomData; use frame_support::pallet_prelude::DispatchResult; use frame_system::RawOrigin; use polkadot_primitives::Balance; -use polkadot_runtime_common::identity_migrator::{OnReapIdentity, WeightInfo}; +use polkadot_runtime_common::identity_migrator::OnReapIdentity; use rococo_runtime_constants::currency::*; use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm}; use xcm_executor::traits::TransactAsset; @@ -88,10 +88,7 @@ where AccountId: Into<[u8; 32]> + Clone + Encode, { fn on_reap_identity(who: &AccountId, fields: u32, subs: u32) -> DispatchResult { - use crate::{ - impls::IdentityMigratorCalls::PokeDeposit, - weights::polkadot_runtime_common_identity_migrator::WeightInfo as MigratorWeights, - }; + use crate::impls::IdentityMigratorCalls::PokeDeposit; let total_to_send = Self::calculate_remote_deposit(fields, subs); @@ -144,7 +141,6 @@ where .into(); let poke = PeopleRuntimePallets::::IdentityMigrator(PokeDeposit(who.clone())); - let remote_weight_limit = MigratorWeights::::poke_deposit().saturating_mul(2); // Actual program to execute on People Chain. let program: Xcm<()> = Xcm(vec![ @@ -161,18 +157,14 @@ where .into(), }, // Poke the deposit to reserve the appropriate amount on the parachain. - Transact { - origin_kind: OriginKind::Superuser, - require_weight_at_most: remote_weight_limit, - call: poke.encode().into(), - }, + Transact { origin_kind: OriginKind::Superuser, call: poke.encode().into() }, ]); // send let _ = >::send( RawOrigin::Root.into(), - Box::new(VersionedLocation::V4(destination)), - Box::new(VersionedXcm::V4(program)), + Box::new(VersionedLocation::from(destination)), + Box::new(VersionedXcm::from(program)), )?; Ok(()) } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index e02a28353d296d002758addb82495f1286cdb2e6..96a97faa4750d386d513906a8b9c8a5fabf32950 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -100,7 +100,7 @@ use pallet_session::historical as session_historical; use pallet_transaction_payment::{FeeDetails, FungibleAdapter, RuntimeDispatchInfo}; use sp_core::{ConstU128, ConstU8, Get, OpaqueMetadata, H256}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{ AccountIdConversion, BlakeTwo256, Block as BlockT, ConstU32, ConvertInto, IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify, @@ -168,8 +168,8 @@ pub mod fast_runtime_binary { /// Runtime version (Rococo). #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("rococo"), - impl_name: create_runtime_str!("parity-rococo-v2.0"), + spec_name: alloc::borrow::Cow::Borrowed("rococo"), + impl_name: alloc::borrow::Cow::Borrowed("parity-rococo-v2.0"), authoring_version: 0, spec_version: 1_016_001, impl_version: 0, @@ -1098,7 +1098,6 @@ impl parachains_scheduler::Config for Runtime { parameter_types! { pub const BrokerId: u32 = BROKER_ID; pub const BrokerPalletId: PalletId = PalletId(*b"py/broke"); - pub MaxXcmTransactWeight: Weight = Weight::from_parts(200_000_000, 20_000); } pub struct BrokerPot; @@ -1122,7 +1121,6 @@ impl coretime::Config for Runtime { xcm_config::ThisNetwork, ::AccountId, >; - type MaxXcmTransactWeight = MaxXcmTransactWeight; } parameter_types! { @@ -1661,6 +1659,7 @@ pub mod migrations { pub const PhragmenElectionPalletId: LockIdentifier = *b"phrelect"; /// Weight for balance unreservations pub BalanceUnreserveWeight: Weight = weights::pallet_balances_balances::WeightInfo::::force_unreserve(); + pub BalanceTransferAllowDeath: Weight = weights::pallet_balances_balances::WeightInfo::::transfer_allow_death(); } // Special Config for Gov V1 pallets, allowing us to run migrations for them without @@ -1710,6 +1709,7 @@ pub mod migrations { paras_registrar::migration::MigrateToV1, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_referenda::migration::v1::MigrateV0ToV1, + pallet_child_bounties::migration::MigrateV0ToV1, // Unlock & unreserve Gov1 funds @@ -2435,7 +2435,7 @@ sp_api::impl_runtime_apis! { config: frame_benchmarking::BenchmarkConfig, ) -> Result< Vec, - sp_runtime::RuntimeString, + alloc::string::String, > { use frame_support::traits::WhitelistedStorageKeys; use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; diff --git a/polkadot/runtime/rococo/src/weights/pallet_bounties.rs b/polkadot/runtime/rococo/src/weights/pallet_bounties.rs index 8f8be5f2386f7983d42dbad355a266ff68cf332a..e1f630ec4ce7bcf596334b98972f159c24bdc0c8 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_bounties.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_bounties.rs @@ -17,25 +17,23 @@ //! Autogenerated weights for `pallet_bounties` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot +// target/production/polkadot // benchmark // pallet -// --chain=rococo-dev // --steps=50 // --repeat=20 -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --pallet=pallet_bounties // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_bounties +// --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ @@ -63,11 +61,11 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `210` // Estimated: `3593` - // Minimum execution time: 21_772_000 picoseconds. - Weight::from_parts(22_861_341, 0) + // Minimum execution time: 26_614_000 picoseconds. + Weight::from_parts(28_274_660, 0) .saturating_add(Weight::from_parts(0, 3593)) - // Standard Error: 3 - .saturating_add(Weight::from_parts(721, 0).saturating_mul(d.into())) + // Standard Error: 4 + .saturating_add(Weight::from_parts(779, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -79,8 +77,8 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `302` // Estimated: `3642` - // Minimum execution time: 11_218_000 picoseconds. - Weight::from_parts(11_796_000, 0) + // Minimum execution time: 14_692_000 picoseconds. + Weight::from_parts(15_070_000, 0) .saturating_add(Weight::from_parts(0, 3642)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -91,22 +89,36 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `322` // Estimated: `3642` - // Minimum execution time: 10_959_000 picoseconds. - Weight::from_parts(11_658_000, 0) + // Minimum execution time: 13_695_000 picoseconds. + Weight::from_parts(14_220_000, 0) .saturating_add(Weight::from_parts(0, 3642)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Bounties::Bounties` (r:1 w:1) /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + fn approve_bounty_with_curator() -> Weight { + // Proof Size summary in bytes: + // Measured: `322` + // Estimated: `3642` + // Minimum execution time: 18_428_000 picoseconds. + Weight::from_parts(19_145_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn unassign_curator() -> Weight { // Proof Size summary in bytes: // Measured: `498` // Estimated: `3642` - // Minimum execution time: 37_419_000 picoseconds. - Weight::from_parts(38_362_000, 0) + // Minimum execution time: 44_648_000 picoseconds. + Weight::from_parts(45_860_000, 0) .saturating_add(Weight::from_parts(0, 3642)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -119,8 +131,8 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `494` // Estimated: `3642` - // Minimum execution time: 27_328_000 picoseconds. - Weight::from_parts(27_661_000, 0) + // Minimum execution time: 33_973_000 picoseconds. + Weight::from_parts(34_979_000, 0) .saturating_add(Weight::from_parts(0, 3642)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -133,8 +145,8 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `400` // Estimated: `3642` - // Minimum execution time: 16_067_000 picoseconds. - Weight::from_parts(16_865_000, 0) + // Minimum execution time: 20_932_000 picoseconds. + Weight::from_parts(21_963_000, 0) .saturating_add(Weight::from_parts(0, 3642)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -151,8 +163,8 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `764` // Estimated: `8799` - // Minimum execution time: 101_153_000 picoseconds. - Weight::from_parts(102_480_000, 0) + // Minimum execution time: 114_942_000 picoseconds. + Weight::from_parts(117_653_000, 0) .saturating_add(Weight::from_parts(0, 8799)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(6)) @@ -169,8 +181,8 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `444` // Estimated: `3642` - // Minimum execution time: 38_838_000 picoseconds. - Weight::from_parts(39_549_000, 0) + // Minimum execution time: 47_649_000 picoseconds. + Weight::from_parts(49_016_000, 0) .saturating_add(Weight::from_parts(0, 3642)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) @@ -187,8 +199,8 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `680` // Estimated: `6196` - // Minimum execution time: 68_592_000 picoseconds. - Weight::from_parts(70_727_000, 0) + // Minimum execution time: 80_298_000 picoseconds. + Weight::from_parts(82_306_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) @@ -199,8 +211,8 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `358` // Estimated: `3642` - // Minimum execution time: 11_272_000 picoseconds. - Weight::from_parts(11_592_000, 0) + // Minimum execution time: 14_237_000 picoseconds. + Weight::from_parts(14_969_000, 0) .saturating_add(Weight::from_parts(0, 3642)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -216,11 +228,11 @@ impl pallet_bounties::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0 + b * (297 ยฑ0)` // Estimated: `1887 + b * (5206 ยฑ0)` - // Minimum execution time: 2_844_000 picoseconds. - Weight::from_parts(2_900_000, 0) + // Minimum execution time: 3_174_000 picoseconds. + Weight::from_parts(3_336_000, 0) .saturating_add(Weight::from_parts(0, 1887)) - // Standard Error: 9_467 - .saturating_add(Weight::from_parts(32_326_595, 0).saturating_mul(b.into())) + // Standard Error: 10_408 + .saturating_add(Weight::from_parts(37_811_366, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/xcm/mod.rs b/polkadot/runtime/rococo/src/weights/xcm/mod.rs index bd2b0fbb8c061519f99ac1b33b53bb3a387ece74..007002bf27bbccd0d55fa172b7feb5c9feee916f 100644 --- a/polkadot/runtime/rococo/src/weights/xcm/mod.rs +++ b/polkadot/runtime/rococo/src/weights/xcm/mod.rs @@ -24,6 +24,7 @@ use xcm::{latest::prelude::*, DoubleEncoded}; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmBalancesWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; +use xcm::latest::AssetTransferFilter; /// Types of asset supported by the Rococo runtime. pub enum AssetTypes { @@ -110,11 +111,7 @@ impl XcmWeightInfo for RococoXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmBalancesWeight::::transfer_reserve_asset()) } - fn transact( - _origin_kind: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_kind: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -163,12 +160,35 @@ impl XcmWeightInfo for RococoXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmBalancesWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmBalancesWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmBalancesWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -266,6 +286,9 @@ impl XcmWeightInfo for RococoXcmWeight { fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { XcmGeneric::::unpaid_execution() } + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } } #[test] diff --git a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 7d743b209124a51c3a09edfd84fbff4d18eb665f..c1d5c3fc89d979dd7e38243c071fb8236a933f2e 100644 --- a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -55,8 +55,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 30_672_000 picoseconds. - Weight::from_parts(31_677_000, 3593) + // Minimum execution time: 32_017_000 picoseconds. + Weight::from_parts(32_841_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,8 +66,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 41_132_000 picoseconds. - Weight::from_parts(41_654_000, 6196) + // Minimum execution time: 42_570_000 picoseconds. + Weight::from_parts(43_526_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -85,8 +85,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `281` // Estimated: `6196` - // Minimum execution time: 97_174_000 picoseconds. - Weight::from_parts(99_537_000, 6196) + // Minimum execution time: 103_020_000 picoseconds. + Weight::from_parts(104_906_000, 6196) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -113,8 +113,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `281` // Estimated: `3746` - // Minimum execution time: 67_105_000 picoseconds. - Weight::from_parts(68_659_000, 3746) + // Minimum execution time: 70_944_000 picoseconds. + Weight::from_parts(73_630_000, 3746) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -124,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 30_780_000 picoseconds. - Weight::from_parts(31_496_000, 3593) + // Minimum execution time: 31_979_000 picoseconds. + Weight::from_parts(32_649_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -135,8 +135,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 23_411_000 picoseconds. - Weight::from_parts(23_891_000, 3593) + // Minimum execution time: 24_462_000 picoseconds. + Weight::from_parts(25_052_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -154,8 +154,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 61_541_000 picoseconds. - Weight::from_parts(63_677_000, 3645) + // Minimum execution time: 65_047_000 picoseconds. + Weight::from_parts(67_225_000, 3645) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -173,8 +173,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 48_574_000 picoseconds. - Weight::from_parts(49_469_000, 3645) + // Minimum execution time: 53_401_000 picoseconds. + Weight::from_parts(55_155_000, 3645) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub(crate) fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 82_584_000 picoseconds. + Weight::from_parts(84_614_000, 3645) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index df2f9b2d0e8d4e031af8daa016ecefa85f6d1ff3..677640b45331f041aba708ee77709df35a7cca48 100644 --- a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -16,28 +16,27 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot +// target/production/polkadot // benchmark // pallet // --steps=50 // --repeat=20 // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::generic // --chain=rococo-dev -// --header=./file_header.txt -// --template=./xcm/pallet-xcm-benchmarks/template.hbs -// --output=./runtime/rococo/src/weights/xcm/ +// --header=./polkadot/file_header.txt +// --template=./polkadot/xcm/pallet-xcm-benchmarks/template.hbs +// --output=./polkadot/runtime/rococo/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,130 +49,125 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm_benchmarks::generic`. pub struct WeightInfo(PhantomData); impl WeightInfo { - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn report_holding() -> Weight { // Proof Size summary in bytes: - // Measured: `565` - // Estimated: `4030` - // Minimum execution time: 36_305_000 picoseconds. - Weight::from_parts(37_096_000, 4030) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 64_284_000 picoseconds. + Weight::from_parts(65_590_000, 3746) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } pub(crate) fn buy_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_831_000 picoseconds. - Weight::from_parts(2_904_000, 0) + // Minimum execution time: 777_000 picoseconds. + Weight::from_parts(825_000, 0) } - /// Storage: XcmPallet Queries (r:1 w:0) - /// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured) + pub(crate) fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_543_000 picoseconds. + Weight::from_parts(1_627_000, 0) + } + /// Storage: `XcmPallet::Queries` (r:1 w:0) + /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn query_response() -> Weight { // Proof Size summary in bytes: - // Measured: `169` - // Estimated: `3634` - // Minimum execution time: 11_769_000 picoseconds. - Weight::from_parts(12_122_000, 3634) + // Measured: `0` + // Estimated: `3465` + // Minimum execution time: 5_995_000 picoseconds. + Weight::from_parts(6_151_000, 3465) .saturating_add(T::DbWeight::get().reads(1)) } pub(crate) fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_293_000 picoseconds. - Weight::from_parts(12_522_000, 0) + // Minimum execution time: 7_567_000 picoseconds. + Weight::from_parts(7_779_000, 0) } pub(crate) fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_858_000 picoseconds. - Weight::from_parts(2_965_000, 0) + // Minimum execution time: 1_226_000 picoseconds. + Weight::from_parts(1_322_000, 0) } pub(crate) fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_623_000 picoseconds. - Weight::from_parts(2_774_000, 0) + // Minimum execution time: 768_000 picoseconds. + Weight::from_parts(828_000, 0) } pub(crate) fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_664_000 picoseconds. - Weight::from_parts(2_752_000, 0) + // Minimum execution time: 765_000 picoseconds. + Weight::from_parts(814_000, 0) } pub(crate) fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_646_000 picoseconds. - Weight::from_parts(2_709_000, 0) + // Minimum execution time: 739_000 picoseconds. + Weight::from_parts(820_000, 0) } pub(crate) fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_602_000 picoseconds. - Weight::from_parts(3_669_000, 0) + // Minimum execution time: 806_000 picoseconds. + Weight::from_parts(849_000, 0) } pub(crate) fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_609_000 picoseconds. - Weight::from_parts(2_721_000, 0) + // Minimum execution time: 782_000 picoseconds. + Weight::from_parts(820_000, 0) } - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn report_error() -> Weight { // Proof Size summary in bytes: - // Measured: `565` - // Estimated: `4030` - // Minimum execution time: 31_776_000 picoseconds. - Weight::from_parts(32_354_000, 4030) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 61_410_000 picoseconds. + Weight::from_parts(62_813_000, 3746) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: XcmPallet AssetTraps (r:1 w:1) - /// Proof Skipped: XcmPallet AssetTraps (max_values: None, max_size: None, mode: Measured) + /// Storage: `XcmPallet::AssetTraps` (r:1 w:1) + /// Proof: `XcmPallet::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn claim_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `226` - // Estimated: `3691` - // Minimum execution time: 15_912_000 picoseconds. - Weight::from_parts(16_219_000, 3691) + // Measured: `23` + // Estimated: `3488` + // Minimum execution time: 9_315_000 picoseconds. + Weight::from_parts(9_575_000, 3488) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -181,171 +175,158 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_704_000 picoseconds. - Weight::from_parts(2_777_000, 0) + // Minimum execution time: 733_000 picoseconds. + Weight::from_parts(813_000, 0) } - /// Storage: XcmPallet VersionNotifyTargets (r:1 w:1) - /// Proof Skipped: XcmPallet VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:1 w:1) + /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn subscribe_version() -> Weight { // Proof Size summary in bytes: - // Measured: `565` - // Estimated: `4030` - // Minimum execution time: 38_690_000 picoseconds. - Weight::from_parts(39_157_000, 4030) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(5)) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 30_641_000 picoseconds. + Weight::from_parts(31_822_000, 3645) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: XcmPallet VersionNotifyTargets (r:0 w:1) - /// Proof Skipped: XcmPallet VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:0 w:1) + /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn unsubscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_943_000 picoseconds. - Weight::from_parts(5_128_000, 0) + // Minimum execution time: 2_978_000 picoseconds. + Weight::from_parts(3_260_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub(crate) fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_438_000 picoseconds. - Weight::from_parts(6_500_000, 0) + // Minimum execution time: 1_139_000 picoseconds. + Weight::from_parts(1_272_000, 0) } pub(crate) fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_773_000 picoseconds. - Weight::from_parts(4_840_000, 0) + // Minimum execution time: 850_000 picoseconds. + Weight::from_parts(879_000, 0) } pub(crate) fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_818_000 picoseconds. - Weight::from_parts(2_893_000, 0) + // Minimum execution time: 770_000 picoseconds. + Weight::from_parts(834_000, 0) } pub(crate) fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_611_000 picoseconds. - Weight::from_parts(2_708_000, 0) + // Minimum execution time: 756_000 picoseconds. + Weight::from_parts(797_000, 0) } pub(crate) fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_870_000 picoseconds. - Weight::from_parts(2_958_000, 0) + // Minimum execution time: 888_000 picoseconds. + Weight::from_parts(1_000_000, 0) } - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn query_pallet() -> Weight { // Proof Size summary in bytes: - // Measured: `565` - // Estimated: `4030` - // Minimum execution time: 40_735_000 picoseconds. - Weight::from_parts(66_023_000, 4030) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 72_138_000 picoseconds. + Weight::from_parts(73_728_000, 3746) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } pub(crate) fn expect_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_293_000 picoseconds. - Weight::from_parts(18_088_000, 0) + // Minimum execution time: 8_482_000 picoseconds. + Weight::from_parts(8_667_000, 0) } - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn report_transact_status() -> Weight { // Proof Size summary in bytes: - // Measured: `565` - // Estimated: `4030` - // Minimum execution time: 31_438_000 picoseconds. - Weight::from_parts(32_086_000, 4030) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 61_580_000 picoseconds. + Weight::from_parts(62_928_000, 3746) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } pub(crate) fn clear_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_676_000 picoseconds. - Weight::from_parts(2_746_000, 0) + // Minimum execution time: 807_000 picoseconds. + Weight::from_parts(844_000, 0) } pub(crate) fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_629_000 picoseconds. - Weight::from_parts(2_724_000, 0) + // Minimum execution time: 757_000 picoseconds. + Weight::from_parts(808_000, 0) } pub(crate) fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_602_000 picoseconds. - Weight::from_parts(2_671_000, 0) + // Minimum execution time: 740_000 picoseconds. + Weight::from_parts(810_000, 0) } pub(crate) fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_681_000 picoseconds. - Weight::from_parts(2_768_000, 0) + // Minimum execution time: 752_000 picoseconds. + Weight::from_parts(786_000, 0) } pub(crate) fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_764_000 picoseconds. - Weight::from_parts(2_865_000, 0) + // Minimum execution time: 798_000 picoseconds. + Weight::from_parts(845_000, 0) + } + pub(crate) fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 707_000 picoseconds. + Weight::from_parts(749_000, 0) } } diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index 05e0ee64820a421eb4a8bdb6d8bd7624edcf611e..82a3136cc0d921d8c7246be23feba86cbe0b02e0 100644 --- a/polkadot/runtime/rococo/src/xcm_config.rs +++ b/polkadot/runtime/rococo/src/xcm_config.rs @@ -35,7 +35,7 @@ use polkadot_runtime_common::{ }; use rococo_runtime_constants::{currency::CENTS, system_parachain::*}; use sp_core::ConstU32; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, @@ -51,7 +51,7 @@ use xcm_executor::XcmExecutor; parameter_types! { pub TokenLocation: Location = Here.into_location(); pub RootLocation: Location = Location::here(); - pub const ThisNetwork: NetworkId = NetworkId::Rococo; + pub const ThisNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); pub UniversalLocation: InteriorLocation = ThisNetwork::get().into(); pub CheckAccount: AccountId = XcmPallet::check_account(); pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 9e7ee488af72d8b9993a307997066702a6e7591a..d2ed5abb6ed18d41e10be92c7088647049904ee9 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -80,7 +80,6 @@ use sp_consensus_beefy::ecdsa_crypto::{AuthorityId as BeefyId, Signature as Beef use sp_core::{ConstU32, OpaqueMetadata}; use sp_mmr_primitives as mmr; use sp_runtime::{ - create_runtime_str, curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ @@ -94,7 +93,7 @@ use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm::v4::{Assets, InteriorLocation, Location, SendError, SendResult, SendXcm, XcmHash}; +use xcm::latest::{Assets, InteriorLocation, Location, SendError, SendResult, SendXcm, XcmHash}; pub use pallet_balances::Call as BalancesCall; #[cfg(feature = "std")] @@ -119,8 +118,8 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); /// Runtime version (Test). #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("polkadot-test-runtime"), - impl_name: create_runtime_str!("parity-polkadot-test-runtime"), + spec_name: alloc::borrow::Cow::Borrowed("polkadot-test-runtime"), + impl_name: alloc::borrow::Cow::Borrowed("parity-polkadot-test-runtime"), authoring_version: 2, spec_version: 1056, impl_version: 0, @@ -585,7 +584,6 @@ impl parachains_paras::Config for Runtime { parameter_types! { pub const BrokerId: u32 = 10u32; - pub MaxXcmTransactWeight: Weight = Weight::from_parts(10_000_000, 10_000); } pub struct BrokerPot; @@ -641,7 +639,7 @@ impl SendXcm for DummyXcmSender { type Ticket = (); fn validate( _: &mut Option, - _: &mut Option>, + _: &mut Option>, ) -> SendResult { Ok(((), Assets::new())) } @@ -659,7 +657,6 @@ impl coretime::Config for Runtime { type BrokerId = BrokerId; type WeightInfo = crate::coretime::TestWeightInfo; type SendXcm = DummyXcmSender; - type MaxXcmTransactWeight = MaxXcmTransactWeight; type BrokerPotLocation = BrokerPot; type AssetTransactor = (); type AccountToLocation = (); diff --git a/polkadot/runtime/westend/src/genesis_config_presets.rs b/polkadot/runtime/westend/src/genesis_config_presets.rs index a9e22898ddb45ebd15dade033c39a98489f33b2e..b8f7710089e044b15ec97645ad95223181f0dd46 100644 --- a/polkadot/runtime/westend/src/genesis_config_presets.rs +++ b/polkadot/runtime/westend/src/genesis_config_presets.rs @@ -222,7 +222,7 @@ fn westend_staging_testnet_config_genesis() -> serde_json::Value { // // SECRET_SEED="slow awkward present example safe bundle science ocean cradle word tennis earn" // subkey inspect -n polkadot "$SECRET_SEED" - let endowed_accounts = vec![ + let endowed_accounts: Vec = vec![ // 15S75FkhCWEowEGfxWwVfrW3LQuy8w8PNhVmrzfsVhCMjUh1 hex!["c416837e232d9603e83162ef4bda08e61580eeefe60fe92fc044aa508559ae42"].into(), ]; @@ -338,7 +338,7 @@ fn westend_staging_testnet_config_genesis() -> serde_json::Value { const ENDOWMENT: u128 = 1_000_000 * WND; const STASH: u128 = 100 * WND; - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts .iter() @@ -364,7 +364,6 @@ fn westend_staging_testnet_config_genesis() -> serde_json::Value { ) }) .collect::>(), - ..Default::default() }, staking: StakingConfig { validator_count: 50, @@ -376,19 +375,12 @@ fn westend_staging_testnet_config_genesis() -> serde_json::Value { invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::>(), force_era: Forcing::ForceNone, slash_reward_fraction: Perbill::from_percent(10), - ..Default::default() }, - babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, + babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG }, sudo: SudoConfig { key: Some(endowed_accounts[0].clone()) }, configuration: ConfigurationConfig { config: default_parachains_host_configuration() }, - registrar: RegistrarConfig { - next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, - ..Default::default() - }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + registrar: RegistrarConfig { next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID }, + }) } //development @@ -411,10 +403,10 @@ fn westend_local_testnet_genesis() -> serde_json::Value { /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &PresetId) -> Option> { - let patch = match id.try_into() { - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => westend_local_testnet_genesis(), - Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => westend_development_config_genesis(), - Ok("staging_testnet") => westend_staging_testnet_config_genesis(), + let patch = match id.as_ref() { + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => westend_local_testnet_genesis(), + sp_genesis_builder::DEV_RUNTIME_PRESET => westend_development_config_genesis(), + "staging_testnet" => westend_staging_testnet_config_genesis(), _ => return None, }; Some( diff --git a/polkadot/runtime/westend/src/impls.rs b/polkadot/runtime/westend/src/impls.rs index ac3f9e679f8d3d5d7b1409a858598cc866f7c415..d7281dad56d4c416057e840732a40243ea4dde2a 100644 --- a/polkadot/runtime/westend/src/impls.rs +++ b/polkadot/runtime/westend/src/impls.rs @@ -21,7 +21,7 @@ use core::marker::PhantomData; use frame_support::pallet_prelude::DispatchResult; use frame_system::RawOrigin; use polkadot_primitives::Balance; -use polkadot_runtime_common::identity_migrator::{OnReapIdentity, WeightInfo}; +use polkadot_runtime_common::identity_migrator::OnReapIdentity; use westend_runtime_constants::currency::*; use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm}; use xcm_executor::traits::TransactAsset; @@ -88,10 +88,7 @@ where AccountId: Into<[u8; 32]> + Clone + Encode, { fn on_reap_identity(who: &AccountId, fields: u32, subs: u32) -> DispatchResult { - use crate::{ - impls::IdentityMigratorCalls::PokeDeposit, - weights::polkadot_runtime_common_identity_migrator::WeightInfo as MigratorWeights, - }; + use crate::impls::IdentityMigratorCalls::PokeDeposit; let total_to_send = Self::calculate_remote_deposit(fields, subs); @@ -144,7 +141,6 @@ where .into(); let poke = PeopleRuntimePallets::::IdentityMigrator(PokeDeposit(who.clone())); - let remote_weight_limit = MigratorWeights::::poke_deposit().saturating_mul(2); // Actual program to execute on People Chain. let program: Xcm<()> = Xcm(vec![ @@ -161,18 +157,14 @@ where .into(), }, // Poke the deposit to reserve the appropriate amount on the parachain. - Transact { - origin_kind: OriginKind::Superuser, - require_weight_at_most: remote_weight_limit, - call: poke.encode().into(), - }, + Transact { origin_kind: OriginKind::Superuser, call: poke.encode().into() }, ]); // send let _ = >::send( RawOrigin::Root.into(), - Box::new(VersionedLocation::V4(destination)), - Box::new(VersionedXcm::V4(program)), + Box::new(VersionedLocation::from(destination)), + Box::new(VersionedXcm::from(program)), )?; Ok(()) } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 251d92b03bbb9cf09177a97d7ca3710b62fa4ed6..4c04af111f811a0e3f0f7e148700cc063404ab7b 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -46,6 +46,7 @@ use frame_support::{ use frame_system::{EnsureRoot, EnsureSigned}; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId}; use pallet_identity::legacy::IdentityInfo; +use pallet_nomination_pools::PoolId; use pallet_session::historical as session_historical; use pallet_transaction_payment::{FeeDetails, FungibleAdapter, RuntimeDispatchInfo}; use polkadot_primitives::{ @@ -95,7 +96,7 @@ use sp_consensus_beefy::{ }; use sp_core::{ConstU8, OpaqueMetadata, RuntimeDebug, H256}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{ AccountIdConversion, BlakeTwo256, Block as BlockT, ConvertInto, IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify, @@ -168,8 +169,8 @@ pub mod fast_runtime_binary { /// Runtime version (Westend). #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("westend"), - impl_name: create_runtime_str!("parity-westend"), + spec_name: alloc::borrow::Cow::Borrowed("westend"), + impl_name: alloc::borrow::Cow::Borrowed("parity-westend"), authoring_version: 2, spec_version: 1_016_001, impl_version: 0, @@ -1323,7 +1324,6 @@ impl parachains_scheduler::Config for Runtime { parameter_types! { pub const BrokerId: u32 = BROKER_ID; pub const BrokerPalletId: PalletId = PalletId(*b"py/broke"); - pub MaxXcmTransactWeight: Weight = Weight::from_parts(200_000_000, 20_000); } pub struct BrokerPot; @@ -1347,7 +1347,6 @@ impl coretime::Config for Runtime { xcm_config::ThisNetwork, ::AccountId, >; - type MaxXcmTransactWeight = MaxXcmTransactWeight; } parameter_types! { @@ -2494,15 +2493,15 @@ sp_api::impl_runtime_apis! { NominationPools::api_pending_rewards(member).unwrap_or_default() } - fn points_to_balance(pool_id: pallet_nomination_pools::PoolId, points: Balance) -> Balance { + fn points_to_balance(pool_id: PoolId, points: Balance) -> Balance { NominationPools::api_points_to_balance(pool_id, points) } - fn balance_to_points(pool_id: pallet_nomination_pools::PoolId, new_funds: Balance) -> Balance { + fn balance_to_points(pool_id: PoolId, new_funds: Balance) -> Balance { NominationPools::api_balance_to_points(pool_id, new_funds) } - fn pool_pending_slash(pool_id: pallet_nomination_pools::PoolId) -> Balance { + fn pool_pending_slash(pool_id: PoolId) -> Balance { NominationPools::api_pool_pending_slash(pool_id) } @@ -2510,7 +2509,7 @@ sp_api::impl_runtime_apis! { NominationPools::api_member_pending_slash(member) } - fn pool_needs_delegate_migration(pool_id: pallet_nomination_pools::PoolId) -> bool { + fn pool_needs_delegate_migration(pool_id: PoolId) -> bool { NominationPools::api_pool_needs_delegate_migration(pool_id) } @@ -2522,9 +2521,13 @@ sp_api::impl_runtime_apis! { NominationPools::api_member_total_balance(member) } - fn pool_balance(pool_id: pallet_nomination_pools::PoolId) -> Balance { + fn pool_balance(pool_id: PoolId) -> Balance { NominationPools::api_pool_balance(pool_id) } + + fn pool_accounts(pool_id: PoolId) -> (AccountId, AccountId) { + NominationPools::api_pool_accounts(pool_id) + } } impl pallet_staking_runtime_api::StakingApi for Runtime { @@ -2592,7 +2595,7 @@ sp_api::impl_runtime_apis! { config: frame_benchmarking::BenchmarkConfig, ) -> Result< Vec, - sp_runtime::RuntimeString, + alloc::string::String, > { use frame_support::traits::WhitelistedStorageKeys; use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; diff --git a/polkadot/runtime/westend/src/weights/xcm/mod.rs b/polkadot/runtime/westend/src/weights/xcm/mod.rs index cb5894ea51e3c5106e75e86bc35249f24e8a03b4..e5f4a0d7ca8ecae97d45701a76695cf334811d9c 100644 --- a/polkadot/runtime/westend/src/weights/xcm/mod.rs +++ b/polkadot/runtime/westend/src/weights/xcm/mod.rs @@ -27,6 +27,7 @@ use xcm::{ use pallet_xcm_benchmarks_fungible::WeightInfo as XcmBalancesWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; +use xcm::latest::AssetTransferFilter; /// Types of asset supported by the westend runtime. pub enum AssetTypes { @@ -113,11 +114,7 @@ impl XcmWeightInfo for WestendXcmWeight { fn transfer_reserve_asset(assets: &Assets, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmBalancesWeight::::transfer_reserve_asset()) } - fn transact( - _origin_kind: &OriginKind, - _require_weight_at_most: &Weight, - _call: &DoubleEncoded, - ) -> Weight { + fn transact(_origin_kind: &OriginKind, _call: &DoubleEncoded) -> Weight { XcmGeneric::::transact() } fn hrmp_new_channel_open_request( @@ -166,12 +163,35 @@ impl XcmWeightInfo for WestendXcmWeight { fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmBalancesWeight::::initiate_teleport()) } + fn initiate_transfer( + _dest: &Location, + remote_fees: &Option, + _preserve_origin: &bool, + assets: &Vec, + _xcm: &Xcm<()>, + ) -> Weight { + let mut weight = if let Some(remote_fees) = remote_fees { + let fees = remote_fees.inner(); + fees.weigh_assets(XcmBalancesWeight::::initiate_transfer()) + } else { + Weight::zero() + }; + for asset_filter in assets { + let assets = asset_filter.inner(); + let extra = assets.weigh_assets(XcmBalancesWeight::::initiate_transfer()); + weight = weight.saturating_add(extra); + } + weight + } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() } fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight { XcmGeneric::::buy_execution() } + fn pay_fees(_asset: &Asset) -> Weight { + XcmGeneric::::pay_fees() + } fn refund_surplus() -> Weight { XcmGeneric::::refund_surplus() } @@ -184,6 +204,11 @@ impl XcmWeightInfo for WestendXcmWeight { fn clear_error() -> Weight { XcmGeneric::::clear_error() } + + fn set_asset_claimer(_location: &Location) -> Weight { + XcmGeneric::::set_asset_claimer() + } + fn claim_asset(_assets: &Assets, _ticket: &Location) -> Weight { XcmGeneric::::claim_asset() } diff --git a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index e0c61c8e2bf2583733954469f5cff132a8f49406..f1ce760d48cf178d06e2900a8dad7943300b2cd7 100644 --- a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-augrssgt-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 // Executed Command: @@ -55,8 +55,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 31_780_000 picoseconds. - Weight::from_parts(32_602_000, 3593) + // Minimum execution time: 31_578_000 picoseconds. + Weight::from_parts(32_243_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,8 +66,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 41_818_000 picoseconds. - Weight::from_parts(42_902_000, 6196) + // Minimum execution time: 42_320_000 picoseconds. + Weight::from_parts(43_036_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -85,8 +85,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `351` // Estimated: `8799` - // Minimum execution time: 101_949_000 picoseconds. - Weight::from_parts(104_190_000, 8799) + // Minimum execution time: 101_972_000 picoseconds. + Weight::from_parts(104_288_000, 8799) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -113,8 +113,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `351` // Estimated: `6196` - // Minimum execution time: 70_123_000 picoseconds. - Weight::from_parts(72_564_000, 6196) + // Minimum execution time: 71_916_000 picoseconds. + Weight::from_parts(73_610_000, 6196) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -124,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 31_868_000 picoseconds. - Weight::from_parts(32_388_000, 3593) + // Minimum execution time: 31_683_000 picoseconds. + Weight::from_parts(32_138_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -135,8 +135,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 24_532_000 picoseconds. - Weight::from_parts(25_166_000, 3593) + // Minimum execution time: 23_786_000 picoseconds. + Weight::from_parts(24_188_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -154,8 +154,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `147` // Estimated: `3612` - // Minimum execution time: 63_378_000 picoseconds. - Weight::from_parts(65_002_000, 3612) + // Minimum execution time: 63_986_000 picoseconds. + Weight::from_parts(65_356_000, 3612) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -173,9 +173,28 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `147` // Estimated: `3612` - // Minimum execution time: 49_174_000 picoseconds. - Weight::from_parts(50_356_000, 3612) + // Minimum execution time: 52_672_000 picoseconds. + Weight::from_parts(54_623_000, 3612) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) + pub(crate) fn initiate_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `250` + // Estimated: `6196` + // Minimum execution time: 83_853_000 picoseconds. + Weight::from_parts(85_876_000, 6196) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 49beb85c2784aa9d6746559322642e6f83c617e5..2ad1cd6359a6060754ba67f869364d2def273fc1 100644 --- a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-09-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 // Executed Command: // target/production/polkadot @@ -29,14 +29,14 @@ // --steps=50 // --repeat=20 // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::generic // --chain=westend-dev -// --header=./file_header.txt -// --template=./xcm/pallet-xcm-benchmarks/template.hbs -// --output=./runtime/westend/src/weights/xcm/ +// --header=./polkadot/file_header.txt +// --template=./polkadot/xcm/pallet-xcm-benchmarks/template.hbs +// --output=./polkadot/runtime/westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -49,126 +49,132 @@ use core::marker::PhantomData; /// Weight functions for `pallet_xcm_benchmarks::generic`. pub struct WeightInfo(PhantomData); impl WeightInfo { - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn report_holding() -> Weight { // Proof Size summary in bytes: - // Measured: `169` - // Estimated: `3634` - // Minimum execution time: 30_790_000 picoseconds. - Weight::from_parts(31_265_000, 3634) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `351` + // Estimated: `6196` + // Minimum execution time: 67_813_000 picoseconds. + Weight::from_parts(69_357_000, 6196) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } pub(crate) fn buy_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_741_000 picoseconds. - Weight::from_parts(2_823_000, 0) + // Minimum execution time: 716_000 picoseconds. + Weight::from_parts(780_000, 0) } - /// Storage: XcmPallet Queries (r:1 w:0) - /// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured) + pub(crate) fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_601_000 picoseconds. + Weight::from_parts(1_680_000, 0) + } + pub(crate) fn set_asset_claimer() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 707_000 picoseconds. + Weight::from_parts(749_000, 0) + } + /// Storage: `XcmPallet::Queries` (r:1 w:0) + /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn query_response() -> Weight { // Proof Size summary in bytes: - // Measured: `169` - // Estimated: `3634` - // Minimum execution time: 10_848_000 picoseconds. - Weight::from_parts(11_183_000, 3634) + // Measured: `0` + // Estimated: `3465` + // Minimum execution time: 6_574_000 picoseconds. + Weight::from_parts(6_790_000, 3465) .saturating_add(T::DbWeight::get().reads(1)) } pub(crate) fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_145_000 picoseconds. - Weight::from_parts(12_366_000, 0) + // Minimum execution time: 7_232_000 picoseconds. + Weight::from_parts(7_422_000, 0) } pub(crate) fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_837_000 picoseconds. - Weight::from_parts(2_939_000, 0) + // Minimum execution time: 1_180_000 picoseconds. + Weight::from_parts(1_250_000, 0) } pub(crate) fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_526_000 picoseconds. - Weight::from_parts(2_622_000, 0) + // Minimum execution time: 702_000 picoseconds. + Weight::from_parts(766_000, 0) } pub(crate) fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_603_000 picoseconds. - Weight::from_parts(2_642_000, 0) + // Minimum execution time: 700_000 picoseconds. + Weight::from_parts(757_000, 0) } pub(crate) fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_500_000 picoseconds. - Weight::from_parts(2_573_000, 0) + // Minimum execution time: 686_000 picoseconds. + Weight::from_parts(751_000, 0) } pub(crate) fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_323_000 picoseconds. - Weight::from_parts(3_401_000, 0) + // Minimum execution time: 705_000 picoseconds. + Weight::from_parts(765_000, 0) } pub(crate) fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_557_000 picoseconds. - Weight::from_parts(2_620_000, 0) + // Minimum execution time: 687_000 picoseconds. + Weight::from_parts(741_000, 0) } - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn report_error() -> Weight { // Proof Size summary in bytes: - // Measured: `169` - // Estimated: `3634` - // Minimum execution time: 25_828_000 picoseconds. - Weight::from_parts(26_318_000, 3634) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `351` + // Estimated: `6196` + // Minimum execution time: 65_398_000 picoseconds. + Weight::from_parts(67_140_000, 6196) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: XcmPallet AssetTraps (r:1 w:1) - /// Proof Skipped: XcmPallet AssetTraps (max_values: None, max_size: None, mode: Measured) + /// Storage: `XcmPallet::AssetTraps` (r:1 w:1) + /// Proof: `XcmPallet::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn claim_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `226` - // Estimated: `3691` - // Minimum execution time: 14_794_000 picoseconds. - Weight::from_parts(15_306_000, 3691) + // Measured: `23` + // Estimated: `3488` + // Minimum execution time: 9_653_000 picoseconds. + Weight::from_parts(9_944_000, 3488) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -176,165 +182,151 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_534_000 picoseconds. - Weight::from_parts(2_574_000, 0) + // Minimum execution time: 698_000 picoseconds. + Weight::from_parts(759_000, 0) } - /// Storage: XcmPallet VersionNotifyTargets (r:1 w:1) - /// Proof Skipped: XcmPallet VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:1 w:1) + /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn subscribe_version() -> Weight { // Proof Size summary in bytes: - // Measured: `169` - // Estimated: `3634` - // Minimum execution time: 32_218_000 picoseconds. - Weight::from_parts(32_945_000, 3634) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(5)) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 31_300_000 picoseconds. + Weight::from_parts(31_989_000, 3612) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: XcmPallet VersionNotifyTargets (r:0 w:1) - /// Proof Skipped: XcmPallet VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + /// Storage: `XcmPallet::VersionNotifyTargets` (r:0 w:1) + /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn unsubscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_983_000 picoseconds. - Weight::from_parts(5_132_000, 0) + // Minimum execution time: 2_863_000 picoseconds. + Weight::from_parts(3_027_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub(crate) fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_101_000 picoseconds. - Weight::from_parts(4_228_000, 0) + // Minimum execution time: 1_046_000 picoseconds. + Weight::from_parts(1_125_000, 0) } pub(crate) fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_740_000 picoseconds. - Weight::from_parts(2_814_000, 0) + // Minimum execution time: 811_000 picoseconds. + Weight::from_parts(871_000, 0) } pub(crate) fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_716_000 picoseconds. - Weight::from_parts(2_795_000, 0) + // Minimum execution time: 707_000 picoseconds. + Weight::from_parts(741_000, 0) } pub(crate) fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_550_000 picoseconds. - Weight::from_parts(2_601_000, 0) + // Minimum execution time: 687_000 picoseconds. + Weight::from_parts(741_000, 0) } pub(crate) fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_762_000 picoseconds. - Weight::from_parts(2_849_000, 0) + // Minimum execution time: 861_000 picoseconds. + Weight::from_parts(931_000, 0) } - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn query_pallet() -> Weight { // Proof Size summary in bytes: - // Measured: `169` - // Estimated: `3634` - // Minimum execution time: 31_709_000 picoseconds. - Weight::from_parts(32_288_000, 3634) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `351` + // Estimated: `6196` + // Minimum execution time: 74_622_000 picoseconds. + Weight::from_parts(77_059_000, 6196) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } pub(crate) fn expect_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_209_000 picoseconds. - Weight::from_parts(7_332_000, 0) + // Minimum execution time: 7_603_000 picoseconds. + Weight::from_parts(7_871_000, 0) } - /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) - /// Storage: Dmp DeliveryFeeFactor (r:1 w:0) - /// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet SupportedVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured) - /// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) - /// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: XcmPallet SafeXcmVersion (r:1 w:0) - /// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueues (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured) - /// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) - /// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) + /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) + /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) + /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn report_transact_status() -> Weight { // Proof Size summary in bytes: - // Measured: `169` - // Estimated: `3634` - // Minimum execution time: 26_161_000 picoseconds. - Weight::from_parts(26_605_000, 3634) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `351` + // Estimated: `6196` + // Minimum execution time: 65_617_000 picoseconds. + Weight::from_parts(66_719_000, 6196) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } pub(crate) fn clear_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_539_000 picoseconds. - Weight::from_parts(2_647_000, 0) + // Minimum execution time: 738_000 picoseconds. + Weight::from_parts(779_000, 0) } pub(crate) fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_494_000 picoseconds. - Weight::from_parts(2_588_000, 0) + // Minimum execution time: 688_000 picoseconds. + Weight::from_parts(755_000, 0) } pub(crate) fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_510_000 picoseconds. - Weight::from_parts(2_590_000, 0) + // Minimum execution time: 684_000 picoseconds. + Weight::from_parts(722_000, 0) } pub(crate) fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_491_000 picoseconds. - Weight::from_parts(2_546_000, 0) + // Minimum execution time: 694_000 picoseconds. + Weight::from_parts(738_000, 0) } pub(crate) fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_696_000 picoseconds. - Weight::from_parts(2_816_000, 0) + // Minimum execution time: 713_000 picoseconds. + Weight::from_parts(776_000, 0) } } diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs index d75083929a04511e104d8c36d7628fa5e329ec84..f8bb2676de3f9a884a6a0873879f2ddf175453f1 100644 --- a/polkadot/runtime/westend/src/xcm_config.rs +++ b/polkadot/runtime/westend/src/xcm_config.rs @@ -36,7 +36,7 @@ use sp_core::ConstU32; use westend_runtime_constants::{ currency::CENTS, system_parachain::*, xcm::body::FELLOWSHIP_ADMIN_INDEX, }; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, @@ -51,7 +51,7 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const TokenLocation: Location = Here.into_location(); pub const RootLocation: Location = Location::here(); - pub const ThisNetwork: NetworkId = Westend; + pub const ThisNetwork: NetworkId = ByGenesis(WESTEND_GENESIS_HASH); pub UniversalLocation: InteriorLocation = [GlobalConsensus(ThisNetwork::get())].into(); pub CheckAccount: AccountId = XcmPallet::check_account(); pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); diff --git a/polkadot/xcm/Cargo.toml b/polkadot/xcm/Cargo.toml index 862f5557a012a9bd1953607e774b36009c87d9e5..86c7067ad6fa8d9eac63df07a64a2a5e8488cdbe 100644 --- a/polkadot/xcm/Cargo.toml +++ b/polkadot/xcm/Cargo.toml @@ -23,11 +23,12 @@ serde = { features = ["alloc", "derive", "rc"], workspace = true } schemars = { default-features = true, optional = true, workspace = true } xcm-procedural = { workspace = true, default-features = true } environmental = { workspace = true } +hex-literal = { workspace = true, default-features = true } +frame-support = { workspace = true } [dev-dependencies] sp-io = { workspace = true, default-features = true } hex = { workspace = true, default-features = true } -hex-literal = { workspace = true, default-features = true } [features] default = ["std"] @@ -36,6 +37,7 @@ std = [ "bounded-collections/std", "codec/std", "environmental/std", + "frame-support/std", "log/std", "scale-info/std", "serde/std", diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs index f8a1826b8ab15bf00c1bd971b1a192b856dd67b4..0180354458ce448b4568a318417c3096644e5b87 100644 --- a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs @@ -21,7 +21,7 @@ use frame::{ runtime::prelude::*, traits::{Everything, Nothing}, }; -use xcm::v4::prelude::*; +use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs index 686f86b37b73204e877ab3c935797ce92540816e..cd8701dbbede153a043f28d0579c7090ac1f0746 100644 --- a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/mod.rs @@ -23,7 +23,7 @@ use frame::{ traits::{IdentityLookup, ProcessMessage, ProcessMessageError}, }; use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId}; -use xcm::v4::prelude::*; +use xcm::latest::prelude::*; mod xcm_config; pub use xcm_config::LocationToAccountId; diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs index e7b602df7338beffb9859c2196d2b046773824d0..06b00c39e8a04e72c1dcfaba354ec26dfd4db64e 100644 --- a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs @@ -21,7 +21,7 @@ use frame::{ runtime::prelude::*, traits::{Everything, Nothing}, }; -use xcm::v4::prelude::*; +use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs index 792cf6149e7cb418f5ffa5720f41ae44956ff036..b7fdaa34ec8ca5776a7ce573ab504665905cc213 100644 --- a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/tests.rs @@ -65,9 +65,9 @@ fn reserve_asset_transfers_work() { let assets: Assets = (Here, 50u128 * CENTS as u128).into(); assert_ok!(relay_chain::XcmPallet::transfer_assets( relay_chain::RuntimeOrigin::signed(ALICE), - Box::new(VersionedLocation::V4(destination.clone())), - Box::new(VersionedLocation::V4(beneficiary)), - Box::new(VersionedAssets::V4(assets)), + Box::new(VersionedLocation::from(destination.clone())), + Box::new(VersionedLocation::from(beneficiary)), + Box::new(VersionedAssets::from(assets)), 0, WeightLimit::Unlimited, )); @@ -101,9 +101,9 @@ fn reserve_asset_transfers_work() { let assets: Assets = (Parent, 25u128 * CENTS as u128).into(); assert_ok!(parachain::XcmPallet::transfer_assets( parachain::RuntimeOrigin::signed(BOB), - Box::new(VersionedLocation::V4(destination)), - Box::new(VersionedLocation::V4(beneficiary)), - Box::new(VersionedAssets::V4(assets)), + Box::new(VersionedLocation::from(destination)), + Box::new(VersionedLocation::from(beneficiary)), + Box::new(VersionedAssets::from(assets)), 0, WeightLimit::Unlimited, )); diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs index 6ce49074a6e2b1fa02dd0f1fbe4f90db1058e560..303ff9493f71b6a6411427146d93774209850e5c 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs @@ -24,7 +24,7 @@ use frame_support::{ weights::Weight, }; use sp_runtime::traits::{Bounded, Zero}; -use xcm::latest::{prelude::*, MAX_ITEMS_IN_ASSETS}; +use xcm::latest::{prelude::*, AssetTransferFilter, MAX_ITEMS_IN_ASSETS}; use xcm_executor::traits::{ConvertLocation, FeeReason, TransactAsset}; benchmarks_instance_pallet! { @@ -299,6 +299,33 @@ benchmarks_instance_pallet! { } } + initiate_transfer { + let (sender_account, sender_location) = account_and_location::(1); + let asset = T::get_asset(); + let mut holding = T::worst_case_holding(1); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + + // Add our asset to the holding. + holding.push(asset.clone()); + + let mut executor = new_executor::(sender_location); + executor.set_holding(holding.into()); + let instruction = Instruction::>::InitiateTransfer { + destination: T::valid_destination()?, + // ReserveDeposit is the most expensive filter. + remote_fees: Some(AssetTransferFilter::ReserveDeposit(asset.clone().into())), + // It's more expensive if we reanchor the origin. + preserve_origin: true, + assets: vec![AssetTransferFilter::ReserveDeposit(asset.into())], + remote_xcm: Xcm::new(), + }; + let xcm = Xcm(vec![instruction]); + }: { + executor.bench_process(xcm)?; + } verify { + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); + } + impl_benchmark_test_suite!( Pallet, crate::fungible::mock::new_test_ext(), diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs index f1ec3f604d7b27158596ac8ee95b1708e1eec8a5..0d80ef89a1ce17fdd56d2f063fd9d74762617a61 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs @@ -19,7 +19,7 @@ use crate::{account_and_location, new_executor, EnsureDelivery, XcmCallOf}; use alloc::{vec, vec::Vec}; use codec::Encode; use frame_benchmarking::{benchmarks, BenchmarkError}; -use frame_support::{dispatch::GetDispatchInfo, traits::fungible::Inspect}; +use frame_support::traits::fungible::Inspect; use xcm::{ latest::{prelude::*, MaxDispatchErrorLen, MaybeErrorCode, Weight, MAX_ITEMS_IN_ASSETS}, DoubleEncoded, @@ -98,6 +98,36 @@ benchmarks! { } + pay_fees { + let holding = T::worst_case_holding(0).into(); + + let mut executor = new_executor::(Default::default()); + executor.set_holding(holding); + // Set some weight to be paid for. + executor.set_message_weight(Weight::from_parts(100_000_000, 100_000)); + + let fee_asset: Asset = T::fee_asset().unwrap(); + + let instruction = Instruction::>::PayFees { asset: fee_asset }; + + let xcm = Xcm(vec![instruction]); + } : { + executor.bench_process(xcm)?; + } verify {} + + set_asset_claimer { + let mut executor = new_executor::(Default::default()); + let (_, sender_location) = account_and_location::(1); + + let instruction = Instruction::SetAssetClaimer{ location:sender_location.clone() }; + + let xcm = Xcm(vec![instruction]); + }: { + executor.bench_process(xcm)?; + } verify { + assert_eq!(executor.asset_claimer(), Some(sender_location.clone())); + } + query_response { let mut executor = new_executor::(Default::default()); let (query_id, response) = T::worst_case_response(); @@ -121,7 +151,6 @@ benchmarks! { let instruction = Instruction::Transact { origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: noop_call.get_dispatch_info().call_weight, call: double_encoded_noop_call, }; let xcm = Xcm(vec![instruction]); diff --git a/polkadot/xcm/pallet-xcm/src/benchmarking.rs b/polkadot/xcm/pallet-xcm/src/benchmarking.rs index 404b9358d4d916a1dad2196896db714a0c542656..e493d4838f5ca4a7a6bca0c8a5def791046f84ef 100644 --- a/polkadot/xcm/pallet-xcm/src/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm/src/benchmarking.rs @@ -382,8 +382,8 @@ benchmarks! { asset.clone().into(), &XcmContext { origin: None, message_id: [0u8; 32], topic: None } ); - let versioned_assets = VersionedAssets::V4(asset.into()); - }: _>(claim_origin.into(), Box::new(versioned_assets), Box::new(VersionedLocation::V4(claim_location))) + let versioned_assets = VersionedAssets::from(Assets::from(asset)); + }: _>(claim_origin.into(), Box::new(versioned_assets), Box::new(VersionedLocation::from(claim_location))) impl_benchmark_test_suite!( Pallet, diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 9b8f735b478f2a99231300bc7e82ea159413d412..4a97546b38d1c446235fcea532a3a91b59b76e77 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -2536,7 +2536,7 @@ impl Pallet { /// /// Returns execution result, events, and any forwarded XCMs to other locations. /// Meant to be used in the `xcm_runtime_apis::dry_run::DryRunApi` runtime API. - pub fn dry_run_xcm( + pub fn dry_run_xcm( origin_location: VersionedLocation, xcm: VersionedXcm, ) -> Result::RuntimeEvent>, XcmDryRunApiError> @@ -3067,7 +3067,7 @@ impl xcm_executor::traits::AssetLock for Pallet { } impl WrapVersion for Pallet { - fn wrap_version( + fn wrap_version( dest: &Location, xcm: impl Into>, ) -> Result, ()> { diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index e98a8f8d2ce7fba605aab9dbfab6b4072a6e251a..350530f7711f8e4f6a89b1d94479229cf9bedb53 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -482,14 +482,14 @@ fn claim_assets_works() { // Even though assets are trapped, the extrinsic returns success. assert_ok!(XcmPallet::execute( RuntimeOrigin::signed(ALICE), - Box::new(VersionedXcm::V4(trapping_program)), + Box::new(VersionedXcm::from(trapping_program)), BaseXcmWeight::get() * 2, )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); // Expected `AssetsTrapped` event info. let source: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); - let versioned_assets = VersionedAssets::V4(Assets::from((Here, SEND_AMOUNT))); + let versioned_assets = VersionedAssets::from(Assets::from((Here, SEND_AMOUNT))); let hash = BlakeTwo256::hash_of(&(source.clone(), versioned_assets.clone())); // Assets were indeed trapped. @@ -512,10 +512,11 @@ fn claim_assets_works() { // Now claim them with the extrinsic. assert_ok!(XcmPallet::claim_assets( RuntimeOrigin::signed(ALICE), - Box::new(VersionedAssets::V4((Here, SEND_AMOUNT).into())), - Box::new(VersionedLocation::V4( - AccountId32 { network: None, id: ALICE.clone().into() }.into() - )), + Box::new(VersionedAssets::from(Assets::from((Here, SEND_AMOUNT)))), + Box::new(VersionedLocation::from(Location::from(AccountId32 { + network: None, + id: ALICE.clone().into() + }))), )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_eq!(AssetTraps::::iter().collect::>(), vec![]); diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs index 09ead1389d19da15f167793b041b9ef849c0e741..b65290332af9808809fbca4b42deb77f63d90696 100644 --- a/polkadot/xcm/procedural/src/builder_pattern.rs +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -160,13 +160,16 @@ fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result>>()?; @@ -260,50 +263,75 @@ fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result, _>>()?; // Then we require fees to be paid - let buy_execution_method = data_enum + let pay_fees_variants = data_enum .variants .iter() - .find(|variant| variant.ident == "BuyExecution") - .map_or( - Err(Error::new_spanned(&data_enum.variants, "No BuyExecution instruction")), - |variant| { - let variant_name = &variant.ident; - let method_name_string = &variant_name.to_string().to_snake_case(); - let method_name = syn::Ident::new(method_name_string, variant_name.span()); - let docs = get_doc_comments(variant); - let fields = match &variant.fields { - Fields::Named(fields) => { - let arg_names: Vec<_> = - fields.named.iter().map(|field| &field.ident).collect(); - let arg_types: Vec<_> = - fields.named.iter().map(|field| &field.ty).collect(); - quote! { - #(#docs)* - pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder { - let mut new_instructions = self.instructions; - #(let #arg_names = #arg_names.into();)* - new_instructions.push(#name::::#variant_name { #(#arg_names),* }); - XcmBuilder { - instructions: new_instructions, - state: core::marker::PhantomData, - } + .map(|variant| { + let maybe_builder_attr = variant.attrs.iter().find(|attr| match attr.meta { + Meta::List(ref list) => list.path.is_ident("builder"), + _ => false, + }); + let builder_attr = match maybe_builder_attr { + Some(builder) => builder.clone(), + None => return Ok(None), /* It's not going to be an instruction that pays fees */ + }; + let Meta::List(ref list) = builder_attr.meta else { unreachable!("We checked before") }; + let inner_ident: Ident = syn::parse2(list.tokens.clone()).map_err(|_| { + Error::new_spanned( + &builder_attr, + "Expected `builder(loads_holding)` or `builder(pays_fees)`", + ) + })?; + let ident_to_match: Ident = syn::parse_quote!(pays_fees); + if inner_ident == ident_to_match { + Ok(Some(variant)) + } else { + Ok(None) // Must have been `loads_holding` instead. + } + }) + .collect::>>()?; + + let pay_fees_methods = pay_fees_variants + .into_iter() + .flatten() + .map(|variant| { + let variant_name = &variant.ident; + let method_name_string = &variant_name.to_string().to_snake_case(); + let method_name = syn::Ident::new(method_name_string, variant_name.span()); + let docs = get_doc_comments(variant); + let fields = match &variant.fields { + Fields::Named(fields) => { + let arg_names: Vec<_> = + fields.named.iter().map(|field| &field.ident).collect(); + let arg_types: Vec<_> = + fields.named.iter().map(|field| &field.ty).collect(); + quote! { + #(#docs)* + pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> XcmBuilder { + let mut new_instructions = self.instructions; + #(let #arg_names = #arg_names.into();)* + new_instructions.push(#name::::#variant_name { #(#arg_names),* }); + XcmBuilder { + instructions: new_instructions, + state: core::marker::PhantomData, } } - }, - _ => - return Err(Error::new_spanned( - variant, - "BuyExecution should have named fields", - )), - }; - Ok(fields) - }, - )?; + } + }, + _ => + return Err(Error::new_spanned( + variant, + "Both BuyExecution and PayFees have named fields", + )), + }; + Ok(fields) + }) + .collect::>>()?; let second_impl = quote! { impl XcmBuilder { #(#allowed_after_load_holding_methods)* - #buy_execution_method + #(#pay_fees_methods)* } }; diff --git a/polkadot/xcm/procedural/src/lib.rs b/polkadot/xcm/procedural/src/lib.rs index 4980d84d3282a02b0f910b9b3b91bc876f9249a7..9971fdceb69a678ccd80f8191273ba6bfdef66f7 100644 --- a/polkadot/xcm/procedural/src/lib.rs +++ b/polkadot/xcm/procedural/src/lib.rs @@ -20,25 +20,11 @@ use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; mod builder_pattern; -mod v2; mod v3; mod v4; +mod v5; mod weight_info; -#[proc_macro] -pub fn impl_conversion_functions_for_multilocation_v2(input: TokenStream) -> TokenStream { - v2::multilocation::generate_conversion_functions(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - -#[proc_macro] -pub fn impl_conversion_functions_for_junctions_v2(input: TokenStream) -> TokenStream { - v2::junctions::generate_conversion_functions(input) - .unwrap_or_else(syn::Error::into_compile_error) - .into() -} - #[proc_macro_derive(XcmWeightInfoTrait)] pub fn derive_xcm_weight_info(item: TokenStream) -> TokenStream { weight_info::derive(item) @@ -72,6 +58,20 @@ pub fn impl_conversion_functions_for_junctions_v4(input: TokenStream) -> TokenSt .into() } +#[proc_macro] +pub fn impl_conversion_functions_for_junctions_v5(input: TokenStream) -> TokenStream { + v5::junctions::generate_conversion_functions(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + +#[proc_macro] +pub fn impl_conversion_functions_for_location_v5(input: TokenStream) -> TokenStream { + v5::location::generate_conversion_functions(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + /// This is called on the `Instruction` enum, not on the `Xcm` struct, /// and allows for the following syntax for building XCMs: /// let message = Xcm::builder() diff --git a/polkadot/xcm/procedural/src/v2.rs b/polkadot/xcm/procedural/src/v2.rs deleted file mode 100644 index 6878f7755cc70b9b540074a63480615338e456aa..0000000000000000000000000000000000000000 --- a/polkadot/xcm/procedural/src/v2.rs +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -use proc_macro2::{Span, TokenStream}; -use quote::{format_ident, quote}; -use syn::{Result, Token}; - -pub mod multilocation { - use super::*; - - pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result { - if !input.is_empty() { - return Err(syn::Error::new(Span::call_site(), "No arguments expected")) - } - - // Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents. - let from_tuples = generate_conversion_from_tuples(8); - let from_v3 = generate_conversion_from_v3(); - - Ok(quote! { - #from_tuples - #from_v3 - }) - } - - fn generate_conversion_from_tuples(max_parents: u8) -> TokenStream { - let mut from_tuples = (0..8usize) - .map(|num_junctions| { - let junctions = - (0..=num_junctions).map(|_| format_ident!("Junction")).collect::>(); - let idents = - (0..=num_junctions).map(|i| format_ident!("j{}", i)).collect::>(); - let variant = &format_ident!("X{}", num_junctions + 1); - let array_size = num_junctions + 1; - - let mut from_tuple = quote! { - impl From<( #(#junctions,)* )> for MultiLocation { - fn from( ( #(#idents,)* ): ( #(#junctions,)* ) ) -> Self { - MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) } - } - } - - impl From<(u8, #(#junctions),*)> for MultiLocation { - fn from( ( parents, #(#idents),* ): (u8, #(#junctions),* ) ) -> Self { - MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) } - } - } - - impl From<(Ancestor, #(#junctions),*)> for MultiLocation { - fn from( ( Ancestor(parents), #(#idents),* ): (Ancestor, #(#junctions),* ) ) -> Self { - MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) } - } - } - - impl From<[Junction; #array_size]> for MultiLocation { - fn from(j: [Junction; #array_size]) -> Self { - let [#(#idents),*] = j; - MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) } - } - } - }; - - let from_parent_tuples = (1..=max_parents).map(|cur_parents| { - let parents = - (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); - let underscores = - (0..cur_parents).map(|_| Token![_](Span::call_site())).collect::>(); - - quote! { - impl From<( #(#parents,)* #(#junctions),* )> for MultiLocation { - fn from( (#(#underscores,)* #(#idents),*): ( #(#parents,)* #(#junctions),* ) ) -> Self { - MultiLocation { parents: #cur_parents, interior: Junctions::#variant( #(#idents),* ) } - } - } - } - }); - - from_tuple.extend(from_parent_tuples); - from_tuple - }) - .collect::(); - - let from_parent_junctions_tuples = (1..=max_parents).map(|cur_parents| { - let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); - let underscores = - (0..cur_parents).map(|_| Token![_](Span::call_site())).collect::>(); - - quote! { - impl From<( #(#parents,)* Junctions )> for MultiLocation { - fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self { - MultiLocation { parents: #cur_parents, interior: junctions } - } - } - } - }); - from_tuples.extend(from_parent_junctions_tuples); - - quote! { - impl From for MultiLocation { - fn from(junctions: Junctions) -> Self { - MultiLocation { parents: 0, interior: junctions } - } - } - - impl From<(u8, Junctions)> for MultiLocation { - fn from((parents, interior): (u8, Junctions)) -> Self { - MultiLocation { parents, interior } - } - } - - impl From<(Ancestor, Junctions)> for MultiLocation { - fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self { - MultiLocation { parents, interior } - } - } - - impl From<()> for MultiLocation { - fn from(_: ()) -> Self { - MultiLocation { parents: 0, interior: Junctions::Here } - } - } - - impl From<(u8,)> for MultiLocation { - fn from((parents,): (u8,)) -> Self { - MultiLocation { parents, interior: Junctions::Here } - } - } - - impl From for MultiLocation { - fn from(x: Junction) -> Self { - MultiLocation { parents: 0, interior: Junctions::X1(x) } - } - } - - impl From<[Junction; 0]> for MultiLocation { - fn from(_: [Junction; 0]) -> Self { - MultiLocation { parents: 0, interior: Junctions::Here } - } - } - - #from_tuples - } - } - - fn generate_conversion_from_v3() -> TokenStream { - let match_variants = (0..8u8) - .map(|cur_num| { - let num_ancestors = cur_num + 1; - let variant = format_ident!("X{}", num_ancestors); - let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::>(); - - quote! { - crate::v3::Junctions::#variant( #(#idents),* ) => - #variant( #( core::convert::TryInto::try_into(#idents)? ),* ), - } - }) - .collect::(); - - quote! { - impl core::convert::TryFrom for Junctions { - type Error = (); - fn try_from(mut new: crate::v3::Junctions) -> core::result::Result { - use Junctions::*; - Ok(match new { - crate::v3::Junctions::Here => Here, - #match_variants - }) - } - } - } - } -} - -pub mod junctions { - use super::*; - - pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result { - if !input.is_empty() { - return Err(syn::Error::new(Span::call_site(), "No arguments expected")) - } - - let from_slice_syntax = generate_conversion_from_slice_syntax(); - - Ok(quote! { - #from_slice_syntax - }) - } - - fn generate_conversion_from_slice_syntax() -> TokenStream { - quote! { - macro_rules! impl_junction { - ($count:expr, $variant:ident, ($($index:literal),+)) => { - /// Additional helper for building junctions - /// Useful for converting to future XCM versions - impl From<[Junction; $count]> for Junctions { - fn from(junctions: [Junction; $count]) -> Self { - Self::$variant($(junctions[$index].clone()),*) - } - } - }; - } - - impl_junction!(1, X1, (0)); - impl_junction!(2, X2, (0, 1)); - impl_junction!(3, X3, (0, 1, 2)); - impl_junction!(4, X4, (0, 1, 2, 3)); - impl_junction!(5, X5, (0, 1, 2, 3, 4)); - impl_junction!(6, X6, (0, 1, 2, 3, 4, 5)); - impl_junction!(7, X7, (0, 1, 2, 3, 4, 5, 6)); - impl_junction!(8, X8, (0, 1, 2, 3, 4, 5, 6, 7)); - } - } -} diff --git a/polkadot/xcm/procedural/src/v3.rs b/polkadot/xcm/procedural/src/v3.rs index f0556d5a8d447389b383dfa3ac3636f2ec77bdf5..1292b56277dd8a87a9cdf502f6c730b48a0bc899 100644 --- a/polkadot/xcm/procedural/src/v3.rs +++ b/polkadot/xcm/procedural/src/v3.rs @@ -127,12 +127,10 @@ pub mod junctions { } // Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents. - let from_v2 = generate_conversion_from_v2(MAX_JUNCTIONS); let from_v4 = generate_conversion_from_v4(); let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS); Ok(quote! { - #from_v2 #from_v4 #from_tuples }) @@ -194,32 +192,4 @@ pub mod junctions { } } } - - fn generate_conversion_from_v2(max_junctions: usize) -> TokenStream { - let match_variants = (0..max_junctions) - .map(|cur_num| { - let num_ancestors = cur_num + 1; - let variant = format_ident!("X{}", num_ancestors); - let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::>(); - - quote! { - crate::v2::Junctions::#variant( #(#idents),* ) => - #variant( #( core::convert::TryInto::try_into(#idents)? ),* ), - } - }) - .collect::(); - - quote! { - impl core::convert::TryFrom for Junctions { - type Error = (); - fn try_from(mut old: crate::v2::Junctions) -> core::result::Result { - use Junctions::*; - Ok(match old { - crate::v2::Junctions::Here => Here, - #match_variants - }) - } - } - } - } } diff --git a/polkadot/xcm/procedural/src/v4.rs b/polkadot/xcm/procedural/src/v4.rs index 5f5e10d3081b39a3fe5e02f312ba5f487509d9e6..9bc2f094d0213d8b26702fc017de930c9b4426d3 100644 --- a/polkadot/xcm/procedural/src/v4.rs +++ b/polkadot/xcm/procedural/src/v4.rs @@ -132,10 +132,12 @@ pub mod junctions { // Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents. let from_v3 = generate_conversion_from_v3(MAX_JUNCTIONS); + let from_v5 = generate_conversion_from_v5(MAX_JUNCTIONS); let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS); Ok(quote! { #from_v3 + #from_v5 #from_tuples }) } @@ -193,4 +195,43 @@ pub mod junctions { } } } + + fn generate_conversion_from_v5(max_junctions: usize) -> TokenStream { + let match_variants = (0..max_junctions) + .map(|current_number| { + let number_ancestors = current_number + 1; + let variant = format_ident!("X{}", number_ancestors); + let idents = + (0..=current_number).map(|i| format_ident!("j{}", i)).collect::>(); + let convert = idents + .iter() + .map(|ident| { + quote! { let #ident = core::convert::TryInto::try_into(#ident.clone())?; } + }) + .collect::>(); + + quote! { + crate::v5::Junctions::#variant( junctions ) => { + let [#(#idents),*] = &*junctions; + #(#convert);* + [#(#idents),*].into() + }, + } + }) + .collect::(); + + quote! { + impl core::convert::TryFrom for Junctions { + type Error = (); + + fn try_from(mut new: crate::v5::Junctions) -> core::result::Result { + use Junctions::*; + Ok(match new { + crate::v5::Junctions::Here => Here, + #match_variants + }) + } + } + } + } } diff --git a/polkadot/xcm/procedural/src/v5.rs b/polkadot/xcm/procedural/src/v5.rs new file mode 100644 index 0000000000000000000000000000000000000000..895a323c17383d4f61b226e2bb089d76649bca1f --- /dev/null +++ b/polkadot/xcm/procedural/src/v5.rs @@ -0,0 +1,198 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote}; +use syn::{Result, Token}; + +const MAX_JUNCTIONS: usize = 8; + +pub mod location { + use super::*; + + /// Generates conversion functions from other types to the `Location` type: + /// - [PalletInstance(50), GeneralIndex(1984)].into() + /// - (Parent, Parachain(1000), AccountId32 { .. }).into() + pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result { + if !input.is_empty() { + return Err(syn::Error::new(Span::call_site(), "No arguments expected")) + } + + let from_tuples = generate_conversion_from_tuples(8, 8); + + Ok(quote! { + #from_tuples + }) + } + + fn generate_conversion_from_tuples(max_junctions: usize, max_parents: usize) -> TokenStream { + let mut from_tuples = (0..=max_junctions) + .map(|num_junctions| { + let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::>(); + let idents = + (0..num_junctions).map(|i| format_ident!("j{}", i)).collect::>(); + let array_size = num_junctions; + let interior = if num_junctions == 0 { + quote!(Junctions::Here) + } else { + let variant = format_ident!("X{}", num_junctions); + quote! { + Junctions::#variant( alloc::sync::Arc::new( [#(#idents .into()),*] ) ) + } + }; + + let mut from_tuple = quote! { + impl< #(#types : Into,)* > From<( Ancestor, #( #types ),* )> for Location { + fn from( ( Ancestor(parents), #(#idents),* ): ( Ancestor, #( #types ),* ) ) -> Self { + Location { parents, interior: #interior } + } + } + + impl From<[Junction; #array_size]> for Location { + fn from(j: [Junction; #array_size]) -> Self { + let [#(#idents),*] = j; + Location { parents: 0, interior: #interior } + } + } + }; + + let from_parent_tuples = (0..=max_parents).map(|cur_parents| { + let parents = + (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); + let underscores = + (0..cur_parents).map(|_| Token![_](Span::call_site())).collect::>(); + + quote! { + impl< #(#types : Into,)* > From<( #( #parents , )* #( #types , )* )> for Location { + fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self { + Self { parents: #cur_parents as u8, interior: #interior } + } + } + } + }); + + from_tuple.extend(from_parent_tuples); + from_tuple + }) + .collect::(); + + let from_parent_junctions_tuples = (0..=max_parents).map(|cur_parents| { + let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::>(); + let underscores = + (0..cur_parents).map(|_| Token![_](Span::call_site())).collect::>(); + + quote! { + impl From<( #(#parents,)* Junctions )> for Location { + fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self { + Location { parents: #cur_parents as u8, interior: junctions } + } + } + } + }); + from_tuples.extend(from_parent_junctions_tuples); + + quote! { + impl From<(Ancestor, Junctions)> for Location { + fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self { + Location { parents, interior } + } + } + + impl From for Location { + fn from(x: Junction) -> Self { + Location { parents: 0, interior: [x].into() } + } + } + + #from_tuples + } + } +} + +pub mod junctions { + use super::*; + + pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result { + if !input.is_empty() { + return Err(syn::Error::new(Span::call_site(), "No arguments expected")) + } + + // Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents. + let from_v4 = generate_conversion_from_v4(MAX_JUNCTIONS); + let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS); + + Ok(quote! { + #from_v4 + #from_tuples + }) + } + + fn generate_conversion_from_tuples(max_junctions: usize) -> TokenStream { + (1..=max_junctions) + .map(|num_junctions| { + let idents = + (0..num_junctions).map(|i| format_ident!("j{}", i)).collect::>(); + let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::>(); + + quote! { + impl<#(#types : Into,)*> From<( #(#types,)* )> for Junctions { + fn from( ( #(#idents,)* ): ( #(#types,)* ) ) -> Self { + [#(#idents .into()),*].into() + } + } + } + }) + .collect() + } + + fn generate_conversion_from_v4(max_junctions: usize) -> TokenStream { + let match_variants = (0..max_junctions) + .map(|cur_num| { + let num_ancestors = cur_num + 1; + let variant = format_ident!("X{}", num_ancestors); + let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::>(); + let convert = idents + .iter() + .enumerate() + .map(|(index, ident)| { + quote! { let #ident = core::convert::TryInto::try_into(slice[#index].clone())?; } + }) + .collect::>(); + + quote! { + crate::v4::Junctions::#variant( arc ) => { + let slice = &arc[..]; + #(#convert);*; + let junctions: Junctions = [#(#idents),*].into(); + junctions + }, + } + }) + .collect::(); + + quote! { + impl core::convert::TryFrom for Junctions { + type Error = (); + fn try_from(mut old: crate::v4::Junctions) -> core::result::Result { + Ok(match old { + crate::v4::Junctions::Here => Junctions::Here, + #match_variants + }) + } + } + } + } +} diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/badly_formatted_attribute.stderr b/polkadot/xcm/procedural/tests/ui/builder_pattern/badly_formatted_attribute.stderr index 978faf2e868d89ea47276bb5a7fed40c529e6336..e4038dc25ae64ae7d3736691b64be4b0ec08bb69 100644 --- a/polkadot/xcm/procedural/tests/ui/builder_pattern/badly_formatted_attribute.stderr +++ b/polkadot/xcm/procedural/tests/ui/builder_pattern/badly_formatted_attribute.stderr @@ -1,4 +1,4 @@ -error: Expected `builder(loads_holding)` +error: Expected `builder(loads_holding)` or `builder(pays_fees)` --> tests/ui/builder_pattern/badly_formatted_attribute.rs:25:5 | 25 | #[builder(funds_holding = 2)] diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/buy_execution_named_fields.rs b/polkadot/xcm/procedural/tests/ui/builder_pattern/buy_execution_named_fields.rs deleted file mode 100644 index dc5c679a96e72b92c0095e246ef487132dad4f69..0000000000000000000000000000000000000000 --- a/polkadot/xcm/procedural/tests/ui/builder_pattern/buy_execution_named_fields.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Test error when the `BuyExecution` instruction doesn't take named fields. - -use xcm_procedural::Builder; - -struct Xcm(pub Vec>); - -#[derive(Builder)] -enum Instruction { - BuyExecution(u128), - UnpaidExecution { weight_limit: (u32, u32) }, - Transact { call: Call }, -} - -fn main() {} diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/buy_execution_named_fields.stderr b/polkadot/xcm/procedural/tests/ui/builder_pattern/buy_execution_named_fields.stderr deleted file mode 100644 index dc8246770ba3e10ed0df45a714dd2d9eb337cc5e..0000000000000000000000000000000000000000 --- a/polkadot/xcm/procedural/tests/ui/builder_pattern/buy_execution_named_fields.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: BuyExecution should have named fields - --> tests/ui/builder_pattern/buy_execution_named_fields.rs:25:5 - | -25 | BuyExecution(u128), - | ^^^^^^^^^^^^^^^^^^ diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/no_buy_execution.stderr b/polkadot/xcm/procedural/tests/ui/builder_pattern/no_buy_execution.stderr deleted file mode 100644 index d8798c8223f18e74e8ec6f409923da482547c9ec..0000000000000000000000000000000000000000 --- a/polkadot/xcm/procedural/tests/ui/builder_pattern/no_buy_execution.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: No BuyExecution instruction - --> tests/ui/builder_pattern/no_buy_execution.rs:25:5 - | -25 | / UnpaidExecution { weight_limit: (u32, u32) }, -26 | | Transact { call: Call }, - | |____________________________^ diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.rs b/polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.rs deleted file mode 100644 index 5808ec571ce75f3d7e25ae8489137ac90ced5687..0000000000000000000000000000000000000000 --- a/polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Test error when using wrong attribute. - -use xcm_procedural::Builder; - -struct Xcm(pub Vec>); - -#[derive(Builder)] -enum Instruction { - #[builder(funds_holding)] - WithdrawAsset(u128), - BuyExecution { fees: u128 }, - UnpaidExecution { weight_limit: (u32, u32) }, - Transact { call: Call }, -} - -fn main() {} diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.stderr b/polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.stderr deleted file mode 100644 index 1ff9d18513686293bc56f438c2bd7fa543820c84..0000000000000000000000000000000000000000 --- a/polkadot/xcm/procedural/tests/ui/builder_pattern/unexpected_attribute.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Expected `builder(loads_holding)` - --> tests/ui/builder_pattern/unexpected_attribute.rs:25:5 - | -25 | #[builder(funds_holding)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/polkadot/xcm/src/lib.rs b/polkadot/xcm/src/lib.rs index 0b916c87f549c6cfc8bbc50a43d0e6e488142e4a..a41a8e797b0f703302f62be04769c07ad2b9c3c4 100644 --- a/polkadot/xcm/src/lib.rs +++ b/polkadot/xcm/src/lib.rs @@ -21,28 +21,24 @@ // // Hence, `no_std` rather than sp-runtime. #![cfg_attr(not(feature = "std"), no_std)] -// Because of XCMv2. -#![allow(deprecated)] extern crate alloc; use codec::{Decode, DecodeLimit, Encode, Error as CodecError, Input, MaxEncodedLen}; use derivative::Derivative; +use frame_support::dispatch::GetDispatchInfo; use scale_info::TypeInfo; -#[deprecated( - note = "XCMv2 will be removed once XCMv5 is released. Please use XCMv3 or XCMv4 instead." -)] -pub mod v2; pub mod v3; pub mod v4; +pub mod v5; pub mod lts { pub use super::v4::*; } pub mod latest { - pub use super::v4::*; + pub use super::v5::*; } mod double_encoded; @@ -81,12 +77,16 @@ pub trait TryAs { fn try_as(&self) -> Result<&T, ()>; } +// Macro that generated versioned wrapper types. +// NOTE: converting a v4 type into a versioned type will make it v5. macro_rules! versioned_type { ($(#[$attr:meta])* pub enum $n:ident { $(#[$index3:meta])+ V3($v3:ty), $(#[$index4:meta])+ V4($v4:ty), + $(#[$index5:meta])+ + V5($v5:ty), }) => { #[derive(Derivative, Encode, Decode, TypeInfo)] #[derivative( @@ -104,6 +104,8 @@ macro_rules! versioned_type { V3($v3), $(#[$index4])* V4($v4), + $(#[$index5])* + V5($v5), } impl $n { pub fn try_as(&self) -> Result<&T, ()> where Self: TryAs { @@ -126,11 +128,20 @@ macro_rules! versioned_type { } } } + impl TryAs<$v5> for $n { + fn try_as(&self) -> Result<&$v5, ()> { + match &self { + Self::V5(ref x) => Ok(x), + _ => Err(()), + } + } + } impl IntoVersion for $n { fn into_version(self, n: Version) -> Result { Ok(match n { 3 => Self::V3(self.try_into()?), 4 => Self::V4(self.try_into()?), + 5 => Self::V5(self.try_into()?), _ => return Err(()), }) } @@ -140,9 +151,9 @@ macro_rules! versioned_type { $n::V3(x.into()) } } - impl From<$v4> for $n { - fn from(x: $v4) -> Self { - $n::V4(x.into()) + impl> From for $n { + fn from(x: T) -> Self { + $n::V5(x.into()) } } impl TryFrom<$n> for $v3 { @@ -151,7 +162,11 @@ macro_rules! versioned_type { use $n::*; match x { V3(x) => Ok(x), - V4(x) => x.try_into(), + V4(x) => x.try_into().map_err(|_| ()), + V5(x) => { + let v4: $v4 = x.try_into().map_err(|_| ())?; + v4.try_into().map_err(|_| ()) + } } } } @@ -162,137 +177,21 @@ macro_rules! versioned_type { match x { V3(x) => x.try_into().map_err(|_| ()), V4(x) => Ok(x), + V5(x) => x.try_into().map_err(|_| ()), } } } - impl MaxEncodedLen for $n { - fn max_encoded_len() -> usize { - <$v3>::max_encoded_len() - } - } - impl IdentifyVersion for $n { - fn identify_version(&self) -> Version { - use $n::*; - match self { - V3(_) => v3::VERSION, - V4(_) => v4::VERSION, - } - } - } - }; - - ($(#[$attr:meta])* pub enum $n:ident { - $(#[$index2:meta])+ - V2($v2:ty), - $(#[$index3:meta])+ - V3($v3:ty), - $(#[$index4:meta])+ - V4($v4:ty), - }) => { - #[derive(Derivative, Encode, Decode, TypeInfo)] - #[derivative( - Clone(bound = ""), - Eq(bound = ""), - PartialEq(bound = ""), - Debug(bound = "") - )] - #[codec(encode_bound())] - #[codec(decode_bound())] - #[scale_info(replace_segment("staging_xcm", "xcm"))] - $(#[$attr])* - pub enum $n { - $(#[$index2])* - V2($v2), - $(#[$index3])* - V3($v3), - $(#[$index4])* - V4($v4), - } - impl $n { - pub fn try_as(&self) -> Result<&T, ()> where Self: TryAs { - >::try_as(&self) - } - } - impl TryAs<$v2> for $n { - fn try_as(&self) -> Result<&$v2, ()> { - match &self { - Self::V2(ref x) => Ok(x), - _ => Err(()), - } - } - } - impl TryAs<$v3> for $n { - fn try_as(&self) -> Result<&$v3, ()> { - match &self { - Self::V3(ref x) => Ok(x), - _ => Err(()), - } - } - } - impl TryAs<$v4> for $n { - fn try_as(&self) -> Result<&$v4, ()> { - match &self { - Self::V4(ref x) => Ok(x), - _ => Err(()), - } - } - } - impl IntoVersion for $n { - fn into_version(self, n: Version) -> Result { - Ok(match n { - 1 | 2 => Self::V2(self.try_into()?), - 3 => Self::V3(self.try_into()?), - 4 => Self::V4(self.try_into()?), - _ => return Err(()), - }) - } - } - impl From<$v2> for $n { - fn from(x: $v2) -> Self { - $n::V2(x) - } - } - impl> From for $n { - fn from(x: T) -> Self { - $n::V4(x.into()) - } - } - impl TryFrom<$n> for $v2 { + impl TryFrom<$n> for $v5 { type Error = (); fn try_from(x: $n) -> Result { use $n::*; match x { - V2(x) => Ok(x), - V3(x) => x.try_into(), - V4(x) => { - let v3: $v3 = x.try_into().map_err(|_| ())?; - v3.try_into() + V3(x) => { + let v4: $v4 = x.try_into().map_err(|_| ())?; + v4.try_into().map_err(|_| ()) }, - } - } - } - impl TryFrom<$n> for $v3 { - type Error = (); - fn try_from(x: $n) -> Result { - use $n::*; - match x { - V2(x) => x.try_into(), - V3(x) => Ok(x), V4(x) => x.try_into().map_err(|_| ()), - } - } - } - impl TryFrom<$n> for $v4 { - type Error = (); - fn try_from(x: $n) -> Result { - use $n::*; - match x { - V2(x) => { - let v3: $v3 = x.try_into().map_err(|_| ())?; - v3.try_into().map_err(|_| ()) - }, - V3(x) => x.try_into().map_err(|_| ()), - V4(x) => Ok(x), + V5(x) => Ok(x), } } } @@ -305,9 +204,9 @@ macro_rules! versioned_type { fn identify_version(&self) -> Version { use $n::*; match self { - V2(_) => v2::VERSION, V3(_) => v3::VERSION, V4(_) => v4::VERSION, + V5(_) => v5::VERSION, } } } @@ -321,42 +220,44 @@ versioned_type! { V3(v3::AssetId), #[codec(index = 4)] V4(v4::AssetId), + #[codec(index = 5)] + V5(v5::AssetId), } } versioned_type! { /// A single version's `Response` value, together with its version code. pub enum VersionedResponse { - #[codec(index = 2)] - V2(v2::Response), #[codec(index = 3)] V3(v3::Response), #[codec(index = 4)] V4(v4::Response), + #[codec(index = 5)] + V5(v5::Response), } } versioned_type! { /// A single `NetworkId` value, together with its version code. pub enum VersionedNetworkId { - #[codec(index = 2)] - V2(v2::NetworkId), #[codec(index = 3)] V3(v3::NetworkId), #[codec(index = 4)] V4(v4::NetworkId), + #[codec(index = 5)] + V5(v5::NetworkId), } } versioned_type! { /// A single `Junction` value, together with its version code. pub enum VersionedJunction { - #[codec(index = 2)] - V2(v2::Junction), #[codec(index = 3)] V3(v3::Junction), #[codec(index = 4)] V4(v4::Junction), + #[codec(index = 5)] + V5(v5::Junction), } } @@ -364,63 +265,51 @@ versioned_type! { /// A single `Location` value, together with its version code. #[derive(Ord, PartialOrd)] pub enum VersionedLocation { - #[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index - V2(v2::MultiLocation), #[codec(index = 3)] V3(v3::MultiLocation), #[codec(index = 4)] V4(v4::Location), + #[codec(index = 5)] + V5(v5::Location), } } -#[deprecated(note = "Use `VersionedLocation` instead")] -pub type VersionedMultiLocation = VersionedLocation; - versioned_type! { /// A single `InteriorLocation` value, together with its version code. pub enum VersionedInteriorLocation { - #[codec(index = 2)] // while this is same as v1::Junctions, VersionedInteriorLocation is introduced in v3 - V2(v2::InteriorMultiLocation), #[codec(index = 3)] V3(v3::InteriorMultiLocation), #[codec(index = 4)] V4(v4::InteriorLocation), + #[codec(index = 5)] + V5(v5::InteriorLocation), } } -#[deprecated(note = "Use `VersionedInteriorLocation` instead")] -pub type VersionedInteriorMultiLocation = VersionedInteriorLocation; - versioned_type! { /// A single `Asset` value, together with its version code. pub enum VersionedAsset { - #[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index - V2(v2::MultiAsset), #[codec(index = 3)] V3(v3::MultiAsset), #[codec(index = 4)] V4(v4::Asset), + #[codec(index = 5)] + V5(v5::Asset), } } -#[deprecated(note = "Use `VersionedAsset` instead")] -pub type VersionedMultiAsset = VersionedAsset; - versioned_type! { /// A single `MultiAssets` value, together with its version code. pub enum VersionedAssets { - #[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index - V2(v2::MultiAssets), #[codec(index = 3)] V3(v3::MultiAssets), #[codec(index = 4)] V4(v4::Assets), + #[codec(index = 5)] + V5(v5::Assets), } } -#[deprecated(note = "Use `VersionedAssets` instead")] -pub type VersionedMultiAssets = VersionedAssets; - /// A single XCM message, together with its version code. #[derive(Derivative, Encode, Decode, TypeInfo)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] @@ -429,21 +318,20 @@ pub type VersionedMultiAssets = VersionedAssets; #[scale_info(bounds(), skip_type_params(RuntimeCall))] #[scale_info(replace_segment("staging_xcm", "xcm"))] pub enum VersionedXcm { - #[codec(index = 2)] - #[deprecated] - V2(v2::Xcm), #[codec(index = 3)] V3(v3::Xcm), #[codec(index = 4)] V4(v4::Xcm), + #[codec(index = 5)] + V5(v5::Xcm), } -impl IntoVersion for VersionedXcm { +impl IntoVersion for VersionedXcm { fn into_version(self, n: Version) -> Result { Ok(match n { - 2 => Self::V2(self.try_into()?), 3 => Self::V3(self.try_into()?), 4 => Self::V4(self.try_into()?), + 5 => Self::V5(self.try_into()?), _ => return Err(()), }) } @@ -452,9 +340,9 @@ impl IntoVersion for VersionedXcm { impl IdentifyVersion for VersionedXcm { fn identify_version(&self) -> Version { match self { - Self::V2(_) => v2::VERSION, Self::V3(_) => v3::VERSION, Self::V4(_) => v4::VERSION, + Self::V5(_) => v5::VERSION, } } } @@ -476,12 +364,6 @@ impl VersionedXcm { } } -impl From> for VersionedXcm { - fn from(x: v2::Xcm) -> Self { - VersionedXcm::V2(x) - } -} - impl From> for VersionedXcm { fn from(x: v3::Xcm) -> Self { VersionedXcm::V3(x) @@ -494,44 +376,50 @@ impl From> for VersionedXcm { } } -impl TryFrom> for v2::Xcm { +impl From> for VersionedXcm { + fn from(x: v5::Xcm) -> Self { + VersionedXcm::V5(x) + } +} + +impl TryFrom> for v3::Xcm { type Error = (); - fn try_from(x: VersionedXcm) -> Result { + fn try_from(x: VersionedXcm) -> Result { use VersionedXcm::*; match x { - V2(x) => Ok(x), - V3(x) => x.try_into(), - V4(x) => { - let v3: v3::Xcm = x.try_into()?; - v3.try_into() + V3(x) => Ok(x), + V4(x) => x.try_into(), + V5(x) => { + let v4: v4::Xcm = x.try_into()?; + v4.try_into() }, } } } -impl TryFrom> for v3::Xcm { +impl TryFrom> for v4::Xcm { type Error = (); fn try_from(x: VersionedXcm) -> Result { use VersionedXcm::*; match x { - V2(x) => x.try_into(), - V3(x) => Ok(x), - V4(x) => x.try_into(), + V3(x) => x.try_into(), + V4(x) => Ok(x), + V5(x) => x.try_into(), } } } -impl TryFrom> for v4::Xcm { +impl TryFrom> for v5::Xcm { type Error = (); fn try_from(x: VersionedXcm) -> Result { use VersionedXcm::*; match x { - V2(x) => { - let v3: v3::Xcm = x.try_into()?; - v3.try_into() + V3(x) => { + let v4: v4::Xcm = x.try_into()?; + v4.try_into() }, - V3(x) => x.try_into(), - V4(x) => Ok(x), + V4(x) => x.try_into(), + V5(x) => Ok(x), } } } @@ -539,7 +427,7 @@ impl TryFrom> for v4::Xcm { /// Convert an `Xcm` datum into a `VersionedXcm`, based on a destination `Location` which will /// interpret it. pub trait WrapVersion { - fn wrap_version( + fn wrap_version( dest: &latest::Location, xcm: impl Into>, ) -> Result, ()>; @@ -568,28 +456,11 @@ impl WrapVersion for () { } } -/// `WrapVersion` implementation which attempts to always convert the XCM to version 2 before -/// wrapping it. -pub struct AlwaysV2; -impl WrapVersion for AlwaysV2 { - fn wrap_version( - _: &latest::Location, - xcm: impl Into>, - ) -> Result, ()> { - Ok(VersionedXcm::::V2(xcm.into().try_into()?)) - } -} -impl GetVersion for AlwaysV2 { - fn get_version_for(_dest: &latest::Location) -> Option { - Some(v2::VERSION) - } -} - /// `WrapVersion` implementation which attempts to always convert the XCM to version 3 before /// wrapping it. pub struct AlwaysV3; impl WrapVersion for AlwaysV3 { - fn wrap_version( + fn wrap_version( _: &latest::Location, xcm: impl Into>, ) -> Result, ()> { @@ -606,7 +477,7 @@ impl GetVersion for AlwaysV3 { /// wrapping it. pub struct AlwaysV4; impl WrapVersion for AlwaysV4 { - fn wrap_version( + fn wrap_version( _: &latest::Location, xcm: impl Into>, ) -> Result, ()> { @@ -619,9 +490,26 @@ impl GetVersion for AlwaysV4 { } } +/// `WrapVersion` implementation which attempts to always convert the XCM to version 3 before +/// wrapping it. +pub struct AlwaysV5; +impl WrapVersion for AlwaysV5 { + fn wrap_version( + _: &latest::Location, + xcm: impl Into>, + ) -> Result, ()> { + Ok(VersionedXcm::::V5(xcm.into().try_into()?)) + } +} +impl GetVersion for AlwaysV5 { + fn get_version_for(_dest: &latest::Location) -> Option { + Some(v5::VERSION) + } +} + /// `WrapVersion` implementation which attempts to always convert the XCM to the latest version /// before wrapping it. -pub type AlwaysLatest = AlwaysV4; +pub type AlwaysLatest = AlwaysV5; /// `WrapVersion` implementation which attempts to always convert the XCM to the most recent Long- /// Term-Support version before wrapping it. @@ -629,7 +517,7 @@ pub type AlwaysLts = AlwaysV4; pub mod prelude { pub use super::{ - latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV2, AlwaysV3, AlwaysV4, GetVersion, + latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV3, AlwaysV4, AlwaysV5, GetVersion, IdentifyVersion, IntoVersion, Unsupported, Version as XcmVersion, VersionedAsset, VersionedAssetId, VersionedAssets, VersionedInteriorLocation, VersionedLocation, VersionedResponse, VersionedXcm, WrapVersion, @@ -637,12 +525,6 @@ pub mod prelude { } pub mod opaque { - pub mod v2 { - // Everything from v2 - pub use crate::v2::*; - // Then override with the opaque types in v2 - pub use crate::v2::opaque::{Instruction, Xcm}; - } pub mod v3 { // Everything from v3 pub use crate::v3::*; @@ -655,9 +537,15 @@ pub mod opaque { // Then override with the opaque types in v4 pub use crate::v4::opaque::{Instruction, Xcm}; } + pub mod v5 { + // Everything from v4 + pub use crate::v5::*; + // Then override with the opaque types in v5 + pub use crate::v5::opaque::{Instruction, Xcm}; + } pub mod latest { - pub use super::v4::*; + pub use super::v5::*; } pub mod lts { @@ -709,7 +597,7 @@ fn size_limits() { } check_sizes! { - (crate::latest::Instruction<()>, 112), + (crate::latest::Instruction<()>, 128), (crate::latest::Asset, 80), (crate::latest::Location, 24), (crate::latest::AssetId, 40), diff --git a/polkadot/xcm/src/tests.rs b/polkadot/xcm/src/tests.rs index 4c666063f3f4706e77869b7aea5e16000f6dbc1d..5a267b3a90483e2bb900a4d9831d95ababe39cf4 100644 --- a/polkadot/xcm/src/tests.rs +++ b/polkadot/xcm/src/tests.rs @@ -34,43 +34,43 @@ fn encode_decode_versioned_asset_id_v3() { } #[test] -fn encode_decode_versioned_response_v2() { - let response = VersionedResponse::V2(v2::Response::Null); +fn encode_decode_versioned_response_v3() { + let response = VersionedResponse::V3(v3::Response::Null); let encoded = response.encode(); - assert_eq!(encoded, hex_literal::hex!("0200"), "encode format changed"); - assert_eq!(encoded[0], 2, "bad version number"); + assert_eq!(encoded, hex_literal::hex!("0300"), "encode format changed"); + assert_eq!(encoded[0], 3, "bad version number"); let decoded = VersionedResponse::decode(&mut &encoded[..]).unwrap(); assert_eq!(response, decoded); } #[test] -fn encode_decode_versioned_response_v3() { - let response = VersionedResponse::V3(v3::Response::Null); +fn encode_decode_versioned_response_v4() { + let response = VersionedResponse::V4(v4::Response::Null); let encoded = response.encode(); - assert_eq!(encoded, hex_literal::hex!("0300"), "encode format changed"); - assert_eq!(encoded[0], 3, "bad version number"); + assert_eq!(encoded, hex_literal::hex!("0400"), "encode format changed"); + assert_eq!(encoded[0], 4, "bad version number"); let decoded = VersionedResponse::decode(&mut &encoded[..]).unwrap(); assert_eq!(response, decoded); } #[test] -fn encode_decode_versioned_multi_location_v2() { - let location = VersionedLocation::V2(v2::MultiLocation::new(0, v2::Junctions::Here)); - let encoded = location.encode(); +fn encode_decode_versioned_response_v5() { + let response = VersionedResponse::V5(v5::Response::Null); + let encoded = response.encode(); - assert_eq!(encoded, hex_literal::hex!("010000"), "encode format changed"); - assert_eq!(encoded[0], 1, "bad version number"); // this is introduced in v1 + assert_eq!(encoded, hex_literal::hex!("0500"), "encode format changed"); + assert_eq!(encoded[0], 5, "bad version number"); - let decoded = VersionedLocation::decode(&mut &encoded[..]).unwrap(); - assert_eq!(location, decoded); + let decoded = VersionedResponse::decode(&mut &encoded[..]).unwrap(); + assert_eq!(response, decoded); } #[test] -fn encode_decode_versioned_multi_location_v3() { +fn encode_decode_versioned_location_v3() { let location = VersionedLocation::V3(v3::MultiLocation::new(0, v3::Junctions::Here)); let encoded = location.encode(); @@ -82,19 +82,31 @@ fn encode_decode_versioned_multi_location_v3() { } #[test] -fn encode_decode_versioned_interior_multi_location_v2() { - let location = VersionedInteriorLocation::V2(v2::InteriorMultiLocation::Here); +fn encode_decode_versioned_location_v4() { + let location = VersionedLocation::V4(v4::Location::new(0, v4::Junctions::Here)); let encoded = location.encode(); - assert_eq!(encoded, hex_literal::hex!("0200"), "encode format changed"); - assert_eq!(encoded[0], 2, "bad version number"); + assert_eq!(encoded, hex_literal::hex!("040000"), "encode format changed"); + assert_eq!(encoded[0], 4, "bad version number"); - let decoded = VersionedInteriorLocation::decode(&mut &encoded[..]).unwrap(); + let decoded = VersionedLocation::decode(&mut &encoded[..]).unwrap(); assert_eq!(location, decoded); } #[test] -fn encode_decode_versioned_interior_multi_location_v3() { +fn encode_decode_versioned_location_v5() { + let location = VersionedLocation::V5(v5::Location::new(0, v5::Junctions::Here)); + let encoded = location.encode(); + + assert_eq!(encoded, hex_literal::hex!("050000"), "encode format changed"); + assert_eq!(encoded[0], 5, "bad version number"); + + let decoded = VersionedLocation::decode(&mut &encoded[..]).unwrap(); + assert_eq!(location, decoded); +} + +#[test] +fn encode_decode_versioned_interior_location_v3() { let location = VersionedInteriorLocation::V3(v3::InteriorMultiLocation::Here); let encoded = location.encode(); @@ -106,19 +118,31 @@ fn encode_decode_versioned_interior_multi_location_v3() { } #[test] -fn encode_decode_versioned_multi_asset_v2() { - let asset = VersionedAsset::V2(v2::MultiAsset::from(((0, v2::Junctions::Here), 1))); - let encoded = asset.encode(); +fn encode_decode_versioned_interior_location_v4() { + let location = VersionedInteriorLocation::V4(v4::InteriorLocation::Here); + let encoded = location.encode(); - assert_eq!(encoded, hex_literal::hex!("010000000004"), "encode format changed"); - assert_eq!(encoded[0], 1, "bad version number"); + assert_eq!(encoded, hex_literal::hex!("0400"), "encode format changed"); + assert_eq!(encoded[0], 4, "bad version number"); - let decoded = VersionedAsset::decode(&mut &encoded[..]).unwrap(); - assert_eq!(asset, decoded); + let decoded = VersionedInteriorLocation::decode(&mut &encoded[..]).unwrap(); + assert_eq!(location, decoded); } #[test] -fn encode_decode_versioned_multi_asset_v3() { +fn encode_decode_versioned_interior_location_v5() { + let location = VersionedInteriorLocation::V5(v5::InteriorLocation::Here); + let encoded = location.encode(); + + assert_eq!(encoded, hex_literal::hex!("0500"), "encode format changed"); + assert_eq!(encoded[0], 5, "bad version number"); + + let decoded = VersionedInteriorLocation::decode(&mut &encoded[..]).unwrap(); + assert_eq!(location, decoded); +} + +#[test] +fn encode_decode_versioned_asset_v3() { let asset = VersionedAsset::V3(v3::MultiAsset::from((v3::MultiLocation::default(), 1))); let encoded = asset.encode(); @@ -130,22 +154,31 @@ fn encode_decode_versioned_multi_asset_v3() { } #[test] -fn encode_decode_versioned_multi_assets_v2() { - let assets = VersionedAssets::V2(v2::MultiAssets::from(vec![v2::MultiAsset::from(( - (0, v2::Junctions::Here), - 1, - ))])); - let encoded = assets.encode(); +fn encode_decode_versioned_asset_v4() { + let asset = VersionedAsset::V4(v4::Asset::from((v4::Location::default(), 1))); + let encoded = asset.encode(); - assert_eq!(encoded, hex_literal::hex!("01040000000004"), "encode format changed"); - assert_eq!(encoded[0], 1, "bad version number"); + assert_eq!(encoded, hex_literal::hex!("0400000004"), "encode format changed"); + assert_eq!(encoded[0], 4, "bad version number"); - let decoded = VersionedAssets::decode(&mut &encoded[..]).unwrap(); - assert_eq!(assets, decoded); + let decoded = VersionedAsset::decode(&mut &encoded[..]).unwrap(); + assert_eq!(asset, decoded); } #[test] -fn encode_decode_versioned_multi_assets_v3() { +fn encode_decode_versioned_asset_v5() { + let asset = VersionedAsset::V5(v5::Asset::from((v5::Location::default(), 1))); + let encoded = asset.encode(); + + assert_eq!(encoded, hex_literal::hex!("0500000004"), "encode format changed"); + assert_eq!(encoded[0], 5, "bad version number"); + + let decoded = VersionedAsset::decode(&mut &encoded[..]).unwrap(); + assert_eq!(asset, decoded); +} + +#[test] +fn encode_decode_versioned_assets_v3() { let assets = VersionedAssets::V3(v3::MultiAssets::from(vec![ (v3::MultiAsset::from((v3::MultiLocation::default(), 1))), ])); @@ -158,6 +191,34 @@ fn encode_decode_versioned_multi_assets_v3() { assert_eq!(assets, decoded); } +#[test] +fn encode_decode_versioned_assets_v4() { + let assets = VersionedAssets::V4(v4::Assets::from(vec![ + (v4::Asset::from((v4::Location::default(), 1))), + ])); + let encoded = assets.encode(); + + assert_eq!(encoded, hex_literal::hex!("040400000004"), "encode format changed"); + assert_eq!(encoded[0], 4, "bad version number"); + + let decoded = VersionedAssets::decode(&mut &encoded[..]).unwrap(); + assert_eq!(assets, decoded); +} + +#[test] +fn encode_decode_versioned_assets_v5() { + let assets = VersionedAssets::V5(v5::Assets::from(vec![ + (v5::Asset::from((v5::Location::default(), 1))), + ])); + let encoded = assets.encode(); + + assert_eq!(encoded, hex_literal::hex!("050400000004"), "encode format changed"); + assert_eq!(encoded[0], 5, "bad version number"); + + let decoded = VersionedAssets::decode(&mut &encoded[..]).unwrap(); + assert_eq!(assets, decoded); +} + #[test] fn encode_decode_versioned_xcm_v3() { let xcm = VersionedXcm::V3(v3::Xcm::<()>::new()); @@ -170,6 +231,30 @@ fn encode_decode_versioned_xcm_v3() { assert_eq!(xcm, decoded); } +#[test] +fn encode_decode_versioned_xcm_v4() { + let xcm = VersionedXcm::V4(v4::Xcm::<()>::new()); + let encoded = xcm.encode(); + + assert_eq!(encoded, hex_literal::hex!("0400"), "encode format changed"); + assert_eq!(encoded[0], 4, "bad version number"); + + let decoded = VersionedXcm::decode(&mut &encoded[..]).unwrap(); + assert_eq!(xcm, decoded); +} + +#[test] +fn encode_decode_versioned_xcm_v5() { + let xcm = VersionedXcm::V5(v5::Xcm::<()>::new()); + let encoded = xcm.encode(); + + assert_eq!(encoded, hex_literal::hex!("0500"), "encode format changed"); + assert_eq!(encoded[0], 5, "bad version number"); + + let decoded = VersionedXcm::decode(&mut &encoded[..]).unwrap(); + assert_eq!(xcm, decoded); +} + // With the renaming of the crate to `staging-xcm` the naming in the metadata changed as well and // this broke downstream users. This test ensures that the name in the metadata isn't changed. #[test] diff --git a/polkadot/xcm/src/v2/junction.rs b/polkadot/xcm/src/v2/junction.rs deleted file mode 100644 index 68a7886f303937870bb42aaee3e1f971a29e7044..0000000000000000000000000000000000000000 --- a/polkadot/xcm/src/v2/junction.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Support data structures for `MultiLocation`, primarily the `Junction` datatype. - -use super::{BodyId, BodyPart, Junctions, MultiLocation, NetworkId}; -use crate::v3::Junction as NewJunction; -use bounded_collections::{ConstU32, WeakBoundedVec}; -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; - -/// A single item in a path to describe the relative location of a consensus system. -/// -/// Each item assumes a pre-existing location as its context and is defined in terms of it. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum Junction { - /// An indexed parachain belonging to and operated by the context. - /// - /// Generally used when the context is a Polkadot Relay-chain. - Parachain(#[codec(compact)] u32), - /// A 32-byte identifier for an account of a specific network that is respected as a sovereign - /// endpoint within the context. - /// - /// Generally used when the context is a Substrate-based chain. - AccountId32 { network: NetworkId, id: [u8; 32] }, - /// An 8-byte index for an account of a specific network that is respected as a sovereign - /// endpoint within the context. - /// - /// May be used when the context is a Frame-based chain and includes e.g. an indices pallet. - AccountIndex64 { - network: NetworkId, - #[codec(compact)] - index: u64, - }, - /// A 20-byte identifier for an account of a specific network that is respected as a sovereign - /// endpoint within the context. - /// - /// May be used when the context is an Ethereum or Bitcoin chain or smart-contract. - AccountKey20 { network: NetworkId, key: [u8; 20] }, - /// An instanced, indexed pallet that forms a constituent part of the context. - /// - /// Generally used when the context is a Frame-based chain. - PalletInstance(u8), - /// A non-descript index within the context location. - /// - /// Usage will vary widely owing to its generality. - /// - /// NOTE: Try to avoid using this and instead use a more specific item. - GeneralIndex(#[codec(compact)] u128), - /// A nondescript datum acting as a key within the context location. - /// - /// Usage will vary widely owing to its generality. - /// - /// NOTE: Try to avoid using this and instead use a more specific item. - GeneralKey(WeakBoundedVec>), - /// The unambiguous child. - /// - /// Not currently used except as a fallback when deriving ancestry. - OnlyChild, - /// A pluralistic body existing within consensus. - /// - /// Typical to be used to represent a governance origin of a chain, but could in principle be - /// used to represent things such as multisigs also. - Plurality { id: BodyId, part: BodyPart }, -} - -impl TryFrom for Junction { - type Error = (); - - fn try_from(value: NewJunction) -> Result { - use NewJunction::*; - Ok(match value { - Parachain(id) => Self::Parachain(id), - AccountId32 { network, id } => Self::AccountId32 { network: network.try_into()?, id }, - AccountIndex64 { network, index } => - Self::AccountIndex64 { network: network.try_into()?, index }, - AccountKey20 { network, key } => - Self::AccountKey20 { network: network.try_into()?, key }, - PalletInstance(index) => Self::PalletInstance(index), - GeneralIndex(id) => Self::GeneralIndex(id), - GeneralKey { length, data } => Self::GeneralKey( - data[0..data.len().min(length as usize)] - .to_vec() - .try_into() - .expect("key is bounded to 32 and so will never be out of bounds; qed"), - ), - OnlyChild => Self::OnlyChild, - Plurality { id, part } => Self::Plurality { id: id.into(), part: part.into() }, - _ => return Err(()), - }) - } -} - -impl Junction { - /// Convert `self` into a `MultiLocation` containing 0 parents. - /// - /// Similar to `Into::into`, except that this method can be used in a const evaluation context. - pub const fn into(self) -> MultiLocation { - MultiLocation { parents: 0, interior: Junctions::X1(self) } - } - - /// Convert `self` into a `MultiLocation` containing `n` parents. - /// - /// Similar to `Self::into`, with the added ability to specify the number of parent junctions. - pub const fn into_exterior(self, n: u8) -> MultiLocation { - MultiLocation { parents: n, interior: Junctions::X1(self) } - } -} diff --git a/polkadot/xcm/src/v2/mod.rs b/polkadot/xcm/src/v2/mod.rs deleted file mode 100644 index e3358f08d4101ecf1f98a4e69d324a4991d60e80..0000000000000000000000000000000000000000 --- a/polkadot/xcm/src/v2/mod.rs +++ /dev/null @@ -1,1222 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! # XCM Version 2 -//! -//! WARNING: DEPRECATED, please use version 3 or 4. -//! -//! Version 2 of the Cross-Consensus Message format data structures. The comprehensive list of -//! changes can be found in -//! [this PR description](https://github.com/paritytech/polkadot/pull/3629#issue-968428279). -//! -//! ## Changes to be aware of -//! The biggest change here is the restructuring of XCM messages: instead of having `Order` and -//! `Xcm` types, the `Xcm` type now simply wraps a `Vec` containing `Instruction`s. However, most -//! changes should still be automatically convertible via the `try_from` and `from` conversion -//! functions. -//! -//! ### Junction -//! - No special attention necessary -//! -//! ### `MultiLocation` -//! - No special attention necessary -//! -//! ### `MultiAsset` -//! - No special attention necessary -//! -//! ### XCM and Order -//! - `Xcm` and `Order` variants are now combined under a single `Instruction` enum. -//! - `Order` is now obsolete and replaced entirely by `Instruction`. -//! - `Xcm` is now a simple wrapper around a `Vec`. -//! - During conversion from `Order` to `Instruction`, we do not handle `BuyExecution`s that have -//! nested XCMs, i.e. if the `instructions` field in the `BuyExecution` enum struct variant is not -//! empty, then the conversion will fail. To address this, rewrite the XCM using `Instruction`s in -//! chronological order. -//! - During conversion from `Xcm` to `Instruction`, we do not handle `RelayedFrom` messages at all. -//! -//! ### XCM Pallet -//! - The `Weigher` configuration item must have sensible weights defined for `BuyExecution` and -//! `DepositAsset` instructions. Failing that, dispatch calls to `teleport_assets` and -//! `reserve_transfer_assets` will fail with `UnweighableMessage`. - -use super::{ - v3::{ - BodyId as NewBodyId, BodyPart as NewBodyPart, Instruction as NewInstruction, - NetworkId as NewNetworkId, OriginKind as NewOriginKind, Response as NewResponse, - WeightLimit as NewWeightLimit, Xcm as NewXcm, - }, - DoubleEncoded, -}; -use alloc::{vec, vec::Vec}; -use bounded_collections::{ConstU32, WeakBoundedVec}; -use codec::{ - self, decode_vec_with_len, Compact, Decode, Encode, Error as CodecError, Input as CodecInput, - MaxEncodedLen, -}; -use core::{fmt::Debug, result}; -use derivative::Derivative; -use scale_info::TypeInfo; - -mod junction; -mod multiasset; -mod multilocation; -mod traits; - -pub use junction::Junction; -pub use multiasset::{ - AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets, - WildFungibility, WildMultiAsset, -}; -pub use multilocation::{ - Ancestor, AncestorThen, InteriorMultiLocation, Junctions, MultiLocation, Parent, ParentThen, -}; -pub use traits::{Error, ExecuteXcm, GetWeight, Outcome, Result, SendError, SendResult, SendXcm}; - -/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`. -#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] -pub enum OriginKind { - /// Origin should just be the native dispatch origin representation for the sender in the - /// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin - /// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a - /// primary/native dispatch origin form. - Native, - - /// Origin should just be the standard account-based origin with the sovereign account of - /// the sender. For Cumulus/Frame chains, this is the `Signed` origin. - SovereignAccount, - - /// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin. - /// This will not usually be an available option. - Superuser, - - /// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be - /// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be - /// the `pallet_xcm::Origin::Xcm` type. - Xcm, -} - -impl From for OriginKind { - fn from(new: NewOriginKind) -> Self { - use NewOriginKind::*; - match new { - Native => Self::Native, - SovereignAccount => Self::SovereignAccount, - Superuser => Self::Superuser, - Xcm => Self::Xcm, - } - } -} - -/// A global identifier of an account-bearing consensus system. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum NetworkId { - /// Unidentified/any. - Any, - /// Some named network. - Named(WeakBoundedVec>), - /// The Polkadot Relay chain - Polkadot, - /// Kusama. - Kusama, -} - -impl TryFrom> for NetworkId { - type Error = (); - fn try_from(new: Option) -> result::Result { - match new { - None => Ok(NetworkId::Any), - Some(id) => Self::try_from(id), - } - } -} - -impl TryFrom for NetworkId { - type Error = (); - fn try_from(new: NewNetworkId) -> result::Result { - use NewNetworkId::*; - match new { - Polkadot => Ok(NetworkId::Polkadot), - Kusama => Ok(NetworkId::Kusama), - _ => Err(()), - } - } -} - -/// An identifier of a pluralistic body. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum BodyId { - /// The only body in its context. - Unit, - /// A named body. - Named(WeakBoundedVec>), - /// An indexed body. - Index(#[codec(compact)] u32), - /// The unambiguous executive body (for Polkadot, this would be the Polkadot council). - Executive, - /// The unambiguous technical body (for Polkadot, this would be the Technical Committee). - Technical, - /// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a - /// majority of lock-voters). - Legislative, - /// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a - /// "grand oracle", it may be considered as that). - Judicial, - /// The unambiguous defense body (for Polkadot, an opinion on the topic given via a public - /// referendum on the `staking_admin` track). - Defense, - /// The unambiguous administration body (for Polkadot, an opinion on the topic given via a - /// public referendum on the `general_admin` track). - Administration, - /// The unambiguous treasury body (for Polkadot, an opinion on the topic given via a public - /// referendum on the `treasurer` track). - Treasury, -} - -impl From for BodyId { - fn from(n: NewBodyId) -> Self { - use NewBodyId::*; - match n { - Unit => Self::Unit, - Moniker(n) => Self::Named( - n[..] - .to_vec() - .try_into() - .expect("array size is 4 and so will never be out of bounds; qed"), - ), - Index(n) => Self::Index(n), - Executive => Self::Executive, - Technical => Self::Technical, - Legislative => Self::Legislative, - Judicial => Self::Judicial, - Defense => Self::Defense, - Administration => Self::Administration, - Treasury => Self::Treasury, - } - } -} - -/// A part of a pluralistic body. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum BodyPart { - /// The body's declaration, under whatever means it decides. - Voice, - /// A given number of members of the body. - Members { - #[codec(compact)] - count: u32, - }, - /// A given number of members of the body, out of some larger caucus. - Fraction { - #[codec(compact)] - nom: u32, - #[codec(compact)] - denom: u32, - }, - /// No less than the given proportion of members of the body. - AtLeastProportion { - #[codec(compact)] - nom: u32, - #[codec(compact)] - denom: u32, - }, - /// More than the given proportion of members of the body. - MoreThanProportion { - #[codec(compact)] - nom: u32, - #[codec(compact)] - denom: u32, - }, -} - -impl BodyPart { - /// Returns `true` if the part represents a strict majority (> 50%) of the body in question. - pub fn is_majority(&self) -> bool { - match self { - BodyPart::Fraction { nom, denom } if *nom * 2 > *denom => true, - BodyPart::AtLeastProportion { nom, denom } if *nom * 2 > *denom => true, - BodyPart::MoreThanProportion { nom, denom } if *nom * 2 >= *denom => true, - _ => false, - } - } -} - -impl From for BodyPart { - fn from(n: NewBodyPart) -> Self { - use NewBodyPart::*; - match n { - Voice => Self::Voice, - Members { count } => Self::Members { count }, - Fraction { nom, denom } => Self::Fraction { nom, denom }, - AtLeastProportion { nom, denom } => Self::AtLeastProportion { nom, denom }, - MoreThanProportion { nom, denom } => Self::MoreThanProportion { nom, denom }, - } - } -} - -/// This module's XCM version. -pub const VERSION: super::Version = 2; - -/// An identifier for a query. -pub type QueryId = u64; - -/// DEPRECATED. Please use XCMv3 or XCMv4 instead. -#[derive(Derivative, Default, Encode, TypeInfo)] -#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] -#[codec(encode_bound())] -#[codec(decode_bound())] -#[scale_info(bounds(), skip_type_params(RuntimeCall))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub struct Xcm(pub Vec>); - -environmental::environmental!(instructions_count: u8); - -impl Decode for Xcm { - fn decode(input: &mut I) -> core::result::Result { - instructions_count::using_once(&mut 0, || { - let number_of_instructions: u32 = >::decode(input)?.into(); - instructions_count::with(|count| { - *count = count.saturating_add(number_of_instructions as u8); - if *count > MAX_INSTRUCTIONS_TO_DECODE { - return Err(CodecError::from("Max instructions exceeded")) - } - Ok(()) - }) - .unwrap_or(Ok(()))?; - let decoded_instructions = decode_vec_with_len(input, number_of_instructions as usize)?; - Ok(Self(decoded_instructions)) - }) - } -} - -/// The maximal number of instructions in an XCM before decoding fails. -/// -/// This is a deliberate limit - not a technical one. -pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100; - -impl Xcm { - /// Create an empty instance. - pub fn new() -> Self { - Self(vec![]) - } - - /// Return `true` if no instructions are held in `self`. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Return the number of instructions held in `self`. - pub fn len(&self) -> usize { - self.0.len() - } - - /// Consume and either return `self` if it contains some instructions, or if it's empty, then - /// instead return the result of `f`. - pub fn or_else(self, f: impl FnOnce() -> Self) -> Self { - if self.0.is_empty() { - f() - } else { - self - } - } - - /// Return the first instruction, if any. - pub fn first(&self) -> Option<&Instruction> { - self.0.first() - } - - /// Return the last instruction, if any. - pub fn last(&self) -> Option<&Instruction> { - self.0.last() - } - - /// Return the only instruction, contained in `Self`, iff only one exists (`None` otherwise). - pub fn only(&self) -> Option<&Instruction> { - if self.0.len() == 1 { - self.0.first() - } else { - None - } - } - - /// Return the only instruction, contained in `Self`, iff only one exists (returns `self` - /// otherwise). - pub fn into_only(mut self) -> core::result::Result, Self> { - if self.0.len() == 1 { - self.0.pop().ok_or(self) - } else { - Err(self) - } - } -} - -/// A prelude for importing all types typically used when interacting with XCM messages. -pub mod prelude { - mod contents { - pub use super::super::{ - Ancestor, AncestorThen, - AssetId::{self, *}, - AssetInstance::{self, *}, - BodyId, BodyPart, Error as XcmError, ExecuteXcm, - Fungibility::{self, *}, - Instruction::*, - InteriorMultiLocation, - Junction::{self, *}, - Junctions::{self, *}, - MultiAsset, - MultiAssetFilter::{self, *}, - MultiAssets, MultiLocation, - NetworkId::{self, *}, - OriginKind, Outcome, Parent, ParentThen, QueryId, Response, Result as XcmResult, - SendError, SendResult, SendXcm, - WeightLimit::{self, *}, - WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, - WildMultiAsset::{self, *}, - XcmWeightInfo, VERSION as XCM_VERSION, - }; - } - pub use super::{Instruction, Xcm}; - pub use contents::*; - pub mod opaque { - pub use super::{ - super::opaque::{Instruction, Xcm}, - contents::*, - }; - } -} - -/// Response data to a query. -#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum Response { - /// No response. Serves as a neutral default. - Null, - /// Some assets. - Assets(MultiAssets), - /// The outcome of an XCM instruction. - ExecutionResult(Option<(u32, Error)>), - /// An XCM version. - Version(super::Version), -} - -impl Default for Response { - fn default() -> Self { - Self::Null - } -} - -/// An optional weight limit. -#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum WeightLimit { - /// No weight limit imposed. - Unlimited, - /// Weight limit imposed of the inner value. - Limited(#[codec(compact)] u64), -} - -impl From> for WeightLimit { - fn from(x: Option) -> Self { - match x { - Some(w) => WeightLimit::Limited(w), - None => WeightLimit::Unlimited, - } - } -} - -impl From for Option { - fn from(x: WeightLimit) -> Self { - match x { - WeightLimit::Limited(w) => Some(w), - WeightLimit::Unlimited => None, - } - } -} - -impl TryFrom for WeightLimit { - type Error = (); - fn try_from(x: NewWeightLimit) -> result::Result { - use NewWeightLimit::*; - match x { - Limited(w) => Ok(Self::Limited(w.ref_time())), - Unlimited => Ok(Self::Unlimited), - } - } -} - -/// Local weight type; execution time in picoseconds. -pub type Weight = u64; - -/// Cross-Consensus Message: A message from one consensus system to another. -/// -/// Consensus systems that may send and receive messages include blockchains and smart contracts. -/// -/// All messages are delivered from a known *origin*, expressed as a `MultiLocation`. -/// -/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the -/// outer XCM format, known as `VersionedXcm`. -#[derive(Derivative, Encode, Decode, TypeInfo, xcm_procedural::XcmWeightInfoTrait)] -#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] -#[codec(encode_bound())] -#[codec(decode_bound())] -#[scale_info(bounds(), skip_type_params(RuntimeCall))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum Instruction { - /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into the Holding - /// Register. - /// - /// - `assets`: The asset(s) to be withdrawn into holding. - /// - /// Kind: *Command*. - /// - /// Errors: - WithdrawAsset(MultiAssets), - - /// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` - /// system and equivalent derivatives should be placed into the Holding Register. - /// - /// - `assets`: The asset(s) that are minted into holding. - /// - /// Safety: `origin` must be trusted to have received and be storing `assets` such that they - /// may later be withdrawn should this system send a corresponding message. - /// - /// Kind: *Trusted Indication*. - /// - /// Errors: - ReserveAssetDeposited(MultiAssets), - - /// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should - /// be created and placed into the Holding Register. - /// - /// - `assets`: The asset(s) that are minted into the Holding Register. - /// - /// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding `assets` - /// prior as a consequence of sending this message. - /// - /// Kind: *Trusted Indication*. - /// - /// Errors: - ReceiveTeleportedAsset(MultiAssets), - - /// Respond with information that the local system is expecting. - /// - /// - `query_id`: The identifier of the query that resulted in this message being sent. - /// - `response`: The message content. - /// - `max_weight`: The maximum weight that handling this response should take. - /// - /// Safety: No concerns. - /// - /// Kind: *Information*. - /// - /// Errors: - QueryResponse { - #[codec(compact)] - query_id: QueryId, - response: Response, - #[codec(compact)] - max_weight: u64, - }, - - /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets - /// under the ownership of `beneficiary`. - /// - /// - `assets`: The asset(s) to be withdrawn. - /// - `beneficiary`: The new owner for the assets. - /// - /// Safety: No concerns. - /// - /// Kind: *Command*. - /// - /// Errors: - TransferAsset { assets: MultiAssets, beneficiary: MultiLocation }, - - /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets - /// under the ownership of `dest` within this consensus system (i.e. its sovereign account). - /// - /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given - /// `xcm`. - /// - /// - `assets`: The asset(s) to be withdrawn. - /// - `dest`: The location whose sovereign account will own the assets and thus the effective - /// beneficiary for the assets and the notification target for the reserve asset deposit - /// message. - /// - `xcm`: The instructions that should follow the `ReserveAssetDeposited` instruction, which - /// is sent onwards to `dest`. - /// - /// Safety: No concerns. - /// - /// Kind: *Command*. - /// - /// Errors: - TransferReserveAsset { assets: MultiAssets, dest: MultiLocation, xcm: Xcm<()> }, - - /// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed - /// by the kind of origin `origin_type`. - /// - /// - `origin_type`: The means of expressing the message origin as a dispatch origin. - /// - `max_weight`: The weight of `call`; this should be at least the chain's calculated weight - /// and will be used in the weight determination arithmetic. - /// - `call`: The encoded transaction to be applied. - /// - /// Safety: No concerns. - /// - /// Kind: *Command*. - /// - /// Errors: - Transact { - origin_type: OriginKind, - #[codec(compact)] - require_weight_at_most: u64, - call: DoubleEncoded, - }, - - /// A message to notify about a new incoming HRMP channel. This message is meant to be sent by - /// the relay-chain to a para. - /// - /// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel - /// opening. - /// - `max_message_size`: The maximum size of a message proposed by the sender. - /// - `max_capacity`: The maximum number of messages that can be queued in the channel. - /// - /// Safety: The message should originate directly from the relay-chain. - /// - /// Kind: *System Notification* - HrmpNewChannelOpenRequest { - #[codec(compact)] - sender: u32, - #[codec(compact)] - max_message_size: u32, - #[codec(compact)] - max_capacity: u32, - }, - - /// A message to notify about that a previously sent open channel request has been accepted by - /// the recipient. That means that the channel will be opened during the next relay-chain - /// session change. This message is meant to be sent by the relay-chain to a para. - /// - /// Safety: The message should originate directly from the relay-chain. - /// - /// Kind: *System Notification* - /// - /// Errors: - HrmpChannelAccepted { - // NOTE: We keep this as a structured item to a) keep it consistent with the other Hrmp - // items; and b) because the field's meaning is not obvious/mentioned from the item name. - #[codec(compact)] - recipient: u32, - }, - - /// A message to notify that the other party in an open channel decided to close it. In - /// particular, `initiator` is going to close the channel opened from `sender` to the - /// `recipient`. The close will be enacted at the next relay-chain session change. This message - /// is meant to be sent by the relay-chain to a para. - /// - /// Safety: The message should originate directly from the relay-chain. - /// - /// Kind: *System Notification* - /// - /// Errors: - HrmpChannelClosing { - #[codec(compact)] - initiator: u32, - #[codec(compact)] - sender: u32, - #[codec(compact)] - recipient: u32, - }, - - /// Clear the origin. - /// - /// This may be used by the XCM author to ensure that later instructions cannot command the - /// authority of the origin (e.g. if they are being relayed from an untrusted source, as often - /// the case with `ReserveAssetDeposited`). - /// - /// Safety: No concerns. - /// - /// Kind: *Command*. - /// - /// Errors: - ClearOrigin, - - /// Mutate the origin to some interior location. - /// - /// Kind: *Command* - /// - /// Errors: - DescendOrigin(InteriorMultiLocation), - - /// Immediately report the contents of the Error Register to the given destination via XCM. - /// - /// A `QueryResponse` message of type `ExecutionOutcome` is sent to `dest` with the given - /// `query_id` and the outcome of the XCM. - /// - /// - `query_id`: An identifier that will be replicated into the returned XCM message. - /// - `dest`: A valid destination for the returned XCM message. - /// - `max_response_weight`: The maximum amount of weight that the `QueryResponse` item which - /// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the - /// response may not execute at all. - /// - /// Kind: *Command* - /// - /// Errors: - ReportError { - #[codec(compact)] - query_id: QueryId, - dest: MultiLocation, - #[codec(compact)] - max_response_weight: u64, - }, - - /// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets under - /// the ownership of `beneficiary` within this consensus system. - /// - /// - `assets`: The asset(s) to remove from holding. - /// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. - /// Only the first `max_assets` assets/instances of those matched by `assets` will be - /// removed, prioritized under standard asset ordering. Any others will remain in holding. - /// - `beneficiary`: The new owner for the assets. - /// - /// Kind: *Command* - /// - /// Errors: - DepositAsset { - assets: MultiAssetFilter, - #[codec(compact)] - max_assets: u32, - beneficiary: MultiLocation, - }, - - /// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets under - /// the ownership of `dest` within this consensus system (i.e. deposit them into its sovereign - /// account). - /// - /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`. - /// - /// - `assets`: The asset(s) to remove from holding. - /// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. - /// Only the first `max_assets` assets/instances of those matched by `assets` will be - /// removed, prioritized under standard asset ordering. Any others will remain in holding. - /// - `dest`: The location whose sovereign account will own the assets and thus the effective - /// beneficiary for the assets and the notification target for the reserve asset deposit - /// message. - /// - `xcm`: The orders that should follow the `ReserveAssetDeposited` instruction which is - /// sent onwards to `dest`. - /// - /// Kind: *Command* - /// - /// Errors: - DepositReserveAsset { - assets: MultiAssetFilter, - #[codec(compact)] - max_assets: u32, - dest: MultiLocation, - xcm: Xcm<()>, - }, - - /// Remove the asset(s) (`give`) from the Holding Register and replace them with alternative - /// assets. - /// - /// The minimum amount of assets to be received into the Holding Register for the order not to - /// fail may be stated. - /// - /// - `give`: The asset(s) to remove from holding. - /// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for. - /// - /// Kind: *Command* - /// - /// Errors: - ExchangeAsset { give: MultiAssetFilter, receive: MultiAssets }, - - /// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a - /// reserve location. - /// - /// - `assets`: The asset(s) to remove from holding. - /// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The - /// sovereign account of this consensus system *on the reserve location* will have - /// appropriate assets withdrawn and `effects` will be executed on them. There will typically - /// be only one valid location on any given asset/chain combination. - /// - `xcm`: The instructions to execute on the assets once withdrawn *on the reserve - /// location*. - /// - /// Kind: *Command* - /// - /// Errors: - InitiateReserveWithdraw { assets: MultiAssetFilter, reserve: MultiLocation, xcm: Xcm<()> }, - - /// Remove the asset(s) (`assets`) from holding and send a `ReceiveTeleportedAsset` XCM message - /// to a `dest` location. - /// - /// - `assets`: The asset(s) to remove from holding. - /// - `dest`: A valid location that respects teleports coming from this location. - /// - `xcm`: The instructions to execute on the assets once arrived *on the destination - /// location*. - /// - /// NOTE: The `dest` location *MUST* respect this origin as a valid teleportation origin for - /// all `assets`. If it does not, then the assets may be lost. - /// - /// Kind: *Command* - /// - /// Errors: - InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, xcm: Xcm<()> }, - - /// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a - /// portion thereof. - /// - /// - `query_id`: An identifier that will be replicated into the returned XCM message. - /// - `dest`: A valid destination for the returned XCM message. This may be limited to the - /// current origin. - /// - `assets`: A filter for the assets that should be reported back. The assets reported back - /// will be, asset-wise, *the lesser of this value and the holding register*. No wildcards - /// will be used when reporting assets back. - /// - `max_response_weight`: The maximum amount of weight that the `QueryResponse` item which - /// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the - /// response may not execute at all. - /// - /// Kind: *Command* - /// - /// Errors: - QueryHolding { - #[codec(compact)] - query_id: QueryId, - dest: MultiLocation, - assets: MultiAssetFilter, - #[codec(compact)] - max_response_weight: u64, - }, - - /// Pay for the execution of some XCM `xcm` and `orders` with up to `weight` - /// picoseconds of execution time, paying for this with up to `fees` from the Holding Register. - /// - /// - `fees`: The asset(s) to remove from the Holding Register to pay for fees. - /// - `weight_limit`: The maximum amount of weight to purchase; this must be at least the - /// expected maximum weight of the total XCM to be executed for the - /// `AllowTopLevelPaidExecutionFrom` barrier to allow the XCM be executed. - /// - /// Kind: *Command* - /// - /// Errors: - BuyExecution { fees: MultiAsset, weight_limit: WeightLimit }, - - /// Refund any surplus weight previously bought with `BuyExecution`. - /// - /// Kind: *Command* - /// - /// Errors: None. - RefundSurplus, - - /// Set the Error Handler Register. This is code that should be called in the case of an error - /// happening. - /// - /// An error occurring within execution of this code will _NOT_ result in the error register - /// being set, nor will an error handler be called due to it. The error handler and appendix - /// may each still be set. - /// - /// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing - /// weight however includes only the difference between the previous handler and the new - /// handler, which can reasonably be negative, which would result in a surplus. - /// - /// Kind: *Command* - /// - /// Errors: None. - SetErrorHandler(Xcm), - - /// Set the Appendix Register. This is code that should be called after code execution - /// (including the error handler if any) is finished. This will be called regardless of whether - /// an error occurred. - /// - /// Any error occurring due to execution of this code will result in the error register being - /// set, and the error handler (if set) firing. - /// - /// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing - /// weight however includes only the difference between the previous appendix and the new - /// appendix, which can reasonably be negative, which would result in a surplus. - /// - /// Kind: *Command* - /// - /// Errors: None. - SetAppendix(Xcm), - - /// Clear the Error Register. - /// - /// Kind: *Command* - /// - /// Errors: None. - ClearError, - - /// Create some assets which are being held on behalf of the origin. - /// - /// - `assets`: The assets which are to be claimed. This must match exactly with the assets - /// claimable by the origin of the ticket. - /// - `ticket`: The ticket of the asset; this is an abstract identifier to help locate the - /// asset. - /// - /// Kind: *Command* - /// - /// Errors: - ClaimAsset { assets: MultiAssets, ticket: MultiLocation }, - - /// Always throws an error of type `Trap`. - /// - /// Kind: *Command* - /// - /// Errors: - /// - `Trap`: All circumstances, whose inner value is the same as this item's inner value. - Trap(#[codec(compact)] u64), - - /// Ask the destination system to respond with the most recent version of XCM that they - /// support in a `QueryResponse` instruction. Any changes to this should also elicit similar - /// responses when they happen. - /// - /// - `query_id`: An identifier that will be replicated into the returned XCM message. - /// - `max_response_weight`: The maximum amount of weight that the `QueryResponse` item which - /// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the - /// response may not execute at all. - /// - /// Kind: *Command* - /// - /// Errors: *Fallible* - SubscribeVersion { - #[codec(compact)] - query_id: QueryId, - #[codec(compact)] - max_response_weight: u64, - }, - - /// Cancel the effect of a previous `SubscribeVersion` instruction. - /// - /// Kind: *Command* - /// - /// Errors: *Fallible* - UnsubscribeVersion, -} - -impl Xcm { - pub fn into(self) -> Xcm { - Xcm::from(self) - } - pub fn from(xcm: Xcm) -> Self { - Self(xcm.0.into_iter().map(Instruction::::from).collect()) - } -} - -impl Instruction { - pub fn into(self) -> Instruction { - Instruction::from(self) - } - pub fn from(xcm: Instruction) -> Self { - use Instruction::*; - match xcm { - WithdrawAsset(assets) => WithdrawAsset(assets), - ReserveAssetDeposited(assets) => ReserveAssetDeposited(assets), - ReceiveTeleportedAsset(assets) => ReceiveTeleportedAsset(assets), - QueryResponse { query_id, response, max_weight } => - QueryResponse { query_id, response, max_weight }, - TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary }, - TransferReserveAsset { assets, dest, xcm } => - TransferReserveAsset { assets, dest, xcm }, - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, - HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, - HrmpChannelClosing { initiator, sender, recipient } => - HrmpChannelClosing { initiator, sender, recipient }, - Transact { origin_type, require_weight_at_most, call } => - Transact { origin_type, require_weight_at_most, call: call.into() }, - ReportError { query_id, dest, max_response_weight } => - ReportError { query_id, dest, max_response_weight }, - DepositAsset { assets, max_assets, beneficiary } => - DepositAsset { assets, max_assets, beneficiary }, - DepositReserveAsset { assets, max_assets, dest, xcm } => - DepositReserveAsset { assets, max_assets, dest, xcm }, - ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, - InitiateReserveWithdraw { assets, reserve, xcm } => - InitiateReserveWithdraw { assets, reserve, xcm }, - InitiateTeleport { assets, dest, xcm } => InitiateTeleport { assets, dest, xcm }, - QueryHolding { query_id, dest, assets, max_response_weight } => - QueryHolding { query_id, dest, assets, max_response_weight }, - BuyExecution { fees, weight_limit } => BuyExecution { fees, weight_limit }, - ClearOrigin => ClearOrigin, - DescendOrigin(who) => DescendOrigin(who), - RefundSurplus => RefundSurplus, - SetErrorHandler(xcm) => SetErrorHandler(xcm.into()), - SetAppendix(xcm) => SetAppendix(xcm.into()), - ClearError => ClearError, - ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket }, - Trap(code) => Trap(code), - SubscribeVersion { query_id, max_response_weight } => - SubscribeVersion { query_id, max_response_weight }, - UnsubscribeVersion => UnsubscribeVersion, - } - } -} - -// TODO: Automate Generation -impl> GetWeight for Instruction { - fn weight(&self) -> sp_weights::Weight { - use Instruction::*; - match self { - WithdrawAsset(assets) => sp_weights::Weight::from_parts(W::withdraw_asset(assets), 0), - ReserveAssetDeposited(assets) => - sp_weights::Weight::from_parts(W::reserve_asset_deposited(assets), 0), - ReceiveTeleportedAsset(assets) => - sp_weights::Weight::from_parts(W::receive_teleported_asset(assets), 0), - QueryResponse { query_id, response, max_weight } => - sp_weights::Weight::from_parts(W::query_response(query_id, response, max_weight), 0), - TransferAsset { assets, beneficiary } => - sp_weights::Weight::from_parts(W::transfer_asset(assets, beneficiary), 0), - TransferReserveAsset { assets, dest, xcm } => - sp_weights::Weight::from_parts(W::transfer_reserve_asset(&assets, dest, xcm), 0), - Transact { origin_type, require_weight_at_most, call } => - sp_weights::Weight::from_parts( - W::transact(origin_type, require_weight_at_most, call), - 0, - ), - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => - sp_weights::Weight::from_parts( - W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity), - 0, - ), - HrmpChannelAccepted { recipient } => - sp_weights::Weight::from_parts(W::hrmp_channel_accepted(recipient), 0), - HrmpChannelClosing { initiator, sender, recipient } => sp_weights::Weight::from_parts( - W::hrmp_channel_closing(initiator, sender, recipient), - 0, - ), - ClearOrigin => sp_weights::Weight::from_parts(W::clear_origin(), 0), - DescendOrigin(who) => sp_weights::Weight::from_parts(W::descend_origin(who), 0), - ReportError { query_id, dest, max_response_weight } => sp_weights::Weight::from_parts( - W::report_error(query_id, dest, max_response_weight), - 0, - ), - DepositAsset { assets, max_assets, beneficiary } => - sp_weights::Weight::from_parts(W::deposit_asset(assets, max_assets, beneficiary), 0), - DepositReserveAsset { assets, max_assets, dest, xcm } => - sp_weights::Weight::from_parts( - W::deposit_reserve_asset(assets, max_assets, dest, xcm), - 0, - ), - ExchangeAsset { give, receive } => - sp_weights::Weight::from_parts(W::exchange_asset(give, receive), 0), - InitiateReserveWithdraw { assets, reserve, xcm } => sp_weights::Weight::from_parts( - W::initiate_reserve_withdraw(assets, reserve, xcm), - 0, - ), - InitiateTeleport { assets, dest, xcm } => - sp_weights::Weight::from_parts(W::initiate_teleport(assets, dest, xcm), 0), - QueryHolding { query_id, dest, assets, max_response_weight } => - sp_weights::Weight::from_parts( - W::query_holding(query_id, dest, assets, max_response_weight), - 0, - ), - BuyExecution { fees, weight_limit } => - sp_weights::Weight::from_parts(W::buy_execution(fees, weight_limit), 0), - RefundSurplus => sp_weights::Weight::from_parts(W::refund_surplus(), 0), - SetErrorHandler(xcm) => sp_weights::Weight::from_parts(W::set_error_handler(xcm), 0), - SetAppendix(xcm) => sp_weights::Weight::from_parts(W::set_appendix(xcm), 0), - ClearError => sp_weights::Weight::from_parts(W::clear_error(), 0), - ClaimAsset { assets, ticket } => - sp_weights::Weight::from_parts(W::claim_asset(assets, ticket), 0), - Trap(code) => sp_weights::Weight::from_parts(W::trap(code), 0), - SubscribeVersion { query_id, max_response_weight } => sp_weights::Weight::from_parts( - W::subscribe_version(query_id, max_response_weight), - 0, - ), - UnsubscribeVersion => sp_weights::Weight::from_parts(W::unsubscribe_version(), 0), - } - } -} - -pub mod opaque { - /// The basic concrete type of `Xcm`, which doesn't make any assumptions about the - /// format of a call other than it is pre-encoded. - pub type Xcm = super::Xcm<()>; - - /// The basic concrete type of `Instruction`, which doesn't make any assumptions about the - /// format of a call other than it is pre-encoded. - pub type Instruction = super::Instruction<()>; -} - -// Convert from a v3 response to a v2 response -impl TryFrom for Response { - type Error = (); - fn try_from(response: NewResponse) -> result::Result { - Ok(match response { - NewResponse::Assets(assets) => Self::Assets(assets.try_into()?), - NewResponse::Version(version) => Self::Version(version), - NewResponse::ExecutionResult(error) => Self::ExecutionResult(match error { - Some((i, e)) => Some((i, e.try_into()?)), - None => None, - }), - NewResponse::Null => Self::Null, - _ => return Err(()), - }) - } -} - -// Convert from a v3 XCM to a v2 XCM. -impl TryFrom> for Xcm { - type Error = (); - fn try_from(new_xcm: NewXcm) -> result::Result { - Ok(Xcm(new_xcm.0.into_iter().map(TryInto::try_into).collect::>()?)) - } -} - -// Convert from a v3 instruction to a v2 instruction -impl TryFrom> for Instruction { - type Error = (); - fn try_from(instruction: NewInstruction) -> result::Result { - use NewInstruction::*; - Ok(match instruction { - WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?), - ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?), - ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?), - QueryResponse { query_id, response, max_weight, .. } => Self::QueryResponse { - query_id, - response: response.try_into()?, - max_weight: max_weight.ref_time(), - }, - TransferAsset { assets, beneficiary } => Self::TransferAsset { - assets: assets.try_into()?, - beneficiary: beneficiary.try_into()?, - }, - TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset { - assets: assets.try_into()?, - dest: dest.try_into()?, - xcm: xcm.try_into()?, - }, - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => - Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, - HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient }, - HrmpChannelClosing { initiator, sender, recipient } => - Self::HrmpChannelClosing { initiator, sender, recipient }, - Transact { origin_kind, require_weight_at_most, call } => Self::Transact { - origin_type: origin_kind.into(), - require_weight_at_most: require_weight_at_most.ref_time(), - call: call.into(), - }, - ReportError(response_info) => Self::ReportError { - query_id: response_info.query_id, - dest: response_info.destination.try_into()?, - max_response_weight: response_info.max_weight.ref_time(), - }, - DepositAsset { assets, beneficiary } => { - let max_assets = assets.count().ok_or(())?; - let beneficiary = beneficiary.try_into()?; - let assets = assets.try_into()?; - Self::DepositAsset { assets, max_assets, beneficiary } - }, - DepositReserveAsset { assets, dest, xcm } => { - let max_assets = assets.count().ok_or(())?; - let dest = dest.try_into()?; - let xcm = xcm.try_into()?; - let assets = assets.try_into()?; - Self::DepositReserveAsset { assets, max_assets, dest, xcm } - }, - ExchangeAsset { give, want, .. } => { - let give = give.try_into()?; - let receive = want.try_into()?; - Self::ExchangeAsset { give, receive } - }, - InitiateReserveWithdraw { assets, reserve, xcm } => { - // No `max_assets` here, so if there's a connt, then we cannot translate. - let assets = assets.try_into()?; - let reserve = reserve.try_into()?; - let xcm = xcm.try_into()?; - Self::InitiateReserveWithdraw { assets, reserve, xcm } - }, - InitiateTeleport { assets, dest, xcm } => { - // No `max_assets` here, so if there's a connt, then we cannot translate. - let assets = assets.try_into()?; - let dest = dest.try_into()?; - let xcm = xcm.try_into()?; - Self::InitiateTeleport { assets, dest, xcm } - }, - ReportHolding { response_info, assets } => Self::QueryHolding { - query_id: response_info.query_id, - dest: response_info.destination.try_into()?, - assets: assets.try_into()?, - max_response_weight: response_info.max_weight.ref_time(), - }, - BuyExecution { fees, weight_limit } => { - let fees = fees.try_into()?; - let weight_limit = weight_limit.try_into()?; - Self::BuyExecution { fees, weight_limit } - }, - ClearOrigin => Self::ClearOrigin, - DescendOrigin(who) => Self::DescendOrigin(who.try_into()?), - RefundSurplus => Self::RefundSurplus, - SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?), - SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?), - ClearError => Self::ClearError, - ClaimAsset { assets, ticket } => { - let assets = assets.try_into()?; - let ticket = ticket.try_into()?; - Self::ClaimAsset { assets, ticket } - }, - Trap(code) => Self::Trap(code), - SubscribeVersion { query_id, max_response_weight } => Self::SubscribeVersion { - query_id, - max_response_weight: max_response_weight.ref_time(), - }, - UnsubscribeVersion => Self::UnsubscribeVersion, - i => { - log::debug!(target: "xcm::v3tov2", "`{i:?}` not supported by v2"); - return Err(()); - }, - }) - } -} - -#[cfg(test)] -mod tests { - use super::{prelude::*, *}; - - #[test] - fn decoding_respects_limit() { - let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]); - let encoded = max_xcm.encode(); - assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok()); - - let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]); - let encoded = big_xcm.encode(); - assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); - - let nested_xcm = Xcm::<()>(vec![ - DepositReserveAsset { - assets: All.into(), - dest: Here.into(), - xcm: max_xcm, - max_assets: 1, - }; - (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize - ]); - let encoded = nested_xcm.encode(); - assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); - - let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]); - let encoded = even_more_nested_xcm.encode(); - assert_eq!(encoded.len(), 345730); - // This should not decode since the limit is 100 - assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition"); - assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); - } -} diff --git a/polkadot/xcm/src/v2/multiasset.rs b/polkadot/xcm/src/v2/multiasset.rs deleted file mode 100644 index 218f21b63b0a9bc40a64146c1d0515e13a99f88f..0000000000000000000000000000000000000000 --- a/polkadot/xcm/src/v2/multiasset.rs +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Cross-Consensus Message format asset data structures. -//! -//! This encompasses four types for representing assets: -//! - `MultiAsset`: A description of a single asset, either an instance of a non-fungible or some -//! amount of a fungible. -//! - `MultiAssets`: A collection of `MultiAsset`s. These are stored in a `Vec` and sorted with -//! fungibles first. -//! - `Wild`: A single asset wildcard, this can either be "all" assets, or all assets of a specific -//! kind. -//! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently -//! filtering an XCM holding account. - -use super::MultiLocation; -use crate::v3::{ - AssetId as NewAssetId, AssetInstance as NewAssetInstance, Fungibility as NewFungibility, - MultiAsset as NewMultiAsset, MultiAssetFilter as NewMultiAssetFilter, - MultiAssets as NewMultiAssets, WildFungibility as NewWildFungibility, - WildMultiAsset as NewWildMultiAsset, -}; -use alloc::{vec, vec::Vec}; -use codec::{self as codec, Decode, Encode}; -use core::cmp::Ordering; -use scale_info::TypeInfo; - -/// A general identifier for an instance of a non-fungible asset class. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum AssetInstance { - /// Undefined - used if the non-fungible asset class has only one instance. - Undefined, - - /// A compact index. Technically this could be greater than `u128`, but this implementation - /// supports only values up to `2**128 - 1`. - Index(#[codec(compact)] u128), - - /// A 4-byte fixed-length datum. - Array4([u8; 4]), - - /// An 8-byte fixed-length datum. - Array8([u8; 8]), - - /// A 16-byte fixed-length datum. - Array16([u8; 16]), - - /// A 32-byte fixed-length datum. - Array32([u8; 32]), - - /// An arbitrary piece of data. Use only when necessary. - Blob(Vec), -} - -impl From<()> for AssetInstance { - fn from(_: ()) -> Self { - Self::Undefined - } -} - -impl From<[u8; 4]> for AssetInstance { - fn from(x: [u8; 4]) -> Self { - Self::Array4(x) - } -} - -impl From<[u8; 8]> for AssetInstance { - fn from(x: [u8; 8]) -> Self { - Self::Array8(x) - } -} - -impl From<[u8; 16]> for AssetInstance { - fn from(x: [u8; 16]) -> Self { - Self::Array16(x) - } -} - -impl From<[u8; 32]> for AssetInstance { - fn from(x: [u8; 32]) -> Self { - Self::Array32(x) - } -} - -impl From> for AssetInstance { - fn from(x: Vec) -> Self { - Self::Blob(x) - } -} - -impl TryFrom for AssetInstance { - type Error = (); - fn try_from(value: NewAssetInstance) -> Result { - use NewAssetInstance::*; - Ok(match value { - Undefined => Self::Undefined, - Index(n) => Self::Index(n), - Array4(n) => Self::Array4(n), - Array8(n) => Self::Array8(n), - Array16(n) => Self::Array16(n), - Array32(n) => Self::Array32(n), - }) - } -} - -/// Classification of an asset being concrete or abstract. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum AssetId { - Concrete(MultiLocation), - Abstract(Vec), -} - -impl> From for AssetId { - fn from(x: T) -> Self { - Self::Concrete(x.into()) - } -} - -impl From> for AssetId { - fn from(x: Vec) -> Self { - Self::Abstract(x) - } -} - -impl TryFrom for AssetId { - type Error = (); - fn try_from(old: NewAssetId) -> Result { - use NewAssetId::*; - Ok(match old { - Concrete(l) => Self::Concrete(l.try_into()?), - Abstract(v) => { - let zeroes = v.iter().rev().position(|n| *n != 0).unwrap_or(v.len()); - Self::Abstract(v[0..(32 - zeroes)].to_vec()) - }, - }) - } -} - -impl AssetId { - /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. - pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> { - if let AssetId::Concrete(ref mut l) = self { - l.prepend_with(prepend.clone()).map_err(|_| ())?; - } - Ok(()) - } - - /// Mutate the asset to represent the same value from the perspective of a new `target` - /// location. The local chain's location is provided in `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { - if let AssetId::Concrete(ref mut l) = self { - l.reanchor(target, ancestry)?; - } - Ok(()) - } - - /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding - /// `MultiAsset` value. - pub fn into_multiasset(self, fun: Fungibility) -> MultiAsset { - MultiAsset { fun, id: self } - } - - /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding - /// `WildMultiAsset` wildcard (`AllOf`) value. - pub fn into_wild(self, fun: WildFungibility) -> WildMultiAsset { - WildMultiAsset::AllOf { fun, id: self } - } -} - -/// Classification of whether an asset is fungible or not, along with a mandatory amount or -/// instance. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum Fungibility { - Fungible(#[codec(compact)] u128), - NonFungible(AssetInstance), -} - -impl Fungibility { - pub fn is_kind(&self, w: WildFungibility) -> bool { - use Fungibility::*; - use WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible}; - matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible)) - } -} - -impl From for Fungibility { - fn from(amount: u128) -> Fungibility { - debug_assert_ne!(amount, 0); - Fungibility::Fungible(amount) - } -} - -impl> From for Fungibility { - fn from(instance: T) -> Fungibility { - Fungibility::NonFungible(instance.into()) - } -} - -impl TryFrom for Fungibility { - type Error = (); - fn try_from(value: NewFungibility) -> Result { - use NewFungibility::*; - Ok(match value { - Fungible(n) => Self::Fungible(n), - NonFungible(i) => Self::NonFungible(i.try_into()?), - }) - } -} - -#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub struct MultiAsset { - pub id: AssetId, - pub fun: Fungibility, -} - -impl PartialOrd for MultiAsset { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for MultiAsset { - fn cmp(&self, other: &Self) -> Ordering { - match (&self.fun, &other.fun) { - (Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less, - (Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater, - _ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)), - } - } -} - -impl, B: Into> From<(A, B)> for MultiAsset { - fn from((id, fun): (A, B)) -> MultiAsset { - MultiAsset { fun: fun.into(), id: id.into() } - } -} - -impl MultiAsset { - pub fn is_fungible(&self, maybe_id: Option) -> bool { - use Fungibility::*; - matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id) - } - - pub fn is_non_fungible(&self, maybe_id: Option) -> bool { - use Fungibility::*; - matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id) - } - - /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. - pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> { - self.id.prepend_with(prepend) - } - - /// Mutate the location of the asset identifier if concrete, giving it the same location - /// relative to a `target` context. The local context is provided as `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { - self.id.reanchor(target, ancestry) - } - - /// Mutate the location of the asset identifier if concrete, giving it the same location - /// relative to a `target` context. The local context is provided as `ancestry`. - pub fn reanchored( - mut self, - target: &MultiLocation, - ancestry: &MultiLocation, - ) -> Result { - self.id.reanchor(target, ancestry)?; - Ok(self) - } - - /// Returns true if `self` is a super-set of the given `inner`. - pub fn contains(&self, inner: &MultiAsset) -> bool { - use Fungibility::*; - if self.id == inner.id { - match (&self.fun, &inner.fun) { - (Fungible(a), Fungible(i)) if a >= i => return true, - (NonFungible(a), NonFungible(i)) if a == i => return true, - _ => (), - } - } - false - } -} - -impl TryFrom for MultiAsset { - type Error = (); - fn try_from(new: NewMultiAsset) -> Result { - Ok(Self { id: new.id.try_into()?, fun: new.fun.try_into()? }) - } -} - -/// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, -/// they must be sorted. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub struct MultiAssets(Vec); - -impl Decode for MultiAssets { - fn decode(input: &mut I) -> Result { - Self::from_sorted_and_deduplicated(Vec::::decode(input)?) - .map_err(|()| "Out of order".into()) - } -} - -impl TryFrom for MultiAssets { - type Error = (); - fn try_from(new: NewMultiAssets) -> Result { - let v = new - .into_inner() - .into_iter() - .map(MultiAsset::try_from) - .collect::, ()>>()?; - Ok(MultiAssets(v)) - } -} - -impl From> for MultiAssets { - fn from(mut assets: Vec) -> Self { - let mut res = Vec::with_capacity(assets.len()); - if !assets.is_empty() { - assets.sort(); - let mut iter = assets.into_iter(); - if let Some(first) = iter.next() { - let last = iter.fold(first, |a, b| -> MultiAsset { - match (a, b) { - ( - MultiAsset { fun: Fungibility::Fungible(a_amount), id: a_id }, - MultiAsset { fun: Fungibility::Fungible(b_amount), id: b_id }, - ) if a_id == b_id => MultiAsset { - id: a_id, - fun: Fungibility::Fungible(a_amount.saturating_add(b_amount)), - }, - ( - MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id }, - MultiAsset { fun: Fungibility::NonFungible(b_instance), id: b_id }, - ) if a_id == b_id && a_instance == b_instance => - MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id }, - (to_push, to_remember) => { - res.push(to_push); - to_remember - }, - } - }); - res.push(last); - } - } - Self(res) - } -} - -impl> From for MultiAssets { - fn from(x: T) -> Self { - Self(vec![x.into()]) - } -} - -impl MultiAssets { - /// A new (empty) value. - pub fn new() -> Self { - Self(Vec::new()) - } - - /// Create a new instance of `MultiAssets` from a `Vec` whose contents are sorted - /// and which contain no duplicates. - /// - /// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates. - /// If you can't guarantee that `r` is sorted and deduplicated, then use - /// `From::>::from` which is infallible. - pub fn from_sorted_and_deduplicated(r: Vec) -> Result { - if r.is_empty() { - return Ok(Self(Vec::new())) - } - r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&MultiAsset, ()> { - if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { - Ok(b) - } else { - Err(()) - } - })?; - Ok(Self(r)) - } - - /// Create a new instance of `MultiAssets` from a `Vec` whose contents are sorted - /// and which contain no duplicates. - /// - /// In release mode, this skips any checks to ensure that `r` is correct, making it a - /// negligible-cost operation. Generally though you should avoid using it unless you have a - /// strict proof that `r` is valid. - #[cfg(test)] - pub fn from_sorted_and_deduplicated_skip_checks(r: Vec) -> Self { - Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped") - } - /// Create a new instance of `MultiAssets` from a `Vec` whose contents are sorted - /// and which contain no duplicates. - /// - /// In release mode, this skips any checks to ensure that `r` is correct, making it a - /// negligible-cost operation. Generally though you should avoid using it unless you have a - /// strict proof that `r` is valid. - /// - /// In test mode, this checks anyway and panics on fail. - #[cfg(not(test))] - pub fn from_sorted_and_deduplicated_skip_checks(r: Vec) -> Self { - Self(r) - } - - /// Add some asset onto the list, saturating. This is quite a laborious operation since it - /// maintains the ordering. - pub fn push(&mut self, a: MultiAsset) { - if let Fungibility::Fungible(ref amount) = a.fun { - for asset in self.0.iter_mut().filter(|x| x.id == a.id) { - if let Fungibility::Fungible(ref mut balance) = asset.fun { - *balance = balance.saturating_add(*amount); - return - } - } - } - self.0.push(a); - self.0.sort(); - } - - /// Returns `true` if this definitely represents no asset. - pub fn is_none(&self) -> bool { - self.0.is_empty() - } - - /// Returns true if `self` is a super-set of the given `inner`. - pub fn contains(&self, inner: &MultiAsset) -> bool { - self.0.iter().any(|i| i.contains(inner)) - } - - /// Consume `self` and return the inner vec. - pub fn drain(self) -> Vec { - self.0 - } - - /// Return a reference to the inner vec. - pub fn inner(&self) -> &Vec { - &self.0 - } - - /// Return the number of distinct asset instances contained. - pub fn len(&self) -> usize { - self.0.len() - } - - /// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location. - pub fn prepend_with(&mut self, prefix: &MultiLocation) -> Result<(), ()> { - self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix)) - } - - /// Mutate the location of the asset identifier if concrete, giving it the same location - /// relative to a `target` context. The local context is provided as `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { - self.0.iter_mut().try_for_each(|i| i.reanchor(target, ancestry)) - } - - /// Return a reference to an item at a specific index or `None` if it doesn't exist. - pub fn get(&self, index: usize) -> Option<&MultiAsset> { - self.0.get(index) - } -} - -/// Classification of whether an asset is fungible or not. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum WildFungibility { - Fungible, - NonFungible, -} - -impl TryFrom for WildFungibility { - type Error = (); - fn try_from(value: NewWildFungibility) -> Result { - use NewWildFungibility::*; - Ok(match value { - Fungible => Self::Fungible, - NonFungible => Self::NonFungible, - }) - } -} - -/// A wildcard representing a set of assets. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum WildMultiAsset { - /// All assets in the holding register, up to `usize` individual assets (different instances of - /// non-fungibles could be separate assets). - All, - /// All assets in the holding register of a given fungibility and ID. If operating on - /// non-fungibles, then a limit is provided for the maximum amount of matching instances. - AllOf { id: AssetId, fun: WildFungibility }, -} - -impl WildMultiAsset { - /// Returns true if `self` is a super-set of the given `inner`. - /// - /// Typically, any wildcard is never contained in anything else, and a wildcard can contain any - /// other non-wildcard. For more details, see the implementation and tests. - pub fn contains(&self, inner: &MultiAsset) -> bool { - use WildMultiAsset::*; - match self { - AllOf { fun, id } => inner.fun.is_kind(*fun) && &inner.id == id, - All => true, - } - } - - /// Mutate the location of the asset identifier if concrete, giving it the same location - /// relative to a `target` context. The local context is provided as `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { - use WildMultiAsset::*; - match self { - AllOf { ref mut id, .. } => id.reanchor(target, ancestry).map_err(|_| ()), - All => Ok(()), - } - } -} - -impl, B: Into> From<(A, B)> for WildMultiAsset { - fn from((id, fun): (A, B)) -> WildMultiAsset { - WildMultiAsset::AllOf { fun: fun.into(), id: id.into() } - } -} - -/// `MultiAsset` collection, either `MultiAssets` or a single wildcard. -/// -/// Note: Vectors of wildcards whose encoding is supported in XCM v0 are unsupported -/// in this implementation and will result in a decode error. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum MultiAssetFilter { - Definite(MultiAssets), - Wild(WildMultiAsset), -} - -impl> From for MultiAssetFilter { - fn from(x: T) -> Self { - Self::Wild(x.into()) - } -} - -impl From for MultiAssetFilter { - fn from(x: MultiAsset) -> Self { - Self::Definite(vec![x].into()) - } -} - -impl From> for MultiAssetFilter { - fn from(x: Vec) -> Self { - Self::Definite(x.into()) - } -} - -impl From for MultiAssetFilter { - fn from(x: MultiAssets) -> Self { - Self::Definite(x) - } -} - -impl MultiAssetFilter { - /// Returns true if `self` is a super-set of the given `inner`. - /// - /// Typically, any wildcard is never contained in anything else, and a wildcard can contain any - /// other non-wildcard. For more details, see the implementation and tests. - pub fn contains(&self, inner: &MultiAsset) -> bool { - match self { - MultiAssetFilter::Definite(ref assets) => assets.contains(inner), - MultiAssetFilter::Wild(ref wild) => wild.contains(inner), - } - } - - /// Mutate the location of the asset identifier if concrete, giving it the same location - /// relative to a `target` context. The local context is provided as `ancestry`. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { - match self { - MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(target, ancestry), - MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(target, ancestry), - } - } -} - -impl TryFrom for WildMultiAsset { - type Error = (); - fn try_from(new: NewWildMultiAsset) -> Result { - use NewWildMultiAsset::*; - Ok(match new { - AllOf { id, fun } | AllOfCounted { id, fun, .. } => - Self::AllOf { id: id.try_into()?, fun: fun.try_into()? }, - All | AllCounted(_) => Self::All, - }) - } -} - -impl TryFrom for MultiAssetFilter { - type Error = (); - fn try_from(old: NewMultiAssetFilter) -> Result { - use NewMultiAssetFilter::*; - Ok(match old { - Definite(x) => Self::Definite(x.try_into()?), - Wild(x) => Self::Wild(x.try_into()?), - }) - } -} diff --git a/polkadot/xcm/src/v2/multilocation.rs b/polkadot/xcm/src/v2/multilocation.rs deleted file mode 100644 index 9399ca6619c0d6c2842cfa6ab312936d727d6518..0000000000000000000000000000000000000000 --- a/polkadot/xcm/src/v2/multilocation.rs +++ /dev/null @@ -1,1105 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Cross-Consensus Message format data structures. - -use super::Junction; -use crate::v3::MultiLocation as NewMultiLocation; -use codec::{Decode, Encode, MaxEncodedLen}; -use core::{mem, result}; -use scale_info::TypeInfo; - -/// A relative path between state-bearing consensus systems. -/// -/// A location in a consensus system is defined as an *isolatable state machine* held within global -/// consensus. The location in question need not have a sophisticated consensus algorithm of its -/// own; a single account within Ethereum, for example, could be considered a location. -/// -/// A very-much non-exhaustive list of types of location include: -/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain. -/// - A layer-0 super-chain, e.g. the Polkadot Relay chain. -/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum. -/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based -/// Substrate chain. -/// - An account. -/// -/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the -/// relative path between two locations, and cannot generally be used to refer to a location -/// universally. It is comprised of an integer number of parents specifying the number of times to -/// "escape" upwards into the containing consensus system and then a number of *junctions*, each -/// diving down and specifying some interior portion of state (which may be considered a -/// "sub-consensus" system). -/// -/// This specific `MultiLocation` implementation uses a `Junctions` datatype which is a Rust `enum` -/// in order to make pattern matching easier. There are occasions where it is important to ensure -/// that a value is strictly an interior location, in those cases, `Junctions` may be used. -/// -/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system. -#[derive(Clone, Decode, Encode, Eq, PartialEq, Ord, PartialOrd, Debug, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub struct MultiLocation { - /// The number of parent junctions at the beginning of this `MultiLocation`. - pub parents: u8, - /// The interior (i.e. non-parent) junctions that this `MultiLocation` contains. - pub interior: Junctions, -} - -impl Default for MultiLocation { - fn default() -> Self { - Self { parents: 0, interior: Junctions::Here } - } -} - -/// A relative location which is constrained to be an interior location of the context. -/// -/// See also `MultiLocation`. -pub type InteriorMultiLocation = Junctions; - -impl MultiLocation { - /// Creates a new `MultiLocation` with the given number of parents and interior junctions. - pub fn new(parents: u8, junctions: Junctions) -> MultiLocation { - MultiLocation { parents, interior: junctions } - } - - /// Consume `self` and return the equivalent `VersionedLocation` value. - pub fn versioned(self) -> crate::VersionedLocation { - self.into() - } - - /// Creates a new `MultiLocation` with 0 parents and a `Here` interior. - /// - /// The resulting `MultiLocation` can be interpreted as the "current consensus system". - pub const fn here() -> MultiLocation { - MultiLocation { parents: 0, interior: Junctions::Here } - } - - /// Creates a new `MultiLocation` which evaluates to the parent context. - pub const fn parent() -> MultiLocation { - MultiLocation { parents: 1, interior: Junctions::Here } - } - - /// Creates a new `MultiLocation` which evaluates to the grand parent context. - pub const fn grandparent() -> MultiLocation { - MultiLocation { parents: 2, interior: Junctions::Here } - } - - /// Creates a new `MultiLocation` with `parents` and an empty (`Here`) interior. - pub const fn ancestor(parents: u8) -> MultiLocation { - MultiLocation { parents, interior: Junctions::Here } - } - - /// Whether the `MultiLocation` has no parents and has a `Here` interior. - pub const fn is_here(&self) -> bool { - self.parents == 0 && self.interior.len() == 0 - } - - /// Return a reference to the interior field. - pub fn interior(&self) -> &Junctions { - &self.interior - } - - /// Return a mutable reference to the interior field. - pub fn interior_mut(&mut self) -> &mut Junctions { - &mut self.interior - } - - /// Returns the number of `Parent` junctions at the beginning of `self`. - pub const fn parent_count(&self) -> u8 { - self.parents - } - - /// Returns boolean indicating whether `self` contains only the specified amount of - /// parents and no interior junctions. - pub const fn contains_parents_only(&self, count: u8) -> bool { - matches!(self.interior, Junctions::Here) && self.parents == count - } - - /// Returns the number of parents and junctions in `self`. - pub const fn len(&self) -> usize { - self.parent_count() as usize + self.interior.len() - } - - /// Returns the first interior junction, or `None` if the location is empty or contains only - /// parents. - pub fn first_interior(&self) -> Option<&Junction> { - self.interior.first() - } - - /// Returns last junction, or `None` if the location is empty or contains only parents. - pub fn last(&self) -> Option<&Junction> { - self.interior.last() - } - - /// Splits off the first interior junction, returning the remaining suffix (first item in tuple) - /// and the first element (second item in tuple) or `None` if it was empty. - pub fn split_first_interior(self) -> (MultiLocation, Option) { - let MultiLocation { parents, interior: junctions } = self; - let (suffix, first) = junctions.split_first(); - let multilocation = MultiLocation { parents, interior: suffix }; - (multilocation, first) - } - - /// Splits off the last interior junction, returning the remaining prefix (first item in tuple) - /// and the last element (second item in tuple) or `None` if it was empty or if `self` only - /// contains parents. - pub fn split_last_interior(self) -> (MultiLocation, Option) { - let MultiLocation { parents, interior: junctions } = self; - let (prefix, last) = junctions.split_last(); - let multilocation = MultiLocation { parents, interior: prefix }; - (multilocation, last) - } - - /// Mutates `self`, suffixing its interior junctions with `new`. Returns `Err` with `new` in - /// case of overflow. - pub fn push_interior(&mut self, new: Junction) -> result::Result<(), Junction> { - self.interior.push(new) - } - - /// Mutates `self`, prefixing its interior junctions with `new`. Returns `Err` with `new` in - /// case of overflow. - pub fn push_front_interior(&mut self, new: Junction) -> result::Result<(), Junction> { - self.interior.push_front(new) - } - - /// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with - /// the original value of `self` in case of overflow. - pub fn pushed_with_interior(self, new: Junction) -> result::Result { - match self.interior.pushed_with(new) { - Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }), - Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)), - } - } - - /// Consumes `self` and returns a `MultiLocation` prefixed with `new`, or an `Err` with the - /// original value of `self` in case of overflow. - pub fn pushed_front_with_interior( - self, - new: Junction, - ) -> result::Result { - match self.interior.pushed_front_with(new) { - Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }), - Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)), - } - } - - /// Returns the junction at index `i`, or `None` if the location is a parent or if the location - /// does not contain that many elements. - pub fn at(&self, i: usize) -> Option<&Junction> { - let num_parents = self.parents as usize; - if i < num_parents { - return None - } - self.interior.at(i - num_parents) - } - - /// Returns a mutable reference to the junction at index `i`, or `None` if the location is a - /// parent or if it doesn't contain that many elements. - pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> { - let num_parents = self.parents as usize; - if i < num_parents { - return None - } - self.interior.at_mut(i - num_parents) - } - - /// Decrements the parent count by 1. - pub fn dec_parent(&mut self) { - self.parents = self.parents.saturating_sub(1); - } - - /// Removes the first interior junction from `self`, returning it - /// (or `None` if it was empty or if `self` contains only parents). - pub fn take_first_interior(&mut self) -> Option { - self.interior.take_first() - } - - /// Removes the last element from `interior`, returning it (or `None` if it was empty or if - /// `self` only contains parents). - pub fn take_last(&mut self) -> Option { - self.interior.take_last() - } - - /// Ensures that `self` has the same number of parents as `prefix`, its junctions begins with - /// the junctions of `prefix` and that it has a single `Junction` item following. - /// If so, returns a reference to this `Junction` item. - /// - /// # Example - /// ```rust - /// # use staging_xcm::v2::{Junctions::*, Junction::*, MultiLocation}; - /// let mut m = MultiLocation::new(1, [PalletInstance(3), OnlyChild].into()); - /// assert_eq!( - /// m.match_and_split(&MultiLocation::new(1, [PalletInstance(3)].into())), - /// Some(&OnlyChild), - /// ); - /// assert_eq!(m.match_and_split(&MultiLocation::new(1, Here)), None); - /// ``` - pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> { - if self.parents != prefix.parents { - return None - } - self.interior.match_and_split(&prefix.interior) - } - - /// Returns whether `self` has the same number of parents as `prefix` and its junctions begins - /// with the junctions of `prefix`. - /// - /// # Example - /// ```rust - /// # use staging_xcm::v2::{Junctions::*, Junction::*, MultiLocation}; - /// let m = MultiLocation::new(1, [PalletInstance(3), OnlyChild, OnlyChild].into()); - /// assert!(m.starts_with(&MultiLocation::new(1, [PalletInstance(3)].into()))); - /// assert!(!m.starts_with(&MultiLocation::new(1, [GeneralIndex(99)].into()))); - /// assert!(!m.starts_with(&MultiLocation::new(0, [PalletInstance(3)].into()))); - /// ``` - pub fn starts_with(&self, prefix: &MultiLocation) -> bool { - if self.parents != prefix.parents { - return false - } - self.interior.starts_with(&prefix.interior) - } - - /// Mutate `self` so that it is suffixed with `suffix`. - /// - /// Does not modify `self` and returns `Err` with `suffix` in case of overflow. - /// - /// # Example - /// ```rust - /// # use staging_xcm::v2::{Junctions::*, Junction::*, MultiLocation}; - /// let mut m = MultiLocation::new(1, [Parachain(21)].into()); - /// assert_eq!(m.append_with([PalletInstance(3)].into()), Ok(())); - /// assert_eq!(m, MultiLocation::new(1, [Parachain(21), PalletInstance(3)].into())); - /// ``` - pub fn append_with(&mut self, suffix: Junctions) -> Result<(), Junctions> { - if self.interior.len().saturating_add(suffix.len()) > MAX_JUNCTIONS { - return Err(suffix) - } - for j in suffix.into_iter() { - self.interior.push(j).expect("Already checked the sum of the len()s; qed") - } - Ok(()) - } - - /// Mutate `self` so that it is prefixed with `prefix`. - /// - /// Does not modify `self` and returns `Err` with `prefix` in case of overflow. - /// - /// # Example - /// ```rust - /// # use staging_xcm::v2::{Junctions::*, Junction::*, MultiLocation}; - /// let mut m = MultiLocation::new(2, [PalletInstance(3)].into()); - /// assert_eq!(m.prepend_with(MultiLocation::new(1, [Parachain(21), OnlyChild].into())), Ok(())); - /// assert_eq!(m, MultiLocation::new(1, [PalletInstance(3)].into())); - /// ``` - pub fn prepend_with(&mut self, mut prefix: MultiLocation) -> Result<(), MultiLocation> { - // prefix self (suffix) - // P .. P I .. I p .. p i .. i - let prepend_interior = prefix.interior.len().saturating_sub(self.parents as usize); - let final_interior = self.interior.len().saturating_add(prepend_interior); - if final_interior > MAX_JUNCTIONS { - return Err(prefix) - } - let suffix_parents = (self.parents as usize).saturating_sub(prefix.interior.len()); - let final_parents = (prefix.parents as usize).saturating_add(suffix_parents); - if final_parents > 255 { - return Err(prefix) - } - - // cancel out the final item on the prefix interior for one of the suffix's parents. - while self.parents > 0 && prefix.take_last().is_some() { - self.dec_parent(); - } - - // now we have either removed all suffix's parents or prefix interior. - // this means we can combine the prefix's and suffix's remaining parents/interior since - // we know that with at least one empty, the overall order will be respected: - // prefix self (suffix) - // P .. P (I) p .. p i .. i => P + p .. (no I) i - // -- or -- - // P .. P I .. I (p) i .. i => P (no p) .. I + i - - self.parents = self.parents.saturating_add(prefix.parents); - for j in prefix.interior.into_iter().rev() { - self.push_front_interior(j) - .expect("final_interior no greater than MAX_JUNCTIONS; qed"); - } - Ok(()) - } - - /// Consume `self` and return the value representing the same location from the point of view - /// of `target`. The context of `self` is provided as `ancestry`. - /// - /// Returns an `Err` with the unmodified `self` in the case of error. - pub fn reanchored( - mut self, - target: &MultiLocation, - ancestry: &MultiLocation, - ) -> Result { - match self.reanchor(target, ancestry) { - Ok(()) => Ok(self), - Err(()) => Err(self), - } - } - - /// Mutate `self` so that it represents the same location from the point of view of `target`. - /// The context of `self` is provided as `ancestry`. - /// - /// Does not modify `self` in case of overflow. - pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { - // TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this. - - // 1. Use our `ancestry` to figure out how the `target` would address us. - let inverted_target = ancestry.inverted(target)?; - - // 2. Prepend `inverted_target` to `self` to get self's location from the perspective of - // `target`. - self.prepend_with(inverted_target).map_err(|_| ())?; - - // 3. Given that we know some of `target` ancestry, ensure that any parents in `self` are - // strictly needed. - self.simplify(target.interior()); - - Ok(()) - } - - /// Treating `self` as a context, determine how it would be referenced by a `target` location. - pub fn inverted(&self, target: &MultiLocation) -> Result { - use Junction::OnlyChild; - let mut ancestry = self.clone(); - let mut junctions = Junctions::Here; - for _ in 0..target.parent_count() { - junctions = junctions - .pushed_front_with(ancestry.interior.take_last().unwrap_or(OnlyChild)) - .map_err(|_| ())?; - } - let parents = target.interior().len() as u8; - Ok(MultiLocation::new(parents, junctions)) - } - - /// Remove any unneeded parents/junctions in `self` based on the given context it will be - /// interpreted in. - pub fn simplify(&mut self, context: &Junctions) { - if context.len() < self.parents as usize { - // Not enough context - return - } - while self.parents > 0 { - let maybe = context.at(context.len() - (self.parents as usize)); - match (self.interior.first(), maybe) { - (Some(i), Some(j)) if i == j => { - self.interior.take_first(); - self.parents -= 1; - }, - _ => break, - } - } - } -} - -impl TryFrom for MultiLocation { - type Error = (); - fn try_from(x: NewMultiLocation) -> result::Result { - Ok(MultiLocation { parents: x.parents, interior: x.interior.try_into()? }) - } -} - -/// A unit struct which can be converted into a `MultiLocation` of `parents` value 1. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct Parent; -impl From for MultiLocation { - fn from(_: Parent) -> Self { - MultiLocation { parents: 1, interior: Junctions::Here } - } -} - -/// A tuple struct which can be converted into a `MultiLocation` of `parents` value 1 with the inner -/// interior. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct ParentThen(pub Junctions); -impl From for MultiLocation { - fn from(ParentThen(interior): ParentThen) -> Self { - MultiLocation { parents: 1, interior } - } -} - -/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct Ancestor(pub u8); -impl From for MultiLocation { - fn from(Ancestor(parents): Ancestor) -> Self { - MultiLocation { parents, interior: Junctions::Here } - } -} - -/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value and the -/// inner interior. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct AncestorThen(pub u8, pub Interior); -impl> From> for MultiLocation { - fn from(AncestorThen(parents, interior): AncestorThen) -> Self { - MultiLocation { parents, interior: interior.into() } - } -} - -xcm_procedural::impl_conversion_functions_for_multilocation_v2!(); -xcm_procedural::impl_conversion_functions_for_junctions_v2!(); - -/// Maximum number of `Junction`s that a `Junctions` can contain. -const MAX_JUNCTIONS: usize = 8; - -/// Non-parent junctions that can be constructed, up to the length of 8. This specific `Junctions` -/// implementation uses a Rust `enum` in order to make pattern matching easier. -/// -/// Parent junctions cannot be constructed with this type. Refer to `MultiLocation` for -/// instructions on constructing parent junctions. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum Junctions { - /// The interpreting consensus system. - Here, - /// A relative path comprising 1 junction. - X1(Junction), - /// A relative path comprising 2 junctions. - X2(Junction, Junction), - /// A relative path comprising 3 junctions. - X3(Junction, Junction, Junction), - /// A relative path comprising 4 junctions. - X4(Junction, Junction, Junction, Junction), - /// A relative path comprising 5 junctions. - X5(Junction, Junction, Junction, Junction, Junction), - /// A relative path comprising 6 junctions. - X6(Junction, Junction, Junction, Junction, Junction, Junction), - /// A relative path comprising 7 junctions. - X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction), - /// A relative path comprising 8 junctions. - X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction), -} - -pub struct JunctionsIterator(Junctions); -impl Iterator for JunctionsIterator { - type Item = Junction; - fn next(&mut self) -> Option { - self.0.take_first() - } -} - -impl DoubleEndedIterator for JunctionsIterator { - fn next_back(&mut self) -> Option { - self.0.take_last() - } -} - -pub struct JunctionsRefIterator<'a> { - junctions: &'a Junctions, - next: usize, - back: usize, -} - -impl<'a> Iterator for JunctionsRefIterator<'a> { - type Item = &'a Junction; - fn next(&mut self) -> Option<&'a Junction> { - if self.next.saturating_add(self.back) >= self.junctions.len() { - return None - } - - let result = self.junctions.at(self.next); - self.next += 1; - result - } -} - -impl<'a> DoubleEndedIterator for JunctionsRefIterator<'a> { - fn next_back(&mut self) -> Option<&'a Junction> { - let next_back = self.back.saturating_add(1); - // checked_sub here, because if the result is less than 0, we end iteration - let index = self.junctions.len().checked_sub(next_back)?; - if self.next > index { - return None - } - self.back = next_back; - - self.junctions.at(index) - } -} - -impl<'a> IntoIterator for &'a Junctions { - type Item = &'a Junction; - type IntoIter = JunctionsRefIterator<'a>; - fn into_iter(self) -> Self::IntoIter { - JunctionsRefIterator { junctions: self, next: 0, back: 0 } - } -} - -impl IntoIterator for Junctions { - type Item = Junction; - type IntoIter = JunctionsIterator; - fn into_iter(self) -> Self::IntoIter { - JunctionsIterator(self) - } -} - -impl Junctions { - /// Convert `self` into a `MultiLocation` containing 0 parents. - /// - /// Similar to `Into::into`, except that this method can be used in a const evaluation context. - pub const fn into(self) -> MultiLocation { - MultiLocation { parents: 0, interior: self } - } - - /// Convert `self` into a `MultiLocation` containing `n` parents. - /// - /// Similar to `Self::into`, with the added ability to specify the number of parent junctions. - pub const fn into_exterior(self, n: u8) -> MultiLocation { - MultiLocation { parents: n, interior: self } - } - - /// Returns first junction, or `None` if the location is empty. - pub fn first(&self) -> Option<&Junction> { - match &self { - Junctions::Here => None, - Junctions::X1(ref a) => Some(a), - Junctions::X2(ref a, ..) => Some(a), - Junctions::X3(ref a, ..) => Some(a), - Junctions::X4(ref a, ..) => Some(a), - Junctions::X5(ref a, ..) => Some(a), - Junctions::X6(ref a, ..) => Some(a), - Junctions::X7(ref a, ..) => Some(a), - Junctions::X8(ref a, ..) => Some(a), - } - } - - /// Returns last junction, or `None` if the location is empty. - pub fn last(&self) -> Option<&Junction> { - match &self { - Junctions::Here => None, - Junctions::X1(ref a) => Some(a), - Junctions::X2(.., ref a) => Some(a), - Junctions::X3(.., ref a) => Some(a), - Junctions::X4(.., ref a) => Some(a), - Junctions::X5(.., ref a) => Some(a), - Junctions::X6(.., ref a) => Some(a), - Junctions::X7(.., ref a) => Some(a), - Junctions::X8(.., ref a) => Some(a), - } - } - - /// Splits off the first junction, returning the remaining suffix (first item in tuple) and the - /// first element (second item in tuple) or `None` if it was empty. - pub fn split_first(self) -> (Junctions, Option) { - match self { - Junctions::Here => (Junctions::Here, None), - Junctions::X1(a) => (Junctions::Here, Some(a)), - Junctions::X2(a, b) => (Junctions::X1(b), Some(a)), - Junctions::X3(a, b, c) => (Junctions::X2(b, c), Some(a)), - Junctions::X4(a, b, c, d) => (Junctions::X3(b, c, d), Some(a)), - Junctions::X5(a, b, c, d, e) => (Junctions::X4(b, c, d, e), Some(a)), - Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(b, c, d, e, f), Some(a)), - Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(b, c, d, e, f, g), Some(a)), - Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(b, c, d, e, f, g, h), Some(a)), - } - } - - /// Splits off the last junction, returning the remaining prefix (first item in tuple) and the - /// last element (second item in tuple) or `None` if it was empty. - pub fn split_last(self) -> (Junctions, Option) { - match self { - Junctions::Here => (Junctions::Here, None), - Junctions::X1(a) => (Junctions::Here, Some(a)), - Junctions::X2(a, b) => (Junctions::X1(a), Some(b)), - Junctions::X3(a, b, c) => (Junctions::X2(a, b), Some(c)), - Junctions::X4(a, b, c, d) => (Junctions::X3(a, b, c), Some(d)), - Junctions::X5(a, b, c, d, e) => (Junctions::X4(a, b, c, d), Some(e)), - Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(a, b, c, d, e), Some(f)), - Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(a, b, c, d, e, f), Some(g)), - Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(a, b, c, d, e, f, g), Some(h)), - } - } - - /// Removes the first element from `self`, returning it (or `None` if it was empty). - pub fn take_first(&mut self) -> Option { - let mut d = Junctions::Here; - mem::swap(&mut *self, &mut d); - let (tail, head) = d.split_first(); - *self = tail; - head - } - - /// Removes the last element from `self`, returning it (or `None` if it was empty). - pub fn take_last(&mut self) -> Option { - let mut d = Junctions::Here; - mem::swap(&mut *self, &mut d); - let (head, tail) = d.split_last(); - *self = head; - tail - } - - /// Mutates `self` to be appended with `new` or returns an `Err` with `new` if would overflow. - pub fn push(&mut self, new: Junction) -> result::Result<(), Junction> { - let mut dummy = Junctions::Here; - mem::swap(self, &mut dummy); - match dummy.pushed_with(new) { - Ok(s) => { - *self = s; - Ok(()) - }, - Err((s, j)) => { - *self = s; - Err(j) - }, - } - } - - /// Mutates `self` to be prepended with `new` or returns an `Err` with `new` if would overflow. - pub fn push_front(&mut self, new: Junction) -> result::Result<(), Junction> { - let mut dummy = Junctions::Here; - mem::swap(self, &mut dummy); - match dummy.pushed_front_with(new) { - Ok(s) => { - *self = s; - Ok(()) - }, - Err((s, j)) => { - *self = s; - Err(j) - }, - } - } - - /// Consumes `self` and returns a `Junctions` suffixed with `new`, or an `Err` with the - /// original value of `self` and `new` in case of overflow. - pub fn pushed_with(self, new: Junction) -> result::Result { - Ok(match self { - Junctions::Here => Junctions::X1(new), - Junctions::X1(a) => Junctions::X2(a, new), - Junctions::X2(a, b) => Junctions::X3(a, b, new), - Junctions::X3(a, b, c) => Junctions::X4(a, b, c, new), - Junctions::X4(a, b, c, d) => Junctions::X5(a, b, c, d, new), - Junctions::X5(a, b, c, d, e) => Junctions::X6(a, b, c, d, e, new), - Junctions::X6(a, b, c, d, e, f) => Junctions::X7(a, b, c, d, e, f, new), - Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(a, b, c, d, e, f, g, new), - s => Err((s, new))?, - }) - } - - /// Consumes `self` and returns a `Junctions` prefixed with `new`, or an `Err` with the - /// original value of `self` and `new` in case of overflow. - pub fn pushed_front_with(self, new: Junction) -> result::Result { - Ok(match self { - Junctions::Here => Junctions::X1(new), - Junctions::X1(a) => Junctions::X2(new, a), - Junctions::X2(a, b) => Junctions::X3(new, a, b), - Junctions::X3(a, b, c) => Junctions::X4(new, a, b, c), - Junctions::X4(a, b, c, d) => Junctions::X5(new, a, b, c, d), - Junctions::X5(a, b, c, d, e) => Junctions::X6(new, a, b, c, d, e), - Junctions::X6(a, b, c, d, e, f) => Junctions::X7(new, a, b, c, d, e, f), - Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(new, a, b, c, d, e, f, g), - s => Err((s, new))?, - }) - } - - /// Returns the number of junctions in `self`. - pub const fn len(&self) -> usize { - match &self { - Junctions::Here => 0, - Junctions::X1(..) => 1, - Junctions::X2(..) => 2, - Junctions::X3(..) => 3, - Junctions::X4(..) => 4, - Junctions::X5(..) => 5, - Junctions::X6(..) => 6, - Junctions::X7(..) => 7, - Junctions::X8(..) => 8, - } - } - - /// Returns the junction at index `i`, or `None` if the location doesn't contain that many - /// elements. - pub fn at(&self, i: usize) -> Option<&Junction> { - Some(match (i, self) { - (0, Junctions::X1(ref a)) => a, - (0, Junctions::X2(ref a, ..)) => a, - (0, Junctions::X3(ref a, ..)) => a, - (0, Junctions::X4(ref a, ..)) => a, - (0, Junctions::X5(ref a, ..)) => a, - (0, Junctions::X6(ref a, ..)) => a, - (0, Junctions::X7(ref a, ..)) => a, - (0, Junctions::X8(ref a, ..)) => a, - (1, Junctions::X2(_, ref a)) => a, - (1, Junctions::X3(_, ref a, ..)) => a, - (1, Junctions::X4(_, ref a, ..)) => a, - (1, Junctions::X5(_, ref a, ..)) => a, - (1, Junctions::X6(_, ref a, ..)) => a, - (1, Junctions::X7(_, ref a, ..)) => a, - (1, Junctions::X8(_, ref a, ..)) => a, - (2, Junctions::X3(_, _, ref a)) => a, - (2, Junctions::X4(_, _, ref a, ..)) => a, - (2, Junctions::X5(_, _, ref a, ..)) => a, - (2, Junctions::X6(_, _, ref a, ..)) => a, - (2, Junctions::X7(_, _, ref a, ..)) => a, - (2, Junctions::X8(_, _, ref a, ..)) => a, - (3, Junctions::X4(_, _, _, ref a)) => a, - (3, Junctions::X5(_, _, _, ref a, ..)) => a, - (3, Junctions::X6(_, _, _, ref a, ..)) => a, - (3, Junctions::X7(_, _, _, ref a, ..)) => a, - (3, Junctions::X8(_, _, _, ref a, ..)) => a, - (4, Junctions::X5(_, _, _, _, ref a)) => a, - (4, Junctions::X6(_, _, _, _, ref a, ..)) => a, - (4, Junctions::X7(_, _, _, _, ref a, ..)) => a, - (4, Junctions::X8(_, _, _, _, ref a, ..)) => a, - (5, Junctions::X6(_, _, _, _, _, ref a)) => a, - (5, Junctions::X7(_, _, _, _, _, ref a, ..)) => a, - (5, Junctions::X8(_, _, _, _, _, ref a, ..)) => a, - (6, Junctions::X7(_, _, _, _, _, _, ref a)) => a, - (6, Junctions::X8(_, _, _, _, _, _, ref a, ..)) => a, - (7, Junctions::X8(_, _, _, _, _, _, _, ref a)) => a, - _ => return None, - }) - } - - /// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't - /// contain that many elements. - pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> { - Some(match (i, self) { - (0, Junctions::X1(ref mut a)) => a, - (0, Junctions::X2(ref mut a, ..)) => a, - (0, Junctions::X3(ref mut a, ..)) => a, - (0, Junctions::X4(ref mut a, ..)) => a, - (0, Junctions::X5(ref mut a, ..)) => a, - (0, Junctions::X6(ref mut a, ..)) => a, - (0, Junctions::X7(ref mut a, ..)) => a, - (0, Junctions::X8(ref mut a, ..)) => a, - (1, Junctions::X2(_, ref mut a)) => a, - (1, Junctions::X3(_, ref mut a, ..)) => a, - (1, Junctions::X4(_, ref mut a, ..)) => a, - (1, Junctions::X5(_, ref mut a, ..)) => a, - (1, Junctions::X6(_, ref mut a, ..)) => a, - (1, Junctions::X7(_, ref mut a, ..)) => a, - (1, Junctions::X8(_, ref mut a, ..)) => a, - (2, Junctions::X3(_, _, ref mut a)) => a, - (2, Junctions::X4(_, _, ref mut a, ..)) => a, - (2, Junctions::X5(_, _, ref mut a, ..)) => a, - (2, Junctions::X6(_, _, ref mut a, ..)) => a, - (2, Junctions::X7(_, _, ref mut a, ..)) => a, - (2, Junctions::X8(_, _, ref mut a, ..)) => a, - (3, Junctions::X4(_, _, _, ref mut a)) => a, - (3, Junctions::X5(_, _, _, ref mut a, ..)) => a, - (3, Junctions::X6(_, _, _, ref mut a, ..)) => a, - (3, Junctions::X7(_, _, _, ref mut a, ..)) => a, - (3, Junctions::X8(_, _, _, ref mut a, ..)) => a, - (4, Junctions::X5(_, _, _, _, ref mut a)) => a, - (4, Junctions::X6(_, _, _, _, ref mut a, ..)) => a, - (4, Junctions::X7(_, _, _, _, ref mut a, ..)) => a, - (4, Junctions::X8(_, _, _, _, ref mut a, ..)) => a, - (5, Junctions::X6(_, _, _, _, _, ref mut a)) => a, - (5, Junctions::X7(_, _, _, _, _, ref mut a, ..)) => a, - (5, Junctions::X8(_, _, _, _, _, ref mut a, ..)) => a, - (6, Junctions::X7(_, _, _, _, _, _, ref mut a)) => a, - (6, Junctions::X8(_, _, _, _, _, _, ref mut a, ..)) => a, - (7, Junctions::X8(_, _, _, _, _, _, _, ref mut a)) => a, - _ => return None, - }) - } - - /// Returns a reference iterator over the junctions. - pub fn iter(&self) -> JunctionsRefIterator { - JunctionsRefIterator { junctions: self, next: 0, back: 0 } - } - - /// Returns a reference iterator over the junctions in reverse. - #[deprecated(note = "Please use iter().rev()")] - pub fn iter_rev(&self) -> impl Iterator + '_ { - self.iter().rev() - } - - /// Consumes `self` and returns an iterator over the junctions in reverse. - #[deprecated(note = "Please use into_iter().rev()")] - pub fn into_iter_rev(self) -> impl Iterator { - self.into_iter().rev() - } - - /// Ensures that self begins with `prefix` and that it has a single `Junction` item following. - /// If so, returns a reference to this `Junction` item. - /// - /// # Example - /// ```rust - /// # use staging_xcm::v2::{Junctions::*, Junction::*}; - /// let mut m = X3(Parachain(2), PalletInstance(3), OnlyChild); - /// assert_eq!(m.match_and_split(&X2(Parachain(2), PalletInstance(3))), Some(&OnlyChild)); - /// assert_eq!(m.match_and_split(&X1(Parachain(2))), None); - /// ``` - pub fn match_and_split(&self, prefix: &Junctions) -> Option<&Junction> { - if prefix.len() + 1 != self.len() || !self.starts_with(prefix) { - return None - } - self.at(prefix.len()) - } - - /// Returns whether `self` begins with or is equal to `prefix`. - /// - /// # Example - /// ```rust - /// # use staging_xcm::v2::{Junctions::*, Junction::*}; - /// let mut j = X3(Parachain(2), PalletInstance(3), OnlyChild); - /// assert!(j.starts_with(&X2(Parachain(2), PalletInstance(3)))); - /// assert!(j.starts_with(&j)); - /// assert!(j.starts_with(&X1(Parachain(2)))); - /// assert!(!j.starts_with(&X1(Parachain(999)))); - /// assert!(!j.starts_with(&X4(Parachain(2), PalletInstance(3), OnlyChild, OnlyChild))); - /// ``` - pub fn starts_with(&self, prefix: &Junctions) -> bool { - if self.len() < prefix.len() { - return false - } - prefix.iter().zip(self.iter()).all(|(l, r)| l == r) - } -} - -impl TryFrom for Junctions { - type Error = (); - fn try_from(x: MultiLocation) -> result::Result { - if x.parents > 0 { - Err(()) - } else { - Ok(x.interior) - } - } -} - -#[cfg(test)] -mod tests { - use super::{Ancestor, AncestorThen, Junctions::*, MultiLocation, Parent, ParentThen}; - use crate::opaque::v2::{Junction::*, NetworkId::*}; - use codec::{Decode, Encode}; - - #[test] - fn inverted_works() { - let ancestry: MultiLocation = (Parachain(1000), PalletInstance(42)).into(); - let target = (Parent, PalletInstance(69)).into(); - let expected = (Parent, PalletInstance(42)).into(); - let inverted = ancestry.inverted(&target).unwrap(); - assert_eq!(inverted, expected); - - let ancestry: MultiLocation = (Parachain(1000), PalletInstance(42), GeneralIndex(1)).into(); - let target = (Parent, Parent, PalletInstance(69), GeneralIndex(2)).into(); - let expected = (Parent, Parent, PalletInstance(42), GeneralIndex(1)).into(); - let inverted = ancestry.inverted(&target).unwrap(); - assert_eq!(inverted, expected); - } - - #[test] - fn simplify_basic_works() { - let mut location: MultiLocation = - (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); - let context = X2(Parachain(1000), PalletInstance(42)); - let expected = GeneralIndex(69).into(); - location.simplify(&context); - assert_eq!(location, expected); - - let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into(); - let context = X1(PalletInstance(42)); - let expected = GeneralIndex(69).into(); - location.simplify(&context); - assert_eq!(location, expected); - - let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into(); - let context = X2(Parachain(1000), PalletInstance(42)); - let expected = GeneralIndex(69).into(); - location.simplify(&context); - assert_eq!(location, expected); - - let mut location: MultiLocation = - (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); - let context = X3(OnlyChild, Parachain(1000), PalletInstance(42)); - let expected = GeneralIndex(69).into(); - location.simplify(&context); - assert_eq!(location, expected); - } - - #[test] - fn simplify_incompatible_location_fails() { - let mut location: MultiLocation = - (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); - let context = X3(Parachain(1000), PalletInstance(42), GeneralIndex(42)); - let expected = - (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); - location.simplify(&context); - assert_eq!(location, expected); - - let mut location: MultiLocation = - (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); - let context = X1(Parachain(1000)); - let expected = - (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); - location.simplify(&context); - assert_eq!(location, expected); - } - - #[test] - fn reanchor_works() { - let mut id: MultiLocation = (Parent, Parachain(1000), GeneralIndex(42)).into(); - let ancestry = Parachain(2000).into(); - let target = (Parent, Parachain(1000)).into(); - let expected = GeneralIndex(42).into(); - id.reanchor(&target, &ancestry).unwrap(); - assert_eq!(id, expected); - } - - #[test] - fn encode_and_decode_works() { - let m = MultiLocation { - parents: 1, - interior: X2(Parachain(42), AccountIndex64 { network: Any, index: 23 }), - }; - let encoded = m.encode(); - assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec()); - let decoded = MultiLocation::decode(&mut &encoded[..]); - assert_eq!(decoded, Ok(m)); - } - - #[test] - fn match_and_split_works() { - let m = MultiLocation { - parents: 1, - interior: X2(Parachain(42), AccountIndex64 { network: Any, index: 23 }), - }; - assert_eq!(m.match_and_split(&MultiLocation { parents: 1, interior: Here }), None); - assert_eq!( - m.match_and_split(&MultiLocation { parents: 1, interior: X1(Parachain(42)) }), - Some(&AccountIndex64 { network: Any, index: 23 }) - ); - assert_eq!(m.match_and_split(&m), None); - } - - #[test] - fn starts_with_works() { - let full: MultiLocation = - (Parent, Parachain(1000), AccountId32 { network: Any, id: [0; 32] }).into(); - let identity: MultiLocation = full.clone(); - let prefix: MultiLocation = (Parent, Parachain(1000)).into(); - let wrong_parachain: MultiLocation = (Parent, Parachain(1001)).into(); - let wrong_account: MultiLocation = - (Parent, Parachain(1000), AccountId32 { network: Any, id: [1; 32] }).into(); - let no_parents: MultiLocation = (Parachain(1000)).into(); - let too_many_parents: MultiLocation = (Parent, Parent, Parachain(1000)).into(); - - assert!(full.starts_with(&identity)); - assert!(full.starts_with(&prefix)); - assert!(!full.starts_with(&wrong_parachain)); - assert!(!full.starts_with(&wrong_account)); - assert!(!full.starts_with(&no_parents)); - assert!(!full.starts_with(&too_many_parents)); - } - - #[test] - fn append_with_works() { - let acc = AccountIndex64 { network: Any, index: 23 }; - let mut m = MultiLocation { parents: 1, interior: X1(Parachain(42)) }; - assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(())); - assert_eq!( - m, - MultiLocation { - parents: 1, - interior: X3(Parachain(42), PalletInstance(3), acc.clone()) - } - ); - - // cannot append to create overly long multilocation - let acc = AccountIndex64 { network: Any, index: 23 }; - let m = MultiLocation { - parents: 254, - interior: X5(Parachain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild), - }; - let suffix = X4(PalletInstance(3), acc.clone(), OnlyChild, OnlyChild); - assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix)); - } - - #[test] - fn prepend_with_works() { - let mut m = MultiLocation { - parents: 1, - interior: X2(Parachain(42), AccountIndex64 { network: Any, index: 23 }), - }; - assert_eq!(m.prepend_with(MultiLocation { parents: 1, interior: X1(OnlyChild) }), Ok(())); - assert_eq!( - m, - MultiLocation { - parents: 1, - interior: X2(Parachain(42), AccountIndex64 { network: Any, index: 23 }) - } - ); - - // cannot prepend to create overly long multilocation - let mut m = MultiLocation { parents: 254, interior: X1(Parachain(42)) }; - let prefix = MultiLocation { parents: 2, interior: Here }; - assert_eq!(m.prepend_with(prefix.clone()), Err(prefix)); - - let prefix = MultiLocation { parents: 1, interior: Here }; - assert_eq!(m.prepend_with(prefix), Ok(())); - assert_eq!(m, MultiLocation { parents: 255, interior: X1(Parachain(42)) }); - } - - #[test] - fn double_ended_ref_iteration_works() { - let m = X3(Parachain(1000), Parachain(3), PalletInstance(5)); - let mut iter = m.iter(); - - let first = iter.next().unwrap(); - assert_eq!(first, &Parachain(1000)); - let third = iter.next_back().unwrap(); - assert_eq!(third, &PalletInstance(5)); - let second = iter.next_back().unwrap(); - assert_eq!(iter.next(), None); - assert_eq!(iter.next_back(), None); - assert_eq!(second, &Parachain(3)); - - let res = Here - .pushed_with(first.clone()) - .unwrap() - .pushed_with(second.clone()) - .unwrap() - .pushed_with(third.clone()) - .unwrap(); - assert_eq!(m, res); - - // make sure there's no funny business with the 0 indexing - let m = Here; - let mut iter = m.iter(); - - assert_eq!(iter.next(), None); - assert_eq!(iter.next_back(), None); - } - - #[test] - fn conversion_from_other_types_works() { - fn takes_multilocation>(_arg: Arg) {} - - takes_multilocation(Parent); - takes_multilocation(Here); - takes_multilocation(X1(Parachain(42))); - takes_multilocation((255, PalletInstance(8))); - takes_multilocation((Ancestor(5), Parachain(1), PalletInstance(3))); - takes_multilocation((Ancestor(2), Here)); - takes_multilocation(AncestorThen( - 3, - X2(Parachain(43), AccountIndex64 { network: Any, index: 155 }), - )); - takes_multilocation((Parent, AccountId32 { network: Any, id: [0; 32] })); - takes_multilocation((Parent, Here)); - takes_multilocation(ParentThen(X1(Parachain(75)))); - takes_multilocation([Parachain(100), PalletInstance(3)]); - } -} diff --git a/polkadot/xcm/src/v2/traits.rs b/polkadot/xcm/src/v2/traits.rs deleted file mode 100644 index 815495b812710adfe3197d7c929f8a1a9e8161d5..0000000000000000000000000000000000000000 --- a/polkadot/xcm/src/v2/traits.rs +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Cross-Consensus Message format data structures. - -use crate::v3::Error as NewError; -use codec::{Decode, Encode}; -use core::result; -use scale_info::TypeInfo; - -use super::*; - -// A simple trait to get the weight of some object. -pub trait GetWeight { - fn weight(&self) -> sp_weights::Weight; -} - -#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum Error { - // Errors that happen due to instructions being executed. These alone are defined in the - // XCM specification. - /// An arithmetic overflow happened. - #[codec(index = 0)] - Overflow, - /// The instruction is intentionally unsupported. - #[codec(index = 1)] - Unimplemented, - /// Origin Register does not contain a value value for a reserve transfer notification. - #[codec(index = 2)] - UntrustedReserveLocation, - /// Origin Register does not contain a value value for a teleport notification. - #[codec(index = 3)] - UntrustedTeleportLocation, - /// `MultiLocation` value too large to descend further. - #[codec(index = 4)] - MultiLocationFull, - /// `MultiLocation` value ascend more parents than known ancestors of local location. - #[codec(index = 5)] - MultiLocationNotInvertible, - /// The Origin Register does not contain a valid value for instruction. - #[codec(index = 6)] - BadOrigin, - /// The location parameter is not a valid value for the instruction. - #[codec(index = 7)] - InvalidLocation, - /// The given asset is not handled. - #[codec(index = 8)] - AssetNotFound, - /// An asset transaction (like withdraw or deposit) failed (typically due to type conversions). - #[codec(index = 9)] - FailedToTransactAsset(#[codec(skip)] &'static str), - /// An asset cannot be withdrawn, potentially due to lack of ownership, availability or rights. - #[codec(index = 10)] - NotWithdrawable, - /// An asset cannot be deposited under the ownership of a particular location. - #[codec(index = 11)] - LocationCannotHold, - /// Attempt to send a message greater than the maximum supported by the transport protocol. - #[codec(index = 12)] - ExceedsMaxMessageSize, - /// The given message cannot be translated into a format supported by the destination. - #[codec(index = 13)] - DestinationUnsupported, - /// Destination is routable, but there is some issue with the transport mechanism. - #[codec(index = 14)] - Transport(#[codec(skip)] &'static str), - /// Destination is known to be unroutable. - #[codec(index = 15)] - Unroutable, - /// Used by `ClaimAsset` when the given claim could not be recognized/found. - #[codec(index = 16)] - UnknownClaim, - /// Used by `Transact` when the functor cannot be decoded. - #[codec(index = 17)] - FailedToDecode, - /// Used by `Transact` to indicate that the given weight limit could be breached by the - /// functor. - #[codec(index = 18)] - MaxWeightInvalid, - /// Used by `BuyExecution` when the Holding Register does not contain payable fees. - #[codec(index = 19)] - NotHoldingFees, - /// Used by `BuyExecution` when the fees declared to purchase weight are insufficient. - #[codec(index = 20)] - TooExpensive, - /// Used by the `Trap` instruction to force an error intentionally. Its code is included. - #[codec(index = 21)] - Trap(u64), - - // Errors that happen prior to instructions being executed. These fall outside of the XCM - // spec. - /// XCM version not able to be handled. - UnhandledXcmVersion, - /// Execution of the XCM would potentially result in a greater weight used than weight limit. - WeightLimitReached(Weight), - /// The XCM did not pass the barrier condition for execution. - /// - /// The barrier condition differs on different chains and in different circumstances, but - /// generally it means that the conditions surrounding the message were not such that the chain - /// considers the message worth spending time executing. Since most chains lift the barrier to - /// execution on appropriate payment, presentation of an NFT voucher, or based on the message - /// origin, it means that none of those were the case. - Barrier, - /// The weight of an XCM message is not computable ahead of execution. - WeightNotComputable, -} - -impl TryFrom for Error { - type Error = (); - fn try_from(new_error: NewError) -> result::Result { - use NewError::*; - Ok(match new_error { - Overflow => Self::Overflow, - Unimplemented => Self::Unimplemented, - UntrustedReserveLocation => Self::UntrustedReserveLocation, - UntrustedTeleportLocation => Self::UntrustedTeleportLocation, - LocationFull => Self::MultiLocationFull, - LocationNotInvertible => Self::MultiLocationNotInvertible, - BadOrigin => Self::BadOrigin, - InvalidLocation => Self::InvalidLocation, - AssetNotFound => Self::AssetNotFound, - FailedToTransactAsset(s) => Self::FailedToTransactAsset(s), - NotWithdrawable => Self::NotWithdrawable, - LocationCannotHold => Self::LocationCannotHold, - ExceedsMaxMessageSize => Self::ExceedsMaxMessageSize, - DestinationUnsupported => Self::DestinationUnsupported, - Transport(s) => Self::Transport(s), - Unroutable => Self::Unroutable, - UnknownClaim => Self::UnknownClaim, - FailedToDecode => Self::FailedToDecode, - MaxWeightInvalid => Self::MaxWeightInvalid, - NotHoldingFees => Self::NotHoldingFees, - TooExpensive => Self::TooExpensive, - Trap(i) => Self::Trap(i), - _ => return Err(()), - }) - } -} - -impl From for Error { - fn from(e: SendError) -> Self { - match e { - SendError::NotApplicable(..) | SendError::Unroutable => Error::Unroutable, - SendError::Transport(s) => Error::Transport(s), - SendError::DestinationUnsupported => Error::DestinationUnsupported, - SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize, - } - } -} - -pub type Result = result::Result<(), Error>; - -/// Outcome of an XCM execution. -#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum Outcome { - /// Execution completed successfully; given weight was used. - Complete(Weight), - /// Execution started, but did not complete successfully due to the given error; given weight - /// was used. - Incomplete(Weight, Error), - /// Execution did not start due to the given error. - Error(Error), -} - -impl Outcome { - pub fn ensure_complete(self) -> Result { - match self { - Outcome::Complete(_) => Ok(()), - Outcome::Incomplete(_, e) => Err(e), - Outcome::Error(e) => Err(e), - } - } - pub fn ensure_execution(self) -> result::Result { - match self { - Outcome::Complete(w) => Ok(w), - Outcome::Incomplete(w, _) => Ok(w), - Outcome::Error(e) => Err(e), - } - } - /// How much weight was used by the XCM execution attempt. - pub fn weight_used(&self) -> Weight { - match self { - Outcome::Complete(w) => *w, - Outcome::Incomplete(w, _) => *w, - Outcome::Error(_) => 0, - } - } -} - -/// Type of XCM message executor. -pub trait ExecuteXcm { - /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The - /// weight limit is a basic hard-limit and the implementation may place further restrictions or - /// requirements on weight and other aspects. - fn execute_xcm( - origin: impl Into, - message: Xcm, - weight_limit: Weight, - ) -> Outcome { - let origin = origin.into(); - log::debug!( - target: "xcm::execute_xcm", - "origin: {:?}, message: {:?}, weight_limit: {:?}", - origin, - message, - weight_limit, - ); - Self::execute_xcm_in_credit(origin, message, weight_limit, 0) - } - - /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. - /// - /// Some amount of `weight_credit` may be provided which, depending on the implementation, may - /// allow execution without associated payment. - fn execute_xcm_in_credit( - origin: impl Into, - message: Xcm, - weight_limit: Weight, - weight_credit: Weight, - ) -> Outcome; -} - -impl ExecuteXcm for () { - fn execute_xcm_in_credit( - _origin: impl Into, - _message: Xcm, - _weight_limit: Weight, - _weight_credit: Weight, - ) -> Outcome { - Outcome::Error(Error::Unimplemented) - } -} - -/// Error result value when attempting to send an XCM message. -#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, scale_info::TypeInfo)] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -pub enum SendError { - /// The message and destination combination was not recognized as being reachable. - /// - /// This is not considered fatal: if there are alternative transport routes available, then - /// they may be attempted. For this reason, the destination and message are contained. - NotApplicable(MultiLocation, Xcm<()>), - /// Destination is routable, but there is some issue with the transport mechanism. This is - /// considered fatal. - /// A human-readable explanation of the specific issue is provided. - Transport(#[codec(skip)] &'static str), - /// Destination is known to be unroutable. This is considered fatal. - Unroutable, - /// The given message cannot be translated into a format that the destination can be expected - /// to interpret. - DestinationUnsupported, - /// Message could not be sent due to its size exceeding the maximum allowed by the transport - /// layer. - ExceedsMaxMessageSize, -} - -/// Result value when attempting to send an XCM message. -pub type SendResult = result::Result<(), SendError>; - -/// Utility for sending an XCM message. -/// -/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each -/// router might return `NotApplicable` to pass the execution to the next sender item. Note that -/// each `NotApplicable` might alter the destination and the XCM message for to the next router. -/// -/// -/// # Example -/// ```rust -/// # use staging_xcm::v2::prelude::*; -/// # use codec::Encode; -/// -/// /// A sender that only passes the message through and does nothing. -/// struct Sender1; -/// impl SendXcm for Sender1 { -/// fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { -/// return Err(SendError::NotApplicable(destination.into(), message)) -/// } -/// } -/// -/// /// A sender that accepts a message that has two junctions, otherwise stops the routing. -/// struct Sender2; -/// impl SendXcm for Sender2 { -/// fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { -/// let destination = destination.into(); -/// if destination.parents == 0 && destination.interior.len() == 2 { -/// Ok(()) -/// } else { -/// Err(SendError::Unroutable) -/// } -/// } -/// } -/// -/// /// A sender that accepts a message from a parent, passing through otherwise. -/// struct Sender3; -/// impl SendXcm for Sender3 { -/// fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { -/// let destination = destination.into(); -/// match destination { -/// MultiLocation { parents: 1, interior: Here } => Ok(()), -/// _ => Err(SendError::NotApplicable(destination, message)), -/// } -/// } -/// } -/// -/// // A call to send via XCM. We don't really care about this. -/// # fn main() { -/// let call: Vec = ().encode(); -/// let message = Xcm(vec![Instruction::Transact { -/// origin_type: OriginKind::Superuser, -/// require_weight_at_most: 0, -/// call: call.into(), -/// }]); -/// -/// assert!( -/// // Sender2 will block this. -/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(Parent, message.clone()) -/// .is_err() -/// ); -/// -/// assert!( -/// // Sender3 will catch this. -/// <(Sender1, Sender3) as SendXcm>::send_xcm(Parent, message.clone()) -/// .is_ok() -/// ); -/// # } -/// ``` -pub trait SendXcm { - /// Send an XCM `message` to a given `destination`. - /// - /// If it is not a destination which can be reached with this type but possibly could by others, - /// then it *MUST* return `NotApplicable`. Any other error will cause the tuple implementation - /// to exit early without trying other type fields. - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl SendXcm for Tuple { - fn send_xcm(destination: impl Into, message: Xcm<()>) -> SendResult { - for_tuples!( #( - // we shadow `destination` and `message` in each expansion for the next one. - let (destination, message) = match Tuple::send_xcm(destination, message) { - Err(SendError::NotApplicable(d, m)) => (d, m), - o @ _ => return o, - }; - )* ); - Err(SendError::NotApplicable(destination.into(), message)) - } -} diff --git a/polkadot/xcm/src/v3/junction.rs b/polkadot/xcm/src/v3/junction.rs index 24348bf2e67213e9396a3120dfab8816695ba497..24e9c16bf699a9f2bdbc6221b55c33760ecf2282 100644 --- a/polkadot/xcm/src/v3/junction.rs +++ b/polkadot/xcm/src/v3/junction.rs @@ -18,10 +18,6 @@ use super::{Junctions, MultiLocation}; use crate::{ - v2::{ - BodyId as OldBodyId, BodyPart as OldBodyPart, Junction as OldJunction, - NetworkId as OldNetworkId, - }, v4::{Junction as NewJunction, NetworkId as NewNetworkId}, VersionedLocation, }; @@ -80,30 +76,6 @@ pub enum NetworkId { PolkadotBulletin, } -impl From for Option { - fn from(old: OldNetworkId) -> Option { - use OldNetworkId::*; - match old { - Any => None, - Named(_) => None, - Polkadot => Some(NetworkId::Polkadot), - Kusama => Some(NetworkId::Kusama), - } - } -} - -impl TryFrom for NetworkId { - type Error = (); - fn try_from(old: OldNetworkId) -> Result { - use OldNetworkId::*; - match old { - Any | Named(_) => Err(()), - Polkadot => Ok(NetworkId::Polkadot), - Kusama => Ok(NetworkId::Kusama), - } - } -} - impl From for Option { fn from(new: NewNetworkId) -> Self { Some(NetworkId::from(new)) @@ -175,32 +147,6 @@ pub enum BodyId { Treasury, } -impl TryFrom for BodyId { - type Error = (); - fn try_from(value: OldBodyId) -> Result { - use OldBodyId::*; - Ok(match value { - Unit => Self::Unit, - Named(n) => - if n.len() == 4 { - let mut r = [0u8; 4]; - r.copy_from_slice(&n[..]); - Self::Moniker(r) - } else { - return Err(()) - }, - Index(n) => Self::Index(n), - Executive => Self::Executive, - Technical => Self::Technical, - Legislative => Self::Legislative, - Judicial => Self::Judicial, - Defense => Self::Defense, - Administration => Self::Administration, - Treasury => Self::Treasury, - }) - } -} - /// A part of a pluralistic body. #[derive( Copy, @@ -262,20 +208,6 @@ impl BodyPart { } } -impl TryFrom for BodyPart { - type Error = (); - fn try_from(value: OldBodyPart) -> Result { - use OldBodyPart::*; - Ok(match value { - Voice => Self::Voice, - Members { count } => Self::Members { count }, - Fraction { nom, denom } => Self::Fraction { nom, denom }, - AtLeastProportion { nom, denom } => Self::AtLeastProportion { nom, denom }, - MoreThanProportion { nom, denom } => Self::MoreThanProportion { nom, denom }, - }) - } -} - /// A single item in a path to describe the relative location of a consensus system. /// /// Each item assumes a pre-existing location as its context and is defined in terms of it. @@ -409,36 +341,6 @@ impl From for Junction { } } -impl TryFrom for Junction { - type Error = (); - fn try_from(value: OldJunction) -> Result { - use OldJunction::*; - Ok(match value { - Parachain(id) => Self::Parachain(id), - AccountId32 { network, id } => Self::AccountId32 { network: network.into(), id }, - AccountIndex64 { network, index } => - Self::AccountIndex64 { network: network.into(), index }, - AccountKey20 { network, key } => Self::AccountKey20 { network: network.into(), key }, - PalletInstance(index) => Self::PalletInstance(index), - GeneralIndex(id) => Self::GeneralIndex(id), - GeneralKey(key) => match key.len() { - len @ 0..=32 => Self::GeneralKey { - length: len as u8, - data: { - let mut data = [0u8; 32]; - data[..len].copy_from_slice(&key[..]); - data - }, - }, - _ => return Err(()), - }, - OnlyChild => Self::OnlyChild, - Plurality { id, part } => - Self::Plurality { id: id.try_into()?, part: part.try_into()? }, - }) - } -} - impl TryFrom for Junction { type Error = (); @@ -496,30 +398,3 @@ impl Junction { } } } - -#[cfg(test)] -mod tests { - use super::*; - use alloc::vec; - - #[test] - fn junction_round_trip_works() { - let j = Junction::GeneralKey { length: 32, data: [1u8; 32] }; - let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap(); - assert_eq!(j, k); - - let j = OldJunction::GeneralKey(vec![1u8; 32].try_into().unwrap()); - let k = OldJunction::try_from(Junction::try_from(j.clone()).unwrap()).unwrap(); - assert_eq!(j, k); - - let j = Junction::from(BoundedVec::try_from(vec![1u8, 2, 3, 4]).unwrap()); - let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap(); - assert_eq!(j, k); - let s: BoundedSlice<_, _> = (&k).try_into().unwrap(); - assert_eq!(s, &[1u8, 2, 3, 4][..]); - - let j = OldJunction::GeneralKey(vec![1u8, 2, 3, 4].try_into().unwrap()); - let k = OldJunction::try_from(Junction::try_from(j.clone()).unwrap()).unwrap(); - assert_eq!(j, k); - } -} diff --git a/polkadot/xcm/src/v3/mod.rs b/polkadot/xcm/src/v3/mod.rs index ff64c98e15b3fa3a99af3ca6f426ed2641da5c6d..b60209a440c620f52746c407691213b664d215b7 100644 --- a/polkadot/xcm/src/v3/mod.rs +++ b/polkadot/xcm/src/v3/mod.rs @@ -16,11 +16,6 @@ //! Version 3 of the Cross-Consensus Message format data structures. -#[allow(deprecated)] -use super::v2::{ - Instruction as OldInstruction, OriginKind as OldOriginKind, Response as OldResponse, - WeightLimit as OldWeightLimit, Xcm as OldXcm, -}; use super::v4::{ Instruction as NewInstruction, PalletInfo as NewPalletInfo, QueryResponseInfo as NewQueryResponseInfo, Response as NewResponse, Xcm as NewXcm, @@ -56,43 +51,6 @@ pub use traits::{ SendError, SendResult, SendXcm, Weight, XcmHash, }; -/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`. -#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] -#[scale_info(replace_segment("staging_xcm", "xcm"))] -#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] -pub enum OriginKind { - /// Origin should just be the native dispatch origin representation for the sender in the - /// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin - /// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a - /// primary/native dispatch origin form. - Native, - - /// Origin should just be the standard account-based origin with the sovereign account of - /// the sender. For Cumulus/Frame chains, this is the `Signed` origin. - SovereignAccount, - - /// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin. - /// This will not usually be an available option. - Superuser, - - /// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be - /// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be - /// the `pallet_xcm::Origin::Xcm` type. - Xcm, -} - -impl From for OriginKind { - fn from(old: OldOriginKind) -> Self { - use OldOriginKind::*; - match old { - Native => Self::Native, - SovereignAccount => Self::SovereignAccount, - Superuser => Self::Superuser, - Xcm => Self::Xcm, - } - } -} - /// This module's XCM version. pub const VERSION: super::Version = 3; @@ -456,14 +414,29 @@ impl From for Option { } } -impl From for WeightLimit { - fn from(x: OldWeightLimit) -> Self { - use OldWeightLimit::*; - match x { - Limited(w) => Self::Limited(Weight::from_parts(w, DEFAULT_PROOF_SIZE)), - Unlimited => Self::Unlimited, - } - } +/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`. +#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] +#[scale_info(replace_segment("staging_xcm", "xcm"))] +#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] +pub enum OriginKind { + /// Origin should just be the native dispatch origin representation for the sender in the + /// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin + /// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a + /// primary/native dispatch origin form. + Native, + + /// Origin should just be the standard account-based origin with the sovereign account of + /// the sender. For Cumulus/Frame chains, this is the `Signed` origin. + SovereignAccount, + + /// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin. + /// This will not usually be an available option. + Superuser, + + /// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be + /// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be + /// the `pallet_xcm::Origin::Xcm` type. + Xcm, } /// Contextual data pertaining to a specific list of XCM instructions. @@ -819,6 +792,7 @@ pub enum Instruction { /// Kind: *Command* /// /// Errors: + #[builder(pays_fees)] BuyExecution { fees: MultiAsset, weight_limit: WeightLimit }, /// Refund any surplus weight previously bought with `BuyExecution`. @@ -1327,31 +1301,6 @@ pub mod opaque { pub type Instruction = super::Instruction<()>; } -// Convert from a v2 response to a v3 response. -impl TryFrom for Response { - type Error = (); - fn try_from(old_response: OldResponse) -> result::Result { - match old_response { - OldResponse::Assets(assets) => Ok(Self::Assets(assets.try_into()?)), - OldResponse::Version(version) => Ok(Self::Version(version)), - OldResponse::ExecutionResult(error) => Ok(Self::ExecutionResult(match error { - Some((i, e)) => Some((i, e.try_into()?)), - None => None, - })), - OldResponse::Null => Ok(Self::Null), - } - } -} - -// Convert from a v2 XCM to a v3 XCM. -#[allow(deprecated)] -impl TryFrom> for Xcm { - type Error = (); - fn try_from(old_xcm: OldXcm) -> result::Result { - Ok(Xcm(old_xcm.0.into_iter().map(TryInto::try_into).collect::>()?)) - } -} - // Convert from a v4 XCM to a v3 XCM. impl TryFrom> for Xcm { type Error = (); @@ -1501,109 +1450,6 @@ impl TryFrom> for Instruction { } } -/// Default value for the proof size weight component when converting from V2. Set at 64 KB. -/// NOTE: Make sure this is removed after we properly account for PoV weights. -const DEFAULT_PROOF_SIZE: u64 = 64 * 1024; - -// Convert from a v2 instruction to a v3 instruction. -impl TryFrom> for Instruction { - type Error = (); - fn try_from(old_instruction: OldInstruction) -> result::Result { - use OldInstruction::*; - Ok(match old_instruction { - WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?), - ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?), - ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?), - QueryResponse { query_id, response, max_weight } => Self::QueryResponse { - query_id, - response: response.try_into()?, - max_weight: Weight::from_parts(max_weight, DEFAULT_PROOF_SIZE), - querier: None, - }, - TransferAsset { assets, beneficiary } => Self::TransferAsset { - assets: assets.try_into()?, - beneficiary: beneficiary.try_into()?, - }, - TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset { - assets: assets.try_into()?, - dest: dest.try_into()?, - xcm: xcm.try_into()?, - }, - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => - Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, - HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient }, - HrmpChannelClosing { initiator, sender, recipient } => - Self::HrmpChannelClosing { initiator, sender, recipient }, - Transact { origin_type, require_weight_at_most, call } => Self::Transact { - origin_kind: origin_type.into(), - require_weight_at_most: Weight::from_parts( - require_weight_at_most, - DEFAULT_PROOF_SIZE, - ), - call: call.into(), - }, - ReportError { query_id, dest, max_response_weight } => { - let response_info = QueryResponseInfo { - destination: dest.try_into()?, - query_id, - max_weight: Weight::from_parts(max_response_weight, DEFAULT_PROOF_SIZE), - }; - Self::ReportError(response_info) - }, - DepositAsset { assets, max_assets, beneficiary } => Self::DepositAsset { - assets: (assets, max_assets).try_into()?, - beneficiary: beneficiary.try_into()?, - }, - DepositReserveAsset { assets, max_assets, dest, xcm } => { - let assets = (assets, max_assets).try_into()?; - Self::DepositReserveAsset { assets, dest: dest.try_into()?, xcm: xcm.try_into()? } - }, - ExchangeAsset { give, receive } => { - let give = give.try_into()?; - let want = receive.try_into()?; - Self::ExchangeAsset { give, want, maximal: true } - }, - InitiateReserveWithdraw { assets, reserve, xcm } => Self::InitiateReserveWithdraw { - assets: assets.try_into()?, - reserve: reserve.try_into()?, - xcm: xcm.try_into()?, - }, - InitiateTeleport { assets, dest, xcm } => Self::InitiateTeleport { - assets: assets.try_into()?, - dest: dest.try_into()?, - xcm: xcm.try_into()?, - }, - QueryHolding { query_id, dest, assets, max_response_weight } => { - let response_info = QueryResponseInfo { - destination: dest.try_into()?, - query_id, - max_weight: Weight::from_parts(max_response_weight, DEFAULT_PROOF_SIZE), - }; - Self::ReportHolding { response_info, assets: assets.try_into()? } - }, - BuyExecution { fees, weight_limit } => - Self::BuyExecution { fees: fees.try_into()?, weight_limit: weight_limit.into() }, - ClearOrigin => Self::ClearOrigin, - DescendOrigin(who) => Self::DescendOrigin(who.try_into()?), - RefundSurplus => Self::RefundSurplus, - SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?), - SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?), - ClearError => Self::ClearError, - ClaimAsset { assets, ticket } => { - let assets = assets.try_into()?; - let ticket = ticket.try_into()?; - Self::ClaimAsset { assets, ticket } - }, - Trap(code) => Self::Trap(code), - SubscribeVersion { query_id, max_response_weight } => Self::SubscribeVersion { - query_id, - max_response_weight: Weight::from_parts(max_response_weight, DEFAULT_PROOF_SIZE), - }, - UnsubscribeVersion => Self::UnsubscribeVersion, - }) - } -} - #[cfg(test)] mod tests { use super::{prelude::*, *}; diff --git a/polkadot/xcm/src/v3/multiasset.rs b/polkadot/xcm/src/v3/multiasset.rs index 56b46b1d921e7673abddf2b4af73c8ffa6a6a357..e8bd3e167f61ded225d2f66a4525c6f3c72eff5b 100644 --- a/polkadot/xcm/src/v3/multiasset.rs +++ b/polkadot/xcm/src/v3/multiasset.rs @@ -27,18 +27,10 @@ //! filtering an XCM holding account. use super::{InteriorMultiLocation, MultiLocation}; -use crate::{ - v2::{ - AssetId as OldAssetId, AssetInstance as OldAssetInstance, Fungibility as OldFungibility, - MultiAsset as OldMultiAsset, MultiAssetFilter as OldMultiAssetFilter, - MultiAssets as OldMultiAssets, WildFungibility as OldWildFungibility, - WildMultiAsset as OldWildMultiAsset, - }, - v4::{ - Asset as NewMultiAsset, AssetFilter as NewMultiAssetFilter, AssetId as NewAssetId, - AssetInstance as NewAssetInstance, Assets as NewMultiAssets, Fungibility as NewFungibility, - WildAsset as NewWildMultiAsset, WildFungibility as NewWildFungibility, - }, +use crate::v4::{ + Asset as NewMultiAsset, AssetFilter as NewMultiAssetFilter, AssetId as NewAssetId, + AssetInstance as NewAssetInstance, Assets as NewMultiAssets, Fungibility as NewFungibility, + WildAsset as NewWildMultiAsset, WildFungibility as NewWildFungibility, }; use alloc::{vec, vec::Vec}; use bounded_collections::{BoundedVec, ConstU32}; @@ -85,22 +77,6 @@ pub enum AssetInstance { Array32([u8; 32]), } -impl TryFrom for AssetInstance { - type Error = (); - fn try_from(value: OldAssetInstance) -> Result { - use OldAssetInstance::*; - Ok(match value { - Undefined => Self::Undefined, - Index(n) => Self::Index(n), - Array4(n) => Self::Array4(n), - Array8(n) => Self::Array8(n), - Array16(n) => Self::Array16(n), - Array32(n) => Self::Array32(n), - Blob(_) => return Err(()), - }) - } -} - impl TryFrom for AssetInstance { type Error = (); fn try_from(value: NewAssetInstance) -> Result { @@ -340,17 +316,6 @@ impl> From for Fungibility { } } -impl TryFrom for Fungibility { - type Error = (); - fn try_from(value: OldFungibility) -> Result { - use OldFungibility::*; - Ok(match value { - Fungible(n) => Self::Fungible(n), - NonFungible(i) => Self::NonFungible(i.try_into()?), - }) - } -} - impl TryFrom for Fungibility { type Error = (); fn try_from(value: NewFungibility) -> Result { @@ -387,17 +352,6 @@ pub enum WildFungibility { NonFungible, } -impl TryFrom for WildFungibility { - type Error = (); - fn try_from(value: OldWildFungibility) -> Result { - use OldWildFungibility::*; - Ok(match value { - Fungible => Self::Fungible, - NonFungible => Self::NonFungible, - }) - } -} - impl TryFrom for WildFungibility { type Error = (); fn try_from(value: NewWildFungibility) -> Result { @@ -447,22 +401,6 @@ impl From<[u8; 32]> for AssetId { } } -impl TryFrom for AssetId { - type Error = (); - fn try_from(old: OldAssetId) -> Result { - use OldAssetId::*; - Ok(match old { - Concrete(l) => Self::Concrete(l.try_into()?), - Abstract(v) if v.len() <= 32 => { - let mut r = [0u8; 32]; - r[..v.len()].copy_from_slice(&v[..]); - Self::Abstract(r) - }, - _ => return Err(()), - }) - } -} - impl TryFrom for AssetId { type Error = (); fn try_from(new: NewAssetId) -> Result { @@ -601,13 +539,6 @@ impl MultiAsset { } } -impl TryFrom for MultiAsset { - type Error = (); - fn try_from(old: OldMultiAsset) -> Result { - Ok(Self { id: old.id.try_into()?, fun: old.fun.try_into()? }) - } -} - impl TryFrom for MultiAsset { type Error = (); fn try_from(new: NewMultiAsset) -> Result { @@ -657,18 +588,6 @@ impl Decode for MultiAssets { } } -impl TryFrom for MultiAssets { - type Error = (); - fn try_from(old: OldMultiAssets) -> Result { - let v = old - .drain() - .into_iter() - .map(MultiAsset::try_from) - .collect::, ()>>()?; - Ok(MultiAssets(v)) - } -} - impl TryFrom for MultiAssets { type Error = (); fn try_from(new: NewMultiAssets) -> Result { @@ -882,17 +801,6 @@ pub enum WildMultiAsset { }, } -impl TryFrom for WildMultiAsset { - type Error = (); - fn try_from(old: OldWildMultiAsset) -> Result { - use OldWildMultiAsset::*; - Ok(match old { - AllOf { id, fun } => Self::AllOf { id: id.try_into()?, fun: fun.try_into()? }, - All => Self::All, - }) - } -} - impl TryFrom for WildMultiAsset { type Error = (); fn try_from(new: NewWildMultiAsset) -> Result { @@ -907,19 +815,6 @@ impl TryFrom for WildMultiAsset { } } -impl TryFrom<(OldWildMultiAsset, u32)> for WildMultiAsset { - type Error = (); - fn try_from(old: (OldWildMultiAsset, u32)) -> Result { - use OldWildMultiAsset::*; - let count = old.1; - Ok(match old.0 { - AllOf { id, fun } => - Self::AllOfCounted { id: id.try_into()?, fun: fun.try_into()?, count }, - All => Self::AllCounted(count), - }) - } -} - impl WildMultiAsset { /// Returns true if `self` is a super-set of the given `inner` asset. pub fn contains(&self, inner: &MultiAsset) -> bool { @@ -1079,16 +974,6 @@ impl MultiAssetFilter { } } -impl TryFrom for MultiAssetFilter { - type Error = (); - fn try_from(old: OldMultiAssetFilter) -> Result { - Ok(match old { - OldMultiAssetFilter::Definite(x) => Self::Definite(x.try_into()?), - OldMultiAssetFilter::Wild(x) => Self::Wild(x.try_into()?), - }) - } -} - impl TryFrom for MultiAssetFilter { type Error = (); fn try_from(new: NewMultiAssetFilter) -> Result { @@ -1100,19 +985,6 @@ impl TryFrom for MultiAssetFilter { } } -impl TryFrom<(OldMultiAssetFilter, u32)> for MultiAssetFilter { - type Error = (); - fn try_from(old: (OldMultiAssetFilter, u32)) -> Result { - let count = old.1; - Ok(match old.0 { - OldMultiAssetFilter::Definite(x) if count >= x.len() as u32 => - Self::Definite(x.try_into()?), - OldMultiAssetFilter::Wild(x) => Self::Wild((x, count).try_into()?), - _ => return Err(()), - }) - } -} - #[cfg(test)] mod tests { use super::super::prelude::*; diff --git a/polkadot/xcm/src/v3/multilocation.rs b/polkadot/xcm/src/v3/multilocation.rs index e51981204d96ce709991007c91c6a52e133711fe..8f18312046f8820946bb70154a028d2152a054a2 100644 --- a/polkadot/xcm/src/v3/multilocation.rs +++ b/polkadot/xcm/src/v3/multilocation.rs @@ -17,9 +17,7 @@ //! XCM `MultiLocation` datatype. use super::{Junction, Junctions}; -use crate::{ - v2::MultiLocation as OldMultiLocation, v4::Location as NewMultiLocation, VersionedLocation, -}; +use crate::{v4::Location as NewMultiLocation, VersionedLocation}; use codec::{Decode, Encode, MaxEncodedLen}; use core::result; use scale_info::TypeInfo; @@ -464,13 +462,6 @@ impl MultiLocation { } } -impl TryFrom for MultiLocation { - type Error = (); - fn try_from(x: OldMultiLocation) -> result::Result { - Ok(MultiLocation { parents: x.parents, interior: x.interior.try_into()? }) - } -} - impl TryFrom for Option { type Error = (); fn try_from(new: NewMultiLocation) -> result::Result { @@ -759,37 +750,4 @@ mod tests { let expected = MultiLocation::new(2, (GlobalConsensus(Kusama), Parachain(42))); assert_eq!(para_to_remote_para.chain_location(), expected); } - - #[test] - fn conversion_from_other_types_works() { - use crate::v2; - - fn takes_multilocation>(_arg: Arg) {} - - takes_multilocation(Parent); - takes_multilocation(Here); - takes_multilocation(X1(Parachain(42))); - takes_multilocation((Ancestor(255), PalletInstance(8))); - takes_multilocation((Ancestor(5), Parachain(1), PalletInstance(3))); - takes_multilocation((Ancestor(2), Here)); - takes_multilocation(AncestorThen( - 3, - X2(Parachain(43), AccountIndex64 { network: None, index: 155 }), - )); - takes_multilocation((Parent, AccountId32 { network: None, id: [0; 32] })); - takes_multilocation((Parent, Here)); - takes_multilocation(ParentThen(X1(Parachain(75)))); - takes_multilocation([Parachain(100), PalletInstance(3)]); - - assert_eq!( - v2::MultiLocation::from(v2::Junctions::Here).try_into(), - Ok(MultiLocation::here()) - ); - assert_eq!(v2::MultiLocation::from(v2::Parent).try_into(), Ok(MultiLocation::parent())); - assert_eq!( - v2::MultiLocation::from((v2::Parent, v2::Parent, v2::Junction::GeneralIndex(42u128),)) - .try_into(), - Ok(MultiLocation { parents: 2, interior: X1(GeneralIndex(42u128)) }), - ); - } } diff --git a/polkadot/xcm/src/v3/traits.rs b/polkadot/xcm/src/v3/traits.rs index 34c46453b9a8c0928722bc2abccb97a301a536f2..1c8620708922319f9934dbf5943f77f39164ed09 100644 --- a/polkadot/xcm/src/v3/traits.rs +++ b/polkadot/xcm/src/v3/traits.rs @@ -16,20 +16,19 @@ //! Cross-Consensus Message format data structures. -use crate::v2::Error as OldError; -use codec::{Decode, Encode, MaxEncodedLen}; +use crate::v5::Error as NewError; use core::result; use scale_info::TypeInfo; pub use sp_weights::Weight; -use super::*; - // A simple trait to get the weight of some object. pub trait GetWeight { fn weight(&self) -> sp_weights::Weight; } +use super::*; + /// Error codes used in XCM. The first errors codes have explicit indices and are part of the XCM /// format. Those trailing are merely part of the XCM implementation; there is no expectation that /// they will retain the same index over time. @@ -166,25 +165,17 @@ pub enum Error { ExceedsStackLimit, } -impl MaxEncodedLen for Error { - fn max_encoded_len() -> usize { - // TODO: max_encoded_len doesn't quite work here as it tries to take notice of the fields - // marked `codec(skip)`. We can hard-code it with the right answer for now. - 1 - } -} - -impl TryFrom for Error { +impl TryFrom for Error { type Error = (); - fn try_from(old_error: OldError) -> result::Result { - use OldError::*; - Ok(match old_error { + fn try_from(new_error: NewError) -> result::Result { + use NewError::*; + Ok(match new_error { Overflow => Self::Overflow, Unimplemented => Self::Unimplemented, UntrustedReserveLocation => Self::UntrustedReserveLocation, UntrustedTeleportLocation => Self::UntrustedTeleportLocation, - MultiLocationFull => Self::LocationFull, - MultiLocationNotInvertible => Self::LocationNotInvertible, + LocationFull => Self::LocationFull, + LocationNotInvertible => Self::LocationNotInvertible, BadOrigin => Self::BadOrigin, InvalidLocation => Self::InvalidLocation, AssetNotFound => Self::AssetNotFound, @@ -201,11 +192,32 @@ impl TryFrom for Error { NotHoldingFees => Self::NotHoldingFees, TooExpensive => Self::TooExpensive, Trap(i) => Self::Trap(i), + ExpectationFalse => Self::ExpectationFalse, + PalletNotFound => Self::PalletNotFound, + NameMismatch => Self::NameMismatch, + VersionIncompatible => Self::VersionIncompatible, + HoldingWouldOverflow => Self::HoldingWouldOverflow, + ExportError => Self::ExportError, + ReanchorFailed => Self::ReanchorFailed, + NoDeal => Self::NoDeal, + FeesNotMet => Self::FeesNotMet, + LockError => Self::LockError, + NoPermission => Self::NoPermission, + Unanchored => Self::Unanchored, + NotDepositable => Self::NotDepositable, _ => return Err(()), }) } } +impl MaxEncodedLen for Error { + fn max_encoded_len() -> usize { + // TODO: max_encoded_len doesn't quite work here as it tries to take notice of the fields + // marked `codec(skip)`. We can hard-code it with the right answer for now. + 1 + } +} + impl From for Error { fn from(e: SendError) -> Self { match e { diff --git a/polkadot/xcm/src/v4/asset.rs b/polkadot/xcm/src/v4/asset.rs index 41f1f82f828cfc822ee721d63623625773c611df..d7a9297d69322899f7f025e1791c4bc6024c6ca5 100644 --- a/polkadot/xcm/src/v4/asset.rs +++ b/polkadot/xcm/src/v4/asset.rs @@ -27,10 +27,17 @@ //! holding account. use super::{InteriorLocation, Location, Reanchorable}; -use crate::v3::{ - AssetId as OldAssetId, AssetInstance as OldAssetInstance, Fungibility as OldFungibility, - MultiAsset as OldAsset, MultiAssetFilter as OldAssetFilter, MultiAssets as OldAssets, - WildFungibility as OldWildFungibility, WildMultiAsset as OldWildAsset, +use crate::{ + v3::{ + AssetId as OldAssetId, AssetInstance as OldAssetInstance, Fungibility as OldFungibility, + MultiAsset as OldAsset, MultiAssetFilter as OldAssetFilter, MultiAssets as OldAssets, + WildFungibility as OldWildFungibility, WildMultiAsset as OldWildAsset, + }, + v5::{ + Asset as NewAsset, AssetFilter as NewAssetFilter, AssetId as NewAssetId, + AssetInstance as NewAssetInstance, Assets as NewAssets, Fungibility as NewFungibility, + WildAsset as NewWildAsset, WildFungibility as NewWildFungibility, + }, }; use alloc::{vec, vec::Vec}; use bounded_collections::{BoundedVec, ConstU32}; @@ -90,6 +97,21 @@ impl TryFrom for AssetInstance { } } +impl TryFrom for AssetInstance { + type Error = (); + fn try_from(value: NewAssetInstance) -> Result { + use NewAssetInstance::*; + Ok(match value { + Undefined => Self::Undefined, + Index(n) => Self::Index(n), + Array4(n) => Self::Array4(n), + Array8(n) => Self::Array8(n), + Array16(n) => Self::Array16(n), + Array32(n) => Self::Array32(n), + }) + } +} + impl From<()> for AssetInstance { fn from(_: ()) -> Self { Self::Undefined @@ -244,6 +266,17 @@ impl TryFrom for u128 { } } +impl TryFrom for Fungibility { + type Error = (); + fn try_from(value: NewFungibility) -> Result { + use NewFungibility::*; + Ok(match value { + Fungible(n) => Self::Fungible(n), + NonFungible(i) => Self::NonFungible(i.try_into()?), + }) + } +} + /// Classification of whether an asset is fungible or not, along with a mandatory amount or /// instance. #[derive( @@ -357,6 +390,17 @@ impl TryFrom for WildFungibility { } } +impl TryFrom for WildFungibility { + type Error = (); + fn try_from(value: NewWildFungibility) -> Result { + use NewWildFungibility::*; + Ok(match value { + Fungible => Self::Fungible, + NonFungible => Self::NonFungible, + }) + } +} + /// Location to identify an asset. #[derive( Clone, @@ -391,6 +435,13 @@ impl TryFrom for AssetId { } } +impl TryFrom for AssetId { + type Error = (); + fn try_from(new: NewAssetId) -> Result { + Ok(Self(new.0.try_into()?)) + } +} + impl AssetId { /// Prepend a `Location` to an asset id, giving it a new root location. pub fn prepend_with(&mut self, prepend: &Location) -> Result<(), ()> { @@ -526,6 +577,13 @@ impl TryFrom for Asset { } } +impl TryFrom for Asset { + type Error = (); + fn try_from(new: NewAsset) -> Result { + Ok(Self { id: new.id.try_into()?, fun: new.fun.try_into()? }) + } +} + /// A `Vec` of `Asset`s. /// /// There are a number of invariants which the construction and mutation functions must ensure are @@ -579,6 +637,18 @@ impl TryFrom for Assets { } } +impl TryFrom for Assets { + type Error = (); + fn try_from(new: NewAssets) -> Result { + let v = new + .into_inner() + .into_iter() + .map(Asset::try_from) + .collect::, ()>>()?; + Ok(Assets(v)) + } +} + impl From> for Assets { fn from(mut assets: Vec) -> Self { let mut res = Vec::with_capacity(assets.len()); @@ -795,6 +865,20 @@ impl TryFrom for WildAsset { } } +impl TryFrom for WildAsset { + type Error = (); + fn try_from(new: NewWildAsset) -> Result { + use NewWildAsset::*; + Ok(match new { + AllOf { id, fun } => Self::AllOf { id: id.try_into()?, fun: fun.try_into()? }, + AllOfCounted { id, fun, count } => + Self::AllOfCounted { id: id.try_into()?, fun: fun.try_into()?, count }, + All => Self::All, + AllCounted(count) => Self::AllCounted(count), + }) + } +} + impl WildAsset { /// Returns true if `self` is a super-set of the given `inner` asset. pub fn contains(&self, inner: &Asset) -> bool { @@ -944,6 +1028,17 @@ impl AssetFilter { } } +impl TryFrom for AssetFilter { + type Error = (); + fn try_from(new: NewAssetFilter) -> Result { + use NewAssetFilter::*; + Ok(match new { + Definite(x) => Self::Definite(x.try_into()?), + Wild(x) => Self::Wild(x.try_into()?), + }) + } +} + impl TryFrom for AssetFilter { type Error = (); fn try_from(old: OldAssetFilter) -> Result { diff --git a/polkadot/xcm/src/v4/junction.rs b/polkadot/xcm/src/v4/junction.rs index 36fb616d2dc549a05c71531e0ac63cf63c7d78d0..c6e83214328e92f2762e028a62a654cd008080c9 100644 --- a/polkadot/xcm/src/v4/junction.rs +++ b/polkadot/xcm/src/v4/junction.rs @@ -20,6 +20,7 @@ use super::Location; pub use crate::v3::{BodyId, BodyPart}; use crate::{ v3::{Junction as OldJunction, NetworkId as OldNetworkId}, + v5::{Junction as NewJunction, NetworkId as NewNetworkId}, VersionedLocation, }; use bounded_collections::{BoundedSlice, BoundedVec, ConstU32}; @@ -72,7 +73,6 @@ pub enum Junction { /// An instanced, indexed pallet that forms a constituent part of the context. /// /// Generally used when the context is a Frame-based chain. - // TODO XCMv4 inner should be `Compact`. PalletInstance(u8), /// A non-descript index within the context location. /// @@ -103,6 +103,28 @@ pub enum Junction { GlobalConsensus(NetworkId), } +impl From for Option { + fn from(new: NewNetworkId) -> Self { + Some(NetworkId::from(new)) + } +} + +impl From for NetworkId { + fn from(new: NewNetworkId) -> Self { + use NewNetworkId::*; + match new { + ByGenesis(hash) => Self::ByGenesis(hash), + ByFork { block_number, block_hash } => Self::ByFork { block_number, block_hash }, + Polkadot => Self::Polkadot, + Kusama => Self::Kusama, + Ethereum { chain_id } => Self::Ethereum { chain_id }, + BitcoinCore => Self::BitcoinCore, + BitcoinCash => Self::BitcoinCash, + PolkadotBulletin => Self::PolkadotBulletin, + } + } +} + /// A global identifier of a data structure existing within consensus. /// /// Maintenance note: Networks with global consensus and which are practically bridgeable within the @@ -253,6 +275,29 @@ impl TryFrom for Junction { } } +impl TryFrom for Junction { + type Error = (); + + fn try_from(value: NewJunction) -> Result { + use NewJunction::*; + Ok(match value { + Parachain(id) => Self::Parachain(id), + AccountId32 { network: maybe_network, id } => + Self::AccountId32 { network: maybe_network.map(|network| network.into()), id }, + AccountIndex64 { network: maybe_network, index } => + Self::AccountIndex64 { network: maybe_network.map(|network| network.into()), index }, + AccountKey20 { network: maybe_network, key } => + Self::AccountKey20 { network: maybe_network.map(|network| network.into()), key }, + PalletInstance(index) => Self::PalletInstance(index), + GeneralIndex(id) => Self::GeneralIndex(id), + GeneralKey { length, data } => Self::GeneralKey { length, data }, + OnlyChild => Self::OnlyChild, + Plurality { id, part } => Self::Plurality { id, part }, + GlobalConsensus(network) => Self::GlobalConsensus(network.into()), + }) + } +} + impl Junction { /// Convert `self` into a `Location` containing 0 parents. /// diff --git a/polkadot/xcm/src/v4/location.rs b/polkadot/xcm/src/v4/location.rs index f2c302495c73d2ebb96c53774c6cd98387242504..3a44b0696be41687a2a0d04a66e39c265501b391 100644 --- a/polkadot/xcm/src/v4/location.rs +++ b/polkadot/xcm/src/v4/location.rs @@ -17,7 +17,7 @@ //! XCM `Location` datatype. use super::{traits::Reanchorable, Junction, Junctions}; -use crate::{v3::MultiLocation as OldLocation, VersionedLocation}; +use crate::{v3::MultiLocation as OldLocation, v5::Location as NewLocation, VersionedLocation}; use codec::{Decode, Encode, MaxEncodedLen}; use core::result; use scale_info::TypeInfo; @@ -489,6 +489,20 @@ impl TryFrom for Location { } } +impl TryFrom for Option { + type Error = (); + fn try_from(new: NewLocation) -> result::Result { + Ok(Some(Location::try_from(new)?)) + } +} + +impl TryFrom for Location { + type Error = (); + fn try_from(new: NewLocation) -> result::Result { + Ok(Location { parents: new.parent_count(), interior: new.interior().clone().try_into()? }) + } +} + /// A unit struct which can be converted into a `Location` of `parents` value 1. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct Parent; diff --git a/polkadot/xcm/src/v4/mod.rs b/polkadot/xcm/src/v4/mod.rs index a2b12dcc54cee3882f4dc7b28182b1da8d8163f1..545b75a99ff328848769086ffe1ab8345594e16e 100644 --- a/polkadot/xcm/src/v4/mod.rs +++ b/polkadot/xcm/src/v4/mod.rs @@ -17,9 +17,15 @@ //! Version 4 of the Cross-Consensus Message format data structures. pub use super::v3::GetWeight; -use super::v3::{ - Instruction as OldInstruction, PalletInfo as OldPalletInfo, - QueryResponseInfo as OldQueryResponseInfo, Response as OldResponse, Xcm as OldXcm, +use super::{ + v3::{ + Instruction as OldInstruction, PalletInfo as OldPalletInfo, + QueryResponseInfo as OldQueryResponseInfo, Response as OldResponse, Xcm as OldXcm, + }, + v5::{ + Instruction as NewInstruction, PalletInfo as NewPalletInfo, + QueryResponseInfo as NewQueryResponseInfo, Response as NewResponse, Xcm as NewXcm, + }, }; use crate::DoubleEncoded; use alloc::{vec, vec::Vec}; @@ -30,6 +36,7 @@ use codec::{ }; use core::{fmt::Debug, result}; use derivative::Derivative; +use frame_support::dispatch::GetDispatchInfo; use scale_info::TypeInfo; mod asset; @@ -50,7 +57,7 @@ pub use traits::{ SendError, SendResult, SendXcm, Weight, XcmHash, }; // These parts of XCM v3 are unchanged in XCM v4, and are re-imported here. -pub use super::v3::{MaybeErrorCode, OriginKind, WeightLimit}; +pub use super::v3::{MaxDispatchErrorLen, MaybeErrorCode, OriginKind, WeightLimit}; /// This module's XCM version. pub const VERSION: super::Version = 4; @@ -222,9 +229,6 @@ pub mod prelude { parameter_types! { pub MaxPalletNameLen: u32 = 48; - /// Maximum size of the encoded error code coming from a `Dispatch` result, used for - /// `MaybeErrorCode`. This is not (yet) enforced, so it's just an indication of expectation. - pub MaxDispatchErrorLen: u32 = 128; pub MaxPalletsInfo: u32 = 64; } @@ -258,6 +262,22 @@ impl TryInto for PalletInfo { } } +impl TryInto for PalletInfo { + type Error = (); + + fn try_into(self) -> result::Result { + NewPalletInfo::new( + self.index, + self.name.into_inner(), + self.module_name.into_inner(), + self.major, + self.minor, + self.patch, + ) + .map_err(|_| ()) + } +} + impl PalletInfo { pub fn new( index: u32, @@ -322,6 +342,36 @@ impl TryFrom for Response { } } +impl TryFrom for Response { + type Error = (); + + fn try_from(new: NewResponse) -> result::Result { + use NewResponse::*; + Ok(match new { + Null => Self::Null, + Assets(assets) => Self::Assets(assets.try_into()?), + ExecutionResult(result) => Self::ExecutionResult( + result + .map(|(num, new_error)| (num, new_error.try_into())) + .map(|(num, result)| result.map(|inner| (num, inner))) + .transpose()?, + ), + Version(version) => Self::Version(version), + PalletsInfo(pallet_info) => { + let inner = pallet_info + .into_iter() + .map(TryInto::try_into) + .collect::, _>>()?; + Self::PalletsInfo( + BoundedVec::::try_from(inner).map_err(|_| ())?, + ) + }, + DispatchResult(maybe_error) => + Self::DispatchResult(maybe_error.try_into().map_err(|_| ())?), + }) + } +} + /// Information regarding the composition of a query response. #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] pub struct QueryResponseInfo { @@ -334,6 +384,18 @@ pub struct QueryResponseInfo { pub max_weight: Weight, } +impl TryFrom for QueryResponseInfo { + type Error = (); + + fn try_from(new: NewQueryResponseInfo) -> result::Result { + Ok(Self { + destination: new.destination.try_into()?, + query_id: new.query_id, + max_weight: new.max_weight, + }) + } +} + impl TryFrom for QueryResponseInfo { type Error = (); @@ -690,6 +752,7 @@ pub enum Instruction { /// Kind: *Command* /// /// Errors: + #[builder(pays_fees)] BuyExecution { fees: Asset, weight_limit: WeightLimit }, /// Refund any surplus weight previously bought with `BuyExecution`. @@ -1206,6 +1269,166 @@ impl TryFrom> for Xcm { } } +// Convert from a v5 XCM to a v4 XCM. +impl TryFrom> for Xcm { + type Error = (); + fn try_from(new_xcm: NewXcm) -> result::Result { + Ok(Xcm(new_xcm.0.into_iter().map(TryInto::try_into).collect::>()?)) + } +} + +// Convert from a v5 instruction to a v4 instruction. +impl TryFrom> for Instruction { + type Error = (); + fn try_from(new_instruction: NewInstruction) -> result::Result { + use NewInstruction::*; + Ok(match new_instruction { + WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?), + ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?), + ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?), + QueryResponse { query_id, response, max_weight, querier: Some(querier) } => + Self::QueryResponse { + query_id, + querier: querier.try_into()?, + response: response.try_into()?, + max_weight, + }, + QueryResponse { query_id, response, max_weight, querier: None } => + Self::QueryResponse { + query_id, + querier: None, + response: response.try_into()?, + max_weight, + }, + TransferAsset { assets, beneficiary } => Self::TransferAsset { + assets: assets.try_into()?, + beneficiary: beneficiary.try_into()?, + }, + TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset { + assets: assets.try_into()?, + dest: dest.try_into()?, + xcm: xcm.try_into()?, + }, + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, + HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient }, + HrmpChannelClosing { initiator, sender, recipient } => + Self::HrmpChannelClosing { initiator, sender, recipient }, + Transact { origin_kind, mut call } => { + let require_weight_at_most = call.take_decoded()?.get_dispatch_info().call_weight; + Self::Transact { origin_kind, require_weight_at_most, call: call.into() } + }, + ReportError(response_info) => Self::ReportError(QueryResponseInfo { + query_id: response_info.query_id, + destination: response_info.destination.try_into().map_err(|_| ())?, + max_weight: response_info.max_weight, + }), + DepositAsset { assets, beneficiary } => { + let beneficiary = beneficiary.try_into()?; + let assets = assets.try_into()?; + Self::DepositAsset { assets, beneficiary } + }, + DepositReserveAsset { assets, dest, xcm } => { + let dest = dest.try_into()?; + let xcm = xcm.try_into()?; + let assets = assets.try_into()?; + Self::DepositReserveAsset { assets, dest, xcm } + }, + ExchangeAsset { give, want, maximal } => { + let give = give.try_into()?; + let want = want.try_into()?; + Self::ExchangeAsset { give, want, maximal } + }, + InitiateReserveWithdraw { assets, reserve, xcm } => { + // No `max_assets` here, so if there's a connt, then we cannot translate. + let assets = assets.try_into()?; + let reserve = reserve.try_into()?; + let xcm = xcm.try_into()?; + Self::InitiateReserveWithdraw { assets, reserve, xcm } + }, + InitiateTeleport { assets, dest, xcm } => { + // No `max_assets` here, so if there's a connt, then we cannot translate. + let assets = assets.try_into()?; + let dest = dest.try_into()?; + let xcm = xcm.try_into()?; + Self::InitiateTeleport { assets, dest, xcm } + }, + ReportHolding { response_info, assets } => { + let response_info = QueryResponseInfo { + destination: response_info.destination.try_into().map_err(|_| ())?, + query_id: response_info.query_id, + max_weight: response_info.max_weight, + }; + Self::ReportHolding { response_info, assets: assets.try_into()? } + }, + BuyExecution { fees, weight_limit } => { + let fees = fees.try_into()?; + let weight_limit = weight_limit.into(); + Self::BuyExecution { fees, weight_limit } + }, + ClearOrigin => Self::ClearOrigin, + DescendOrigin(who) => Self::DescendOrigin(who.try_into()?), + RefundSurplus => Self::RefundSurplus, + SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?), + SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?), + ClearError => Self::ClearError, + ClaimAsset { assets, ticket } => { + let assets = assets.try_into()?; + let ticket = ticket.try_into()?; + Self::ClaimAsset { assets, ticket } + }, + Trap(code) => Self::Trap(code), + SubscribeVersion { query_id, max_response_weight } => + Self::SubscribeVersion { query_id, max_response_weight }, + UnsubscribeVersion => Self::UnsubscribeVersion, + BurnAsset(assets) => Self::BurnAsset(assets.try_into()?), + ExpectAsset(assets) => Self::ExpectAsset(assets.try_into()?), + ExpectOrigin(maybe_origin) => + Self::ExpectOrigin(maybe_origin.map(|origin| origin.try_into()).transpose()?), + ExpectError(maybe_error) => Self::ExpectError( + maybe_error + .map(|(num, new_error)| (num, new_error.try_into())) + .map(|(num, result)| result.map(|inner| (num, inner))) + .transpose()?, + ), + ExpectTransactStatus(maybe_error_code) => Self::ExpectTransactStatus(maybe_error_code), + QueryPallet { module_name, response_info } => + Self::QueryPallet { module_name, response_info: response_info.try_into()? }, + ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => + Self::ExpectPallet { index, name, module_name, crate_major, min_crate_minor }, + ReportTransactStatus(response_info) => + Self::ReportTransactStatus(response_info.try_into()?), + ClearTransactStatus => Self::ClearTransactStatus, + UniversalOrigin(junction) => Self::UniversalOrigin(junction.try_into()?), + ExportMessage { network, destination, xcm } => Self::ExportMessage { + network: network.into(), + destination: destination.try_into()?, + xcm: xcm.try_into()?, + }, + LockAsset { asset, unlocker } => + Self::LockAsset { asset: asset.try_into()?, unlocker: unlocker.try_into()? }, + UnlockAsset { asset, target } => + Self::UnlockAsset { asset: asset.try_into()?, target: target.try_into()? }, + NoteUnlockable { asset, owner } => + Self::NoteUnlockable { asset: asset.try_into()?, owner: owner.try_into()? }, + RequestUnlock { asset, locker } => + Self::RequestUnlock { asset: asset.try_into()?, locker: locker.try_into()? }, + SetFeesMode { jit_withdraw } => Self::SetFeesMode { jit_withdraw }, + SetTopic(topic) => Self::SetTopic(topic), + ClearTopic => Self::ClearTopic, + AliasOrigin(location) => Self::AliasOrigin(location.try_into()?), + UnpaidExecution { weight_limit, check_origin } => Self::UnpaidExecution { + weight_limit, + check_origin: check_origin.map(|origin| origin.try_into()).transpose()?, + }, + InitiateTransfer { .. } | PayFees { .. } | SetAssetClaimer { .. } => { + log::debug!(target: "xcm::v5tov4", "`{new_instruction:?}` not supported by v4"); + return Err(()); + }, + }) + } +} + // Convert from a v3 instruction to a v4 instruction impl TryFrom> for Instruction { type Error = (); diff --git a/polkadot/xcm/src/v5/asset.rs b/polkadot/xcm/src/v5/asset.rs new file mode 100644 index 0000000000000000000000000000000000000000..d0d9a7cedff0592bf55306af25eb363255236a08 --- /dev/null +++ b/polkadot/xcm/src/v5/asset.rs @@ -0,0 +1,1155 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Cross-Consensus Message format asset data structures. +//! +//! This encompasses four types for representing assets: +//! - `Asset`: A description of a single asset, either an instance of a non-fungible or some amount +//! of a fungible. +//! - `Assets`: A collection of `Asset`s. These are stored in a `Vec` and sorted with fungibles +//! first. +//! - `Wild`: A single asset wildcard, this can either be "all" assets, or all assets of a specific +//! kind. +//! - `AssetFilter`: A combination of `Wild` and `Assets` designed for efficiently filtering an XCM +//! holding account. + +use super::{InteriorLocation, Location, Reanchorable}; +use crate::v4::{ + Asset as OldAsset, AssetFilter as OldAssetFilter, AssetId as OldAssetId, + AssetInstance as OldAssetInstance, Assets as OldAssets, Fungibility as OldFungibility, + WildAsset as OldWildAsset, WildFungibility as OldWildFungibility, +}; +use alloc::{vec, vec::Vec}; +use bounded_collections::{BoundedVec, ConstU32}; +use codec::{self as codec, Decode, Encode, MaxEncodedLen}; +use core::cmp::Ordering; +use scale_info::TypeInfo; + +/// A general identifier for an instance of a non-fungible asset class. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub enum AssetInstance { + /// Undefined - used if the non-fungible asset class has only one instance. + Undefined, + + /// A compact index. Technically this could be greater than `u128`, but this implementation + /// supports only values up to `2**128 - 1`. + Index(#[codec(compact)] u128), + + /// A 4-byte fixed-length datum. + Array4([u8; 4]), + + /// An 8-byte fixed-length datum. + Array8([u8; 8]), + + /// A 16-byte fixed-length datum. + Array16([u8; 16]), + + /// A 32-byte fixed-length datum. + Array32([u8; 32]), +} + +impl TryFrom for AssetInstance { + type Error = (); + fn try_from(value: OldAssetInstance) -> Result { + use OldAssetInstance::*; + Ok(match value { + Undefined => Self::Undefined, + Index(n) => Self::Index(n), + Array4(n) => Self::Array4(n), + Array8(n) => Self::Array8(n), + Array16(n) => Self::Array16(n), + Array32(n) => Self::Array32(n), + }) + } +} + +impl From<()> for AssetInstance { + fn from(_: ()) -> Self { + Self::Undefined + } +} + +impl From<[u8; 4]> for AssetInstance { + fn from(x: [u8; 4]) -> Self { + Self::Array4(x) + } +} + +impl From<[u8; 8]> for AssetInstance { + fn from(x: [u8; 8]) -> Self { + Self::Array8(x) + } +} + +impl From<[u8; 16]> for AssetInstance { + fn from(x: [u8; 16]) -> Self { + Self::Array16(x) + } +} + +impl From<[u8; 32]> for AssetInstance { + fn from(x: [u8; 32]) -> Self { + Self::Array32(x) + } +} + +impl From for AssetInstance { + fn from(x: u8) -> Self { + Self::Index(x as u128) + } +} + +impl From for AssetInstance { + fn from(x: u16) -> Self { + Self::Index(x as u128) + } +} + +impl From for AssetInstance { + fn from(x: u32) -> Self { + Self::Index(x as u128) + } +} + +impl From for AssetInstance { + fn from(x: u64) -> Self { + Self::Index(x as u128) + } +} + +impl TryFrom for () { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Undefined => Ok(()), + _ => Err(()), + } + } +} + +impl TryFrom for [u8; 4] { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Array4(x) => Ok(x), + _ => Err(()), + } + } +} + +impl TryFrom for [u8; 8] { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Array8(x) => Ok(x), + _ => Err(()), + } + } +} + +impl TryFrom for [u8; 16] { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Array16(x) => Ok(x), + _ => Err(()), + } + } +} + +impl TryFrom for [u8; 32] { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Array32(x) => Ok(x), + _ => Err(()), + } + } +} + +impl TryFrom for u8 { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Index(x) => x.try_into().map_err(|_| ()), + _ => Err(()), + } + } +} + +impl TryFrom for u16 { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Index(x) => x.try_into().map_err(|_| ()), + _ => Err(()), + } + } +} + +impl TryFrom for u32 { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Index(x) => x.try_into().map_err(|_| ()), + _ => Err(()), + } + } +} + +impl TryFrom for u64 { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Index(x) => x.try_into().map_err(|_| ()), + _ => Err(()), + } + } +} + +impl TryFrom for u128 { + type Error = (); + fn try_from(x: AssetInstance) -> Result { + match x { + AssetInstance::Index(x) => Ok(x), + _ => Err(()), + } + } +} + +/// Classification of whether an asset is fungible or not, along with a mandatory amount or +/// instance. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub enum Fungibility { + /// A fungible asset; we record a number of units, as a `u128` in the inner item. + Fungible(#[codec(compact)] u128), + /// A non-fungible asset. We record the instance identifier in the inner item. Only one asset + /// of each instance identifier may ever be in existence at once. + NonFungible(AssetInstance), +} + +#[derive(Decode)] +enum UncheckedFungibility { + Fungible(#[codec(compact)] u128), + NonFungible(AssetInstance), +} + +impl Decode for Fungibility { + fn decode(input: &mut I) -> Result { + match UncheckedFungibility::decode(input)? { + UncheckedFungibility::Fungible(a) if a != 0 => Ok(Self::Fungible(a)), + UncheckedFungibility::NonFungible(i) => Ok(Self::NonFungible(i)), + UncheckedFungibility::Fungible(_) => + Err("Fungible asset of zero amount is not allowed".into()), + } + } +} + +impl Fungibility { + pub fn is_kind(&self, w: WildFungibility) -> bool { + use Fungibility::*; + use WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible}; + matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible)) + } +} + +impl From for Fungibility { + fn from(amount: i32) -> Fungibility { + debug_assert_ne!(amount, 0); + Fungibility::Fungible(amount as u128) + } +} + +impl From for Fungibility { + fn from(amount: u128) -> Fungibility { + debug_assert_ne!(amount, 0); + Fungibility::Fungible(amount) + } +} + +impl> From for Fungibility { + fn from(instance: T) -> Fungibility { + Fungibility::NonFungible(instance.into()) + } +} + +impl TryFrom for Fungibility { + type Error = (); + fn try_from(value: OldFungibility) -> Result { + use OldFungibility::*; + Ok(match value { + Fungible(n) => Self::Fungible(n), + NonFungible(i) => Self::NonFungible(i.try_into()?), + }) + } +} + +/// Classification of whether an asset is fungible or not. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub enum WildFungibility { + /// The asset is fungible. + Fungible, + /// The asset is not fungible. + NonFungible, +} + +impl TryFrom for WildFungibility { + type Error = (); + fn try_from(value: OldWildFungibility) -> Result { + use OldWildFungibility::*; + Ok(match value { + Fungible => Self::Fungible, + NonFungible => Self::NonFungible, + }) + } +} + +/// Location to identify an asset. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub struct AssetId(pub Location); + +impl> From for AssetId { + fn from(x: T) -> Self { + Self(x.into()) + } +} + +impl TryFrom for AssetId { + type Error = (); + fn try_from(old: OldAssetId) -> Result { + Ok(Self(old.0.try_into()?)) + } +} + +impl AssetId { + /// Prepend a `Location` to an asset id, giving it a new root location. + pub fn prepend_with(&mut self, prepend: &Location) -> Result<(), ()> { + self.0.prepend_with(prepend.clone()).map_err(|_| ())?; + Ok(()) + } + + /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding + /// `Asset` value. + pub fn into_asset(self, fun: Fungibility) -> Asset { + Asset { fun, id: self } + } + + /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding + /// `WildAsset` wildcard (`AllOf`) value. + pub fn into_wild(self, fun: WildFungibility) -> WildAsset { + WildAsset::AllOf { fun, id: self } + } +} + +impl Reanchorable for AssetId { + type Error = (); + + /// Mutate the asset to represent the same value from the perspective of a new `target` + /// location. The local chain's location is provided in `context`. + fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> { + self.0.reanchor(target, context)?; + Ok(()) + } + + fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result { + match self.reanchor(target, context) { + Ok(()) => Ok(self), + Err(()) => Err(()), + } + } +} + +/// Either an amount of a single fungible asset, or a single well-identified non-fungible asset. +#[derive( + Clone, + Eq, + PartialEq, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub struct Asset { + /// The overall asset identity (aka *class*, in the case of a non-fungible). + pub id: AssetId, + /// The fungibility of the asset, which contains either the amount (in the case of a fungible + /// asset) or the *instance ID*, the secondary asset identifier. + pub fun: Fungibility, +} + +impl PartialOrd for Asset { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Asset { + fn cmp(&self, other: &Self) -> Ordering { + match (&self.fun, &other.fun) { + (Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less, + (Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater, + _ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)), + } + } +} + +impl, B: Into> From<(A, B)> for Asset { + fn from((id, fun): (A, B)) -> Asset { + Asset { fun: fun.into(), id: id.into() } + } +} + +impl Asset { + pub fn is_fungible(&self, maybe_id: Option) -> bool { + use Fungibility::*; + matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id) + } + + pub fn is_non_fungible(&self, maybe_id: Option) -> bool { + use Fungibility::*; + matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id) + } + + /// Prepend a `Location` to a concrete asset, giving it a new root location. + pub fn prepend_with(&mut self, prepend: &Location) -> Result<(), ()> { + self.id.prepend_with(prepend) + } + + /// Returns true if `self` is a super-set of the given `inner` asset. + pub fn contains(&self, inner: &Asset) -> bool { + use Fungibility::*; + if self.id == inner.id { + match (&self.fun, &inner.fun) { + (Fungible(a), Fungible(i)) if a >= i => return true, + (NonFungible(a), NonFungible(i)) if a == i => return true, + _ => (), + } + } + false + } +} + +impl Reanchorable for Asset { + type Error = (); + + /// Mutate the location of the asset identifier if concrete, giving it the same location + /// relative to a `target` context. The local context is provided as `context`. + fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> { + self.id.reanchor(target, context) + } + + /// Mutate the location of the asset identifier if concrete, giving it the same location + /// relative to a `target` context. The local context is provided as `context`. + fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result { + self.id.reanchor(target, context)?; + Ok(self) + } +} + +impl TryFrom for Asset { + type Error = (); + fn try_from(old: OldAsset) -> Result { + Ok(Self { id: old.id.try_into()?, fun: old.fun.try_into()? }) + } +} + +/// A `Vec` of `Asset`s. +/// +/// There are a number of invariants which the construction and mutation functions must ensure are +/// maintained: +/// - It may contain no items of duplicate asset class; +/// - All items must be ordered; +/// - The number of items should grow no larger than `MAX_ITEMS_IN_ASSETS`. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + TypeInfo, + Default, + serde::Serialize, + serde::Deserialize, +)] +pub struct Assets(Vec); + +/// Maximum number of items we expect in a single `Assets` value. Note this is not (yet) +/// enforced, and just serves to provide a sensible `max_encoded_len` for `Assets`. +pub const MAX_ITEMS_IN_ASSETS: usize = 20; + +impl MaxEncodedLen for Assets { + fn max_encoded_len() -> usize { + Asset::max_encoded_len() * MAX_ITEMS_IN_ASSETS + } +} + +impl Decode for Assets { + fn decode(input: &mut I) -> Result { + let bounded_instructions = + BoundedVec::>::decode(input)?; + Self::from_sorted_and_deduplicated(bounded_instructions.into_inner()) + .map_err(|()| "Out of order".into()) + } +} + +impl TryFrom for Assets { + type Error = (); + fn try_from(old: OldAssets) -> Result { + let v = old + .into_inner() + .into_iter() + .map(Asset::try_from) + .collect::, ()>>()?; + Ok(Assets(v)) + } +} + +impl From> for Assets { + fn from(mut assets: Vec) -> Self { + let mut res = Vec::with_capacity(assets.len()); + if !assets.is_empty() { + assets.sort(); + let mut iter = assets.into_iter(); + if let Some(first) = iter.next() { + let last = iter.fold(first, |a, b| -> Asset { + match (a, b) { + ( + Asset { fun: Fungibility::Fungible(a_amount), id: a_id }, + Asset { fun: Fungibility::Fungible(b_amount), id: b_id }, + ) if a_id == b_id => Asset { + id: a_id, + fun: Fungibility::Fungible(a_amount.saturating_add(b_amount)), + }, + ( + Asset { fun: Fungibility::NonFungible(a_instance), id: a_id }, + Asset { fun: Fungibility::NonFungible(b_instance), id: b_id }, + ) if a_id == b_id && a_instance == b_instance => + Asset { fun: Fungibility::NonFungible(a_instance), id: a_id }, + (to_push, to_remember) => { + res.push(to_push); + to_remember + }, + } + }); + res.push(last); + } + } + Self(res) + } +} + +impl> From for Assets { + fn from(x: T) -> Self { + Self(vec![x.into()]) + } +} + +impl Assets { + /// A new (empty) value. + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Create a new instance of `Assets` from a `Vec` whose contents are sorted + /// and which contain no duplicates. + /// + /// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates. + /// If you can't guarantee that `r` is sorted and deduplicated, then use + /// `From::>::from` which is infallible. + pub fn from_sorted_and_deduplicated(r: Vec) -> Result { + if r.is_empty() { + return Ok(Self(Vec::new())) + } + r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&Asset, ()> { + if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { + Ok(b) + } else { + Err(()) + } + })?; + Ok(Self(r)) + } + + /// Create a new instance of `Assets` from a `Vec` whose contents are sorted + /// and which contain no duplicates. + /// + /// In release mode, this skips any checks to ensure that `r` is correct, making it a + /// negligible-cost operation. Generally though you should avoid using it unless you have a + /// strict proof that `r` is valid. + #[cfg(test)] + pub fn from_sorted_and_deduplicated_skip_checks(r: Vec) -> Self { + Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped") + } + /// Create a new instance of `Assets` from a `Vec` whose contents are sorted + /// and which contain no duplicates. + /// + /// In release mode, this skips any checks to ensure that `r` is correct, making it a + /// negligible-cost operation. Generally though you should avoid using it unless you have a + /// strict proof that `r` is valid. + /// + /// In test mode, this checks anyway and panics on fail. + #[cfg(not(test))] + pub fn from_sorted_and_deduplicated_skip_checks(r: Vec) -> Self { + Self(r) + } + + /// Add some asset onto the list, saturating. This is quite a laborious operation since it + /// maintains the ordering. + pub fn push(&mut self, a: Asset) { + for asset in self.0.iter_mut().filter(|x| x.id == a.id) { + match (&a.fun, &mut asset.fun) { + (Fungibility::Fungible(amount), Fungibility::Fungible(balance)) => { + *balance = balance.saturating_add(*amount); + return + }, + (Fungibility::NonFungible(inst1), Fungibility::NonFungible(inst2)) + if inst1 == inst2 => + return, + _ => (), + } + } + self.0.push(a); + self.0.sort(); + } + + /// Returns `true` if this definitely represents no asset. + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + + /// Returns true if `self` is a super-set of the given `inner` asset. + pub fn contains(&self, inner: &Asset) -> bool { + self.0.iter().any(|i| i.contains(inner)) + } + + /// Consume `self` and return the inner vec. + #[deprecated = "Use `into_inner()` instead"] + pub fn drain(self) -> Vec { + self.0 + } + + /// Consume `self` and return the inner vec. + pub fn into_inner(self) -> Vec { + self.0 + } + + /// Return a reference to the inner vec. + pub fn inner(&self) -> &Vec { + &self.0 + } + + /// Return the number of distinct asset instances contained. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Prepend a `Location` to any concrete asset items, giving it a new root location. + pub fn prepend_with(&mut self, prefix: &Location) -> Result<(), ()> { + self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))?; + self.0.sort(); + Ok(()) + } + + /// Return a reference to an item at a specific index or `None` if it doesn't exist. + pub fn get(&self, index: usize) -> Option<&Asset> { + self.0.get(index) + } +} + +impl Reanchorable for Assets { + type Error = (); + + fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> { + self.0.iter_mut().try_for_each(|i| i.reanchor(target, context))?; + self.0.sort(); + Ok(()) + } + + fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result { + match self.reanchor(target, context) { + Ok(()) => Ok(self), + Err(()) => Err(()), + } + } +} + +/// A wildcard representing a set of assets. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub enum WildAsset { + /// All assets in Holding. + All, + /// All assets in Holding of a given fungibility and ID. + AllOf { id: AssetId, fun: WildFungibility }, + /// All assets in Holding, up to `u32` individual assets (different instances of non-fungibles + /// are separate assets). + AllCounted(#[codec(compact)] u32), + /// All assets in Holding of a given fungibility and ID up to `count` individual assets + /// (different instances of non-fungibles are separate assets). + AllOfCounted { + id: AssetId, + fun: WildFungibility, + #[codec(compact)] + count: u32, + }, +} + +impl TryFrom for WildAsset { + type Error = (); + fn try_from(old: OldWildAsset) -> Result { + use OldWildAsset::*; + Ok(match old { + AllOf { id, fun } => Self::AllOf { id: id.try_into()?, fun: fun.try_into()? }, + All => Self::All, + AllOfCounted { id, fun, count } => + Self::AllOfCounted { id: id.try_into()?, fun: fun.try_into()?, count }, + AllCounted(count) => Self::AllCounted(count), + }) + } +} + +impl WildAsset { + /// Returns true if `self` is a super-set of the given `inner` asset. + pub fn contains(&self, inner: &Asset) -> bool { + use WildAsset::*; + match self { + AllOfCounted { count: 0, .. } | AllCounted(0) => false, + AllOf { fun, id } | AllOfCounted { id, fun, .. } => + inner.fun.is_kind(*fun) && &inner.id == id, + All | AllCounted(_) => true, + } + } + + /// Returns true if the wild element of `self` matches `inner`. + /// + /// Note that for `Counted` variants of wildcards, then it will disregard the count except for + /// always returning `false` when equal to 0. + #[deprecated = "Use `contains` instead"] + pub fn matches(&self, inner: &Asset) -> bool { + self.contains(inner) + } + + /// Mutate the asset to represent the same value from the perspective of a new `target` + /// location. The local chain's location is provided in `context`. + pub fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> { + use WildAsset::*; + match self { + AllOf { ref mut id, .. } | AllOfCounted { ref mut id, .. } => + id.reanchor(target, context), + All | AllCounted(_) => Ok(()), + } + } + + /// Maximum count of assets allowed to match, if any. + pub fn count(&self) -> Option { + use WildAsset::*; + match self { + AllOfCounted { count, .. } | AllCounted(count) => Some(*count), + All | AllOf { .. } => None, + } + } + + /// Explicit limit on number of assets allowed to match, if any. + pub fn limit(&self) -> Option { + self.count() + } + + /// Consume self and return the equivalent version but counted and with the `count` set to the + /// given parameter. + pub fn counted(self, count: u32) -> Self { + use WildAsset::*; + match self { + AllOfCounted { fun, id, .. } | AllOf { fun, id } => AllOfCounted { fun, id, count }, + All | AllCounted(_) => AllCounted(count), + } + } +} + +impl, B: Into> From<(A, B)> for WildAsset { + fn from((id, fun): (A, B)) -> WildAsset { + WildAsset::AllOf { fun: fun.into(), id: id.into() } + } +} + +/// `Asset` collection, defined either by a number of `Assets` or a single wildcard. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub enum AssetFilter { + /// Specify the filter as being everything contained by the given `Assets` inner. + Definite(Assets), + /// Specify the filter as the given `WildAsset` wildcard. + Wild(WildAsset), +} + +impl> From for AssetFilter { + fn from(x: T) -> Self { + Self::Wild(x.into()) + } +} + +impl From for AssetFilter { + fn from(x: Asset) -> Self { + Self::Definite(vec![x].into()) + } +} + +impl From> for AssetFilter { + fn from(x: Vec) -> Self { + Self::Definite(x.into()) + } +} + +impl From for AssetFilter { + fn from(x: Assets) -> Self { + Self::Definite(x) + } +} + +impl AssetFilter { + /// Returns true if `inner` would be matched by `self`. + /// + /// Note that for `Counted` variants of wildcards, then it will disregard the count except for + /// always returning `false` when equal to 0. + pub fn matches(&self, inner: &Asset) -> bool { + match self { + AssetFilter::Definite(ref assets) => assets.contains(inner), + AssetFilter::Wild(ref wild) => wild.contains(inner), + } + } + + /// Mutate the location of the asset identifier if concrete, giving it the same location + /// relative to a `target` context. The local context is provided as `context`. + pub fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> { + match self { + AssetFilter::Definite(ref mut assets) => assets.reanchor(target, context), + AssetFilter::Wild(ref mut wild) => wild.reanchor(target, context), + } + } + + /// Maximum count of assets it is possible to match, if known. + pub fn count(&self) -> Option { + use AssetFilter::*; + match self { + Definite(x) => Some(x.len() as u32), + Wild(x) => x.count(), + } + } + + /// Explicit limit placed on the number of items, if any. + pub fn limit(&self) -> Option { + use AssetFilter::*; + match self { + Definite(_) => None, + Wild(x) => x.limit(), + } + } +} + +impl TryFrom for AssetFilter { + type Error = (); + fn try_from(old: OldAssetFilter) -> Result { + Ok(match old { + OldAssetFilter::Definite(x) => Self::Definite(x.try_into()?), + OldAssetFilter::Wild(x) => Self::Wild(x.try_into()?), + }) + } +} + +/// Matches assets based on inner `AssetFilter` and tags them for a specific type of asset transfer. +/// Please note: the transfer type is specific to each particular `(asset, source, dest)` +/// combination, so it should always be built in the context of `source` after knowing `dest`. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub enum AssetTransferFilter { + /// teleport assets matching `AssetFilter` to a specific destination + Teleport(AssetFilter), + /// reserve-transfer assets matching `AssetFilter` to a specific destination, using the local + /// chain as reserve + ReserveDeposit(AssetFilter), + /// reserve-transfer assets matching `AssetFilter` to a specific destination, using the + /// destination as reserve + ReserveWithdraw(AssetFilter), +} + +impl AssetTransferFilter { + /// Returns reference to inner `AssetFilter` ignoring the transfer type. + pub fn inner(&self) -> &AssetFilter { + match self { + AssetTransferFilter::Teleport(inner) => inner, + AssetTransferFilter::ReserveDeposit(inner) => inner, + AssetTransferFilter::ReserveWithdraw(inner) => inner, + } + } +} + +#[cfg(test)] +mod tests { + use super::super::prelude::*; + + #[test] + fn conversion_works() { + let _: Assets = (Here, 1u128).into(); + } + + #[test] + fn from_sorted_and_deduplicated_works() { + use super::*; + use alloc::vec; + + let empty = vec![]; + let r = Assets::from_sorted_and_deduplicated(empty); + assert_eq!(r, Ok(Assets(vec![]))); + + let dup_fun = vec![(Here, 100).into(), (Here, 10).into()]; + let r = Assets::from_sorted_and_deduplicated(dup_fun); + assert!(r.is_err()); + + let dup_nft = vec![(Here, *b"notgood!").into(), (Here, *b"notgood!").into()]; + let r = Assets::from_sorted_and_deduplicated(dup_nft); + assert!(r.is_err()); + + let good_fun = vec![(Here, 10).into(), (Parent, 10).into()]; + let r = Assets::from_sorted_and_deduplicated(good_fun.clone()); + assert_eq!(r, Ok(Assets(good_fun))); + + let bad_fun = vec![(Parent, 10).into(), (Here, 10).into()]; + let r = Assets::from_sorted_and_deduplicated(bad_fun); + assert!(r.is_err()); + + let good_nft = vec![(Here, ()).into(), (Here, *b"good").into()]; + let r = Assets::from_sorted_and_deduplicated(good_nft.clone()); + assert_eq!(r, Ok(Assets(good_nft))); + + let bad_nft = vec![(Here, *b"bad!").into(), (Here, ()).into()]; + let r = Assets::from_sorted_and_deduplicated(bad_nft); + assert!(r.is_err()); + + let mixed_good = vec![(Here, 10).into(), (Here, *b"good").into()]; + let r = Assets::from_sorted_and_deduplicated(mixed_good.clone()); + assert_eq!(r, Ok(Assets(mixed_good))); + + let mixed_bad = vec![(Here, *b"bad!").into(), (Here, 10).into()]; + let r = Assets::from_sorted_and_deduplicated(mixed_bad); + assert!(r.is_err()); + } + + #[test] + fn reanchor_preserves_sorting() { + use super::*; + use alloc::vec; + + let reanchor_context: Junctions = Parachain(2000).into(); + let dest = Location::new(1, []); + + let asset_1: Asset = (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into(); + let mut asset_1_reanchored = asset_1.clone(); + assert!(asset_1_reanchored.reanchor(&dest, &reanchor_context).is_ok()); + assert_eq!( + asset_1_reanchored, + (Location::new(0, [Parachain(2000), PalletInstance(50), GeneralIndex(1)]), 10).into() + ); + + let asset_2: Asset = (Location::new(1, []), 10).into(); + let mut asset_2_reanchored = asset_2.clone(); + assert!(asset_2_reanchored.reanchor(&dest, &reanchor_context).is_ok()); + assert_eq!(asset_2_reanchored, (Location::new(0, []), 10).into()); + + let asset_3: Asset = (Location::new(1, [Parachain(1000)]), 10).into(); + let mut asset_3_reanchored = asset_3.clone(); + assert!(asset_3_reanchored.reanchor(&dest, &reanchor_context).is_ok()); + assert_eq!(asset_3_reanchored, (Location::new(0, [Parachain(1000)]), 10).into()); + + let mut assets: Assets = vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into(); + assert_eq!(assets.clone(), vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into()); + + // decoding respects limits and sorting + assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok()); + + assert!(assets.reanchor(&dest, &reanchor_context).is_ok()); + assert_eq!(assets.0, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored]); + + // decoding respects limits and sorting + assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok()); + } + + #[test] + fn prepend_preserves_sorting() { + use super::*; + use alloc::vec; + + let prefix = Location::new(0, [Parachain(1000)]); + + let asset_1: Asset = (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into(); + let mut asset_1_prepended = asset_1.clone(); + assert!(asset_1_prepended.prepend_with(&prefix).is_ok()); + // changes interior X2->X3 + assert_eq!( + asset_1_prepended, + (Location::new(0, [Parachain(1000), PalletInstance(50), GeneralIndex(1)]), 10).into() + ); + + let asset_2: Asset = (Location::new(1, [PalletInstance(50), GeneralIndex(1)]), 10).into(); + let mut asset_2_prepended = asset_2.clone(); + assert!(asset_2_prepended.prepend_with(&prefix).is_ok()); + // changes parent + assert_eq!( + asset_2_prepended, + (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into() + ); + + let asset_3: Asset = (Location::new(2, [PalletInstance(50), GeneralIndex(1)]), 10).into(); + let mut asset_3_prepended = asset_3.clone(); + assert!(asset_3_prepended.prepend_with(&prefix).is_ok()); + // changes parent + assert_eq!( + asset_3_prepended, + (Location::new(1, [PalletInstance(50), GeneralIndex(1)]), 10).into() + ); + + // `From` impl does sorting. + let mut assets: Assets = vec![asset_1, asset_2, asset_3].into(); + // decoding respects limits and sorting + assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok()); + + // let's do `prepend_with` + assert!(assets.prepend_with(&prefix).is_ok()); + assert_eq!(assets.0, vec![asset_2_prepended, asset_1_prepended, asset_3_prepended]); + + // decoding respects limits and sorting + assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok()); + } + + #[test] + fn decoding_respects_limit() { + use super::*; + + // Having lots of one asset will work since they are deduplicated + let lots_of_one_asset: Assets = + vec![(GeneralIndex(1), 1u128).into(); MAX_ITEMS_IN_ASSETS + 1].into(); + let encoded = lots_of_one_asset.encode(); + assert!(Assets::decode(&mut &encoded[..]).is_ok()); + + // Fewer assets than the limit works + let mut few_assets: Assets = Vec::new().into(); + for i in 0..MAX_ITEMS_IN_ASSETS { + few_assets.push((GeneralIndex(i as u128), 1u128).into()); + } + let encoded = few_assets.encode(); + assert!(Assets::decode(&mut &encoded[..]).is_ok()); + + // Having lots of different assets will not work + let mut too_many_different_assets: Assets = Vec::new().into(); + for i in 0..MAX_ITEMS_IN_ASSETS + 1 { + too_many_different_assets.push((GeneralIndex(i as u128), 1u128).into()); + } + let encoded = too_many_different_assets.encode(); + assert!(Assets::decode(&mut &encoded[..]).is_err()); + } +} diff --git a/polkadot/xcm/src/v5/junction.rs b/polkadot/xcm/src/v5/junction.rs new file mode 100644 index 0000000000000000000000000000000000000000..952b61cd9ffed661b6b583bf0d8672a83ef3eb47 --- /dev/null +++ b/polkadot/xcm/src/v5/junction.rs @@ -0,0 +1,321 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Support data structures for `Location`, primarily the `Junction` datatype. + +use super::Location; +pub use crate::v4::{BodyId, BodyPart}; +use crate::{ + v4::{Junction as OldJunction, NetworkId as OldNetworkId}, + VersionedLocation, +}; +use bounded_collections::{BoundedSlice, BoundedVec, ConstU32}; +use codec::{self, Decode, Encode, MaxEncodedLen}; +use hex_literal::hex; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; + +/// A single item in a path to describe the relative location of a consensus system. +/// +/// Each item assumes a pre-existing location as its context and is defined in terms of it. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub enum Junction { + /// An indexed parachain belonging to and operated by the context. + /// + /// Generally used when the context is a Polkadot Relay-chain. + Parachain(#[codec(compact)] u32), + /// A 32-byte identifier for an account of a specific network that is respected as a sovereign + /// endpoint within the context. + /// + /// Generally used when the context is a Substrate-based chain. + AccountId32 { network: Option, id: [u8; 32] }, + /// An 8-byte index for an account of a specific network that is respected as a sovereign + /// endpoint within the context. + /// + /// May be used when the context is a Frame-based chain and includes e.g. an indices pallet. + AccountIndex64 { + network: Option, + #[codec(compact)] + index: u64, + }, + /// A 20-byte identifier for an account of a specific network that is respected as a sovereign + /// endpoint within the context. + /// + /// May be used when the context is an Ethereum or Bitcoin chain or smart-contract. + AccountKey20 { network: Option, key: [u8; 20] }, + /// An instanced, indexed pallet that forms a constituent part of the context. + /// + /// Generally used when the context is a Frame-based chain. + PalletInstance(u8), + /// A non-descript index within the context location. + /// + /// Usage will vary widely owing to its generality. + /// + /// NOTE: Try to avoid using this and instead use a more specific item. + GeneralIndex(#[codec(compact)] u128), + /// A nondescript array datum, 32 bytes, acting as a key within the context + /// location. + /// + /// Usage will vary widely owing to its generality. + /// + /// NOTE: Try to avoid using this and instead use a more specific item. + // Note this is implemented as an array with a length rather than using `BoundedVec` owing to + // the bound for `Copy`. + GeneralKey { length: u8, data: [u8; 32] }, + /// The unambiguous child. + /// + /// Not currently used except as a fallback when deriving context. + OnlyChild, + /// A pluralistic body existing within consensus. + /// + /// Typical to be used to represent a governance origin of a chain, but could in principle be + /// used to represent things such as multisigs also. + Plurality { id: BodyId, part: BodyPart }, + /// A global network capable of externalizing its own consensus. This is not generally + /// meaningful outside of the universal level. + GlobalConsensus(NetworkId), +} + +/// The genesis hash of the Westend testnet. Used to identify it. +pub const WESTEND_GENESIS_HASH: [u8; 32] = + hex!["e143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e"]; + +/// The genesis hash of the Rococo testnet. Used to identify it. +pub const ROCOCO_GENESIS_HASH: [u8; 32] = + hex!["6408de7737c59c238890533af25896a2c20608d8b380bb01029acb392781063e"]; + +/// Dummy genesis hash used instead of defunct networks like Wococo (and soon Rococo). +pub const DUMMY_GENESIS_HASH: [u8; 32] = [0; 32]; + +/// A global identifier of a data structure existing within consensus. +/// +/// Maintenance note: Networks with global consensus and which are practically bridgeable within the +/// Polkadot ecosystem are given preference over explicit naming in this enumeration. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub enum NetworkId { + /// Network specified by the first 32 bytes of its genesis block. + ByGenesis([u8; 32]), + /// Network defined by the first 32-bytes of the hash and number of some block it contains. + ByFork { block_number: u64, block_hash: [u8; 32] }, + /// The Polkadot mainnet Relay-chain. + Polkadot, + /// The Kusama canary-net Relay-chain. + Kusama, + /// An Ethereum network specified by its chain ID. + Ethereum { + /// The EIP-155 chain ID. + #[codec(compact)] + chain_id: u64, + }, + /// The Bitcoin network, including hard-forks supported by Bitcoin Core development team. + BitcoinCore, + /// The Bitcoin network, including hard-forks supported by Bitcoin Cash developers. + BitcoinCash, + /// The Polkadot Bulletin chain. + PolkadotBulletin, +} + +impl From for Option { + fn from(old: OldNetworkId) -> Self { + Some(NetworkId::from(old)) + } +} + +impl From for NetworkId { + fn from(old: OldNetworkId) -> Self { + use OldNetworkId::*; + match old { + ByGenesis(hash) => Self::ByGenesis(hash), + ByFork { block_number, block_hash } => Self::ByFork { block_number, block_hash }, + Polkadot => Self::Polkadot, + Kusama => Self::Kusama, + Westend => Self::ByGenesis(WESTEND_GENESIS_HASH), + Rococo => Self::ByGenesis(ROCOCO_GENESIS_HASH), + Wococo => Self::ByGenesis(DUMMY_GENESIS_HASH), + Ethereum { chain_id } => Self::Ethereum { chain_id }, + BitcoinCore => Self::BitcoinCore, + BitcoinCash => Self::BitcoinCash, + PolkadotBulletin => Self::PolkadotBulletin, + } + } +} + +impl From for Junction { + fn from(n: NetworkId) -> Self { + Self::GlobalConsensus(n) + } +} + +impl From<[u8; 32]> for Junction { + fn from(id: [u8; 32]) -> Self { + Self::AccountId32 { network: None, id } + } +} + +impl From>> for Junction { + fn from(key: BoundedVec>) -> Self { + key.as_bounded_slice().into() + } +} + +impl<'a> From>> for Junction { + fn from(key: BoundedSlice<'a, u8, ConstU32<32>>) -> Self { + let mut data = [0u8; 32]; + data[..key.len()].copy_from_slice(&key[..]); + Self::GeneralKey { length: key.len() as u8, data } + } +} + +impl<'a> TryFrom<&'a Junction> for BoundedSlice<'a, u8, ConstU32<32>> { + type Error = (); + fn try_from(key: &'a Junction) -> Result { + match key { + Junction::GeneralKey { length, data } => + BoundedSlice::try_from(&data[..data.len().min(*length as usize)]).map_err(|_| ()), + _ => Err(()), + } + } +} + +impl From<[u8; 20]> for Junction { + fn from(key: [u8; 20]) -> Self { + Self::AccountKey20 { network: None, key } + } +} + +impl From for Junction { + fn from(index: u64) -> Self { + Self::AccountIndex64 { network: None, index } + } +} + +impl From for Junction { + fn from(id: u128) -> Self { + Self::GeneralIndex(id) + } +} + +impl TryFrom for Junction { + type Error = (); + fn try_from(value: OldJunction) -> Result { + use OldJunction::*; + Ok(match value { + Parachain(id) => Self::Parachain(id), + AccountId32 { network: maybe_network, id } => + Self::AccountId32 { network: maybe_network.map(|network| network.into()), id }, + AccountIndex64 { network: maybe_network, index } => + Self::AccountIndex64 { network: maybe_network.map(|network| network.into()), index }, + AccountKey20 { network: maybe_network, key } => + Self::AccountKey20 { network: maybe_network.map(|network| network.into()), key }, + PalletInstance(index) => Self::PalletInstance(index), + GeneralIndex(id) => Self::GeneralIndex(id), + GeneralKey { length, data } => Self::GeneralKey { length, data }, + OnlyChild => Self::OnlyChild, + Plurality { id, part } => Self::Plurality { id, part }, + GlobalConsensus(network) => Self::GlobalConsensus(network.into()), + }) + } +} + +impl Junction { + /// Convert `self` into a `Location` containing 0 parents. + /// + /// Similar to `Into::into`, except that this method can be used in a const evaluation context. + pub fn into_location(self) -> Location { + Location::new(0, [self]) + } + + /// Convert `self` into a `Location` containing `n` parents. + /// + /// Similar to `Self::into_location`, with the added ability to specify the number of parent + /// junctions. + pub fn into_exterior(self, n: u8) -> Location { + Location::new(n, [self]) + } + + /// Convert `self` into a `VersionedLocation` containing 0 parents. + /// + /// Similar to `Into::into`, except that this method can be used in a const evaluation context. + pub fn into_versioned(self) -> VersionedLocation { + self.into_location().into_versioned() + } + + /// Remove the `NetworkId` value. + pub fn remove_network_id(&mut self) { + use Junction::*; + match self { + AccountId32 { ref mut network, .. } | + AccountIndex64 { ref mut network, .. } | + AccountKey20 { ref mut network, .. } => *network = None, + _ => {}, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::vec; + + #[test] + fn junction_round_trip_works() { + let j = Junction::GeneralKey { length: 32, data: [1u8; 32] }; + let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap(); + assert_eq!(j, k); + + let j = OldJunction::GeneralKey { length: 32, data: [1u8; 32] }; + let k = OldJunction::try_from(Junction::try_from(j).unwrap()).unwrap(); + assert_eq!(j, k); + + let j = Junction::from(BoundedVec::try_from(vec![1u8, 2, 3, 4]).unwrap()); + let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap(); + assert_eq!(j, k); + let s: BoundedSlice<_, _> = (&k).try_into().unwrap(); + assert_eq!(s, &[1u8, 2, 3, 4][..]); + + let j = OldJunction::GeneralKey { length: 32, data: [1u8; 32] }; + let k = OldJunction::try_from(Junction::try_from(j).unwrap()).unwrap(); + assert_eq!(j, k); + } +} diff --git a/polkadot/xcm/src/v5/junctions.rs b/polkadot/xcm/src/v5/junctions.rs new file mode 100644 index 0000000000000000000000000000000000000000..dc93c541d19d59596b75ed6707a9e594b72b5456 --- /dev/null +++ b/polkadot/xcm/src/v5/junctions.rs @@ -0,0 +1,723 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! XCM `Junctions`/`InteriorLocation` datatype. + +use super::{Junction, Location, NetworkId}; +use alloc::sync::Arc; +use codec::{Decode, Encode, MaxEncodedLen}; +use core::{mem, ops::Range, result}; +use scale_info::TypeInfo; + +/// Maximum number of `Junction`s that a `Junctions` can contain. +pub(crate) const MAX_JUNCTIONS: usize = 8; + +/// Non-parent junctions that can be constructed, up to the length of 8. This specific `Junctions` +/// implementation uses a Rust `enum` in order to make pattern matching easier. +/// +/// Parent junctions cannot be constructed with this type. Refer to `Location` for +/// instructions on constructing parent junctions. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub enum Junctions { + /// The interpreting consensus system. + Here, + /// A relative path comprising 1 junction. + X1(Arc<[Junction; 1]>), + /// A relative path comprising 2 junctions. + X2(Arc<[Junction; 2]>), + /// A relative path comprising 3 junctions. + X3(Arc<[Junction; 3]>), + /// A relative path comprising 4 junctions. + X4(Arc<[Junction; 4]>), + /// A relative path comprising 5 junctions. + X5(Arc<[Junction; 5]>), + /// A relative path comprising 6 junctions. + X6(Arc<[Junction; 6]>), + /// A relative path comprising 7 junctions. + X7(Arc<[Junction; 7]>), + /// A relative path comprising 8 junctions. + X8(Arc<[Junction; 8]>), +} + +macro_rules! impl_junctions { + ($count:expr, $variant:ident) => { + impl From<[Junction; $count]> for Junctions { + fn from(junctions: [Junction; $count]) -> Self { + Self::$variant(Arc::new(junctions)) + } + } + impl PartialEq<[Junction; $count]> for Junctions { + fn eq(&self, rhs: &[Junction; $count]) -> bool { + self.as_slice() == rhs + } + } + }; +} + +impl_junctions!(1, X1); +impl_junctions!(2, X2); +impl_junctions!(3, X3); +impl_junctions!(4, X4); +impl_junctions!(5, X5); +impl_junctions!(6, X6); +impl_junctions!(7, X7); +impl_junctions!(8, X8); + +pub struct JunctionsIterator { + junctions: Junctions, + range: Range, +} + +impl Iterator for JunctionsIterator { + type Item = Junction; + fn next(&mut self) -> Option { + self.junctions.at(self.range.next()?).cloned() + } +} + +impl DoubleEndedIterator for JunctionsIterator { + fn next_back(&mut self) -> Option { + self.junctions.at(self.range.next_back()?).cloned() + } +} + +pub struct JunctionsRefIterator<'a> { + junctions: &'a Junctions, + range: Range, +} + +impl<'a> Iterator for JunctionsRefIterator<'a> { + type Item = &'a Junction; + fn next(&mut self) -> Option<&'a Junction> { + self.junctions.at(self.range.next()?) + } +} + +impl<'a> DoubleEndedIterator for JunctionsRefIterator<'a> { + fn next_back(&mut self) -> Option<&'a Junction> { + self.junctions.at(self.range.next_back()?) + } +} +impl<'a> IntoIterator for &'a Junctions { + type Item = &'a Junction; + type IntoIter = JunctionsRefIterator<'a>; + fn into_iter(self) -> Self::IntoIter { + JunctionsRefIterator { junctions: self, range: 0..self.len() } + } +} + +impl IntoIterator for Junctions { + type Item = Junction; + type IntoIter = JunctionsIterator; + fn into_iter(self) -> Self::IntoIter { + JunctionsIterator { range: 0..self.len(), junctions: self } + } +} + +impl Junctions { + /// Convert `self` into a `Location` containing 0 parents. + /// + /// Similar to `Into::into`, except that this method can be used in a const evaluation context. + pub const fn into_location(self) -> Location { + Location { parents: 0, interior: self } + } + + /// Convert `self` into a `Location` containing `n` parents. + /// + /// Similar to `Self::into_location`, with the added ability to specify the number of parent + /// junctions. + pub const fn into_exterior(self, n: u8) -> Location { + Location { parents: n, interior: self } + } + + /// Casts `self` into a slice containing `Junction`s. + pub fn as_slice(&self) -> &[Junction] { + match self { + Junctions::Here => &[], + Junctions::X1(ref a) => &a[..], + Junctions::X2(ref a) => &a[..], + Junctions::X3(ref a) => &a[..], + Junctions::X4(ref a) => &a[..], + Junctions::X5(ref a) => &a[..], + Junctions::X6(ref a) => &a[..], + Junctions::X7(ref a) => &a[..], + Junctions::X8(ref a) => &a[..], + } + } + + /// Casts `self` into a mutable slice containing `Junction`s. + pub fn as_slice_mut(&mut self) -> &mut [Junction] { + match self { + Junctions::Here => &mut [], + Junctions::X1(ref mut a) => &mut Arc::make_mut(a)[..], + Junctions::X2(ref mut a) => &mut Arc::make_mut(a)[..], + Junctions::X3(ref mut a) => &mut Arc::make_mut(a)[..], + Junctions::X4(ref mut a) => &mut Arc::make_mut(a)[..], + Junctions::X5(ref mut a) => &mut Arc::make_mut(a)[..], + Junctions::X6(ref mut a) => &mut Arc::make_mut(a)[..], + Junctions::X7(ref mut a) => &mut Arc::make_mut(a)[..], + Junctions::X8(ref mut a) => &mut Arc::make_mut(a)[..], + } + } + + /// Remove the `NetworkId` value in any `Junction`s. + pub fn remove_network_id(&mut self) { + self.for_each_mut(Junction::remove_network_id); + } + + /// Treating `self` as the universal context, return the location of the local consensus system + /// from the point of view of the given `target`. + pub fn invert_target(&self, target: &Location) -> Result { + let mut itself = self.clone(); + let mut junctions = Self::Here; + for _ in 0..target.parent_count() { + junctions = junctions + .pushed_front_with(itself.take_last().unwrap_or(Junction::OnlyChild)) + .map_err(|_| ())?; + } + let parents = target.interior().len() as u8; + Ok(Location::new(parents, junctions)) + } + + /// Execute a function `f` on every junction. We use this since we cannot implement a mutable + /// `Iterator` without unsafe code. + pub fn for_each_mut(&mut self, x: impl FnMut(&mut Junction)) { + self.as_slice_mut().iter_mut().for_each(x) + } + + /// Extract the network ID treating this value as a universal location. + /// + /// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate + /// that this value is not a universal location. + pub fn global_consensus(&self) -> Result { + if let Some(Junction::GlobalConsensus(network)) = self.first() { + Ok(*network) + } else { + Err(()) + } + } + + /// Extract the network ID and the interior consensus location, treating this value as a + /// universal location. + /// + /// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate + /// that this value is not a universal location. + pub fn split_global(self) -> Result<(NetworkId, Junctions), ()> { + match self.split_first() { + (location, Some(Junction::GlobalConsensus(network))) => Ok((network, location)), + _ => return Err(()), + } + } + + /// Treat `self` as a universal location and the context of `relative`, returning the universal + /// location of relative. + /// + /// This will return an error if `relative` has as many (or more) parents than there are + /// junctions in `self`, implying that relative refers into a different global consensus. + pub fn within_global(mut self, relative: Location) -> Result { + if self.len() <= relative.parent_count() as usize { + return Err(()) + } + for _ in 0..relative.parent_count() { + self.take_last(); + } + for j in relative.interior() { + self.push(*j).map_err(|_| ())?; + } + Ok(self) + } + + /// Consumes `self` and returns how `viewer` would address it locally. + pub fn relative_to(mut self, viewer: &Junctions) -> Location { + let mut i = 0; + while match (self.first(), viewer.at(i)) { + (Some(x), Some(y)) => x == y, + _ => false, + } { + self = self.split_first().0; + // NOTE: Cannot overflow as loop can only iterate at most `MAX_JUNCTIONS` times. + i += 1; + } + // AUDIT NOTES: + // - above loop ensures that `i <= viewer.len()`. + // - `viewer.len()` is at most `MAX_JUNCTIONS`, so won't overflow a `u8`. + Location::new((viewer.len() - i) as u8, self) + } + + /// Returns first junction, or `None` if the location is empty. + pub fn first(&self) -> Option<&Junction> { + self.as_slice().first() + } + + /// Returns last junction, or `None` if the location is empty. + pub fn last(&self) -> Option<&Junction> { + self.as_slice().last() + } + + /// Splits off the first junction, returning the remaining suffix (first item in tuple) and the + /// first element (second item in tuple) or `None` if it was empty. + pub fn split_first(self) -> (Junctions, Option) { + match self { + Junctions::Here => (Junctions::Here, None), + Junctions::X1(xs) => { + let [a] = *xs; + (Junctions::Here, Some(a)) + }, + Junctions::X2(xs) => { + let [a, b] = *xs; + ([b].into(), Some(a)) + }, + Junctions::X3(xs) => { + let [a, b, c] = *xs; + ([b, c].into(), Some(a)) + }, + Junctions::X4(xs) => { + let [a, b, c, d] = *xs; + ([b, c, d].into(), Some(a)) + }, + Junctions::X5(xs) => { + let [a, b, c, d, e] = *xs; + ([b, c, d, e].into(), Some(a)) + }, + Junctions::X6(xs) => { + let [a, b, c, d, e, f] = *xs; + ([b, c, d, e, f].into(), Some(a)) + }, + Junctions::X7(xs) => { + let [a, b, c, d, e, f, g] = *xs; + ([b, c, d, e, f, g].into(), Some(a)) + }, + Junctions::X8(xs) => { + let [a, b, c, d, e, f, g, h] = *xs; + ([b, c, d, e, f, g, h].into(), Some(a)) + }, + } + } + + /// Splits off the last junction, returning the remaining prefix (first item in tuple) and the + /// last element (second item in tuple) or `None` if it was empty. + pub fn split_last(self) -> (Junctions, Option) { + match self { + Junctions::Here => (Junctions::Here, None), + Junctions::X1(xs) => { + let [a] = *xs; + (Junctions::Here, Some(a)) + }, + Junctions::X2(xs) => { + let [a, b] = *xs; + ([a].into(), Some(b)) + }, + Junctions::X3(xs) => { + let [a, b, c] = *xs; + ([a, b].into(), Some(c)) + }, + Junctions::X4(xs) => { + let [a, b, c, d] = *xs; + ([a, b, c].into(), Some(d)) + }, + Junctions::X5(xs) => { + let [a, b, c, d, e] = *xs; + ([a, b, c, d].into(), Some(e)) + }, + Junctions::X6(xs) => { + let [a, b, c, d, e, f] = *xs; + ([a, b, c, d, e].into(), Some(f)) + }, + Junctions::X7(xs) => { + let [a, b, c, d, e, f, g] = *xs; + ([a, b, c, d, e, f].into(), Some(g)) + }, + Junctions::X8(xs) => { + let [a, b, c, d, e, f, g, h] = *xs; + ([a, b, c, d, e, f, g].into(), Some(h)) + }, + } + } + + /// Removes the first element from `self`, returning it (or `None` if it was empty). + pub fn take_first(&mut self) -> Option { + let mut d = Junctions::Here; + mem::swap(&mut *self, &mut d); + let (tail, head) = d.split_first(); + *self = tail; + head + } + + /// Removes the last element from `self`, returning it (or `None` if it was empty). + pub fn take_last(&mut self) -> Option { + let mut d = Junctions::Here; + mem::swap(&mut *self, &mut d); + let (head, tail) = d.split_last(); + *self = head; + tail + } + + /// Mutates `self` to be appended with `new` or returns an `Err` with `new` if would overflow. + pub fn push(&mut self, new: impl Into) -> result::Result<(), Junction> { + let new = new.into(); + let mut dummy = Junctions::Here; + mem::swap(self, &mut dummy); + match dummy.pushed_with(new) { + Ok(s) => { + *self = s; + Ok(()) + }, + Err((s, j)) => { + *self = s; + Err(j) + }, + } + } + + /// Mutates `self` to be prepended with `new` or returns an `Err` with `new` if would overflow. + pub fn push_front(&mut self, new: impl Into) -> result::Result<(), Junction> { + let new = new.into(); + let mut dummy = Junctions::Here; + mem::swap(self, &mut dummy); + match dummy.pushed_front_with(new) { + Ok(s) => { + *self = s; + Ok(()) + }, + Err((s, j)) => { + *self = s; + Err(j) + }, + } + } + + /// Consumes `self` and returns a `Junctions` suffixed with `new`, or an `Err` with the + /// original value of `self` and `new` in case of overflow. + pub fn pushed_with(self, new: impl Into) -> result::Result { + let new = new.into(); + Ok(match self { + Junctions::Here => [new].into(), + Junctions::X1(xs) => { + let [a] = *xs; + [a, new].into() + }, + Junctions::X2(xs) => { + let [a, b] = *xs; + [a, b, new].into() + }, + Junctions::X3(xs) => { + let [a, b, c] = *xs; + [a, b, c, new].into() + }, + Junctions::X4(xs) => { + let [a, b, c, d] = *xs; + [a, b, c, d, new].into() + }, + Junctions::X5(xs) => { + let [a, b, c, d, e] = *xs; + [a, b, c, d, e, new].into() + }, + Junctions::X6(xs) => { + let [a, b, c, d, e, f] = *xs; + [a, b, c, d, e, f, new].into() + }, + Junctions::X7(xs) => { + let [a, b, c, d, e, f, g] = *xs; + [a, b, c, d, e, f, g, new].into() + }, + s => Err((s, new))?, + }) + } + + /// Consumes `self` and returns a `Junctions` prefixed with `new`, or an `Err` with the + /// original value of `self` and `new` in case of overflow. + pub fn pushed_front_with( + self, + new: impl Into, + ) -> result::Result { + let new = new.into(); + Ok(match self { + Junctions::Here => [new].into(), + Junctions::X1(xs) => { + let [a] = *xs; + [new, a].into() + }, + Junctions::X2(xs) => { + let [a, b] = *xs; + [new, a, b].into() + }, + Junctions::X3(xs) => { + let [a, b, c] = *xs; + [new, a, b, c].into() + }, + Junctions::X4(xs) => { + let [a, b, c, d] = *xs; + [new, a, b, c, d].into() + }, + Junctions::X5(xs) => { + let [a, b, c, d, e] = *xs; + [new, a, b, c, d, e].into() + }, + Junctions::X6(xs) => { + let [a, b, c, d, e, f] = *xs; + [new, a, b, c, d, e, f].into() + }, + Junctions::X7(xs) => { + let [a, b, c, d, e, f, g] = *xs; + [new, a, b, c, d, e, f, g].into() + }, + s => Err((s, new))?, + }) + } + + /// Mutate `self` so that it is suffixed with `suffix`. + /// + /// Does not modify `self` and returns `Err` with `suffix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use staging_xcm::v5::{Junctions, Junction::*, Location}; + /// # fn main() { + /// let mut m = Junctions::from([Parachain(21)]); + /// assert_eq!(m.append_with([PalletInstance(3)]), Ok(())); + /// assert_eq!(m, [Parachain(21), PalletInstance(3)]); + /// # } + /// ``` + pub fn append_with(&mut self, suffix: impl Into) -> Result<(), Junctions> { + let suffix = suffix.into(); + if self.len().saturating_add(suffix.len()) > MAX_JUNCTIONS { + return Err(suffix) + } + for j in suffix.into_iter() { + self.push(j).expect("Already checked the sum of the len()s; qed") + } + Ok(()) + } + + /// Returns the number of junctions in `self`. + pub fn len(&self) -> usize { + self.as_slice().len() + } + + /// Returns the junction at index `i`, or `None` if the location doesn't contain that many + /// elements. + pub fn at(&self, i: usize) -> Option<&Junction> { + self.as_slice().get(i) + } + + /// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't + /// contain that many elements. + pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> { + self.as_slice_mut().get_mut(i) + } + + /// Returns a reference iterator over the junctions. + pub fn iter(&self) -> JunctionsRefIterator { + JunctionsRefIterator { junctions: self, range: 0..self.len() } + } + + /// Ensures that self begins with `prefix` and that it has a single `Junction` item following. + /// If so, returns a reference to this `Junction` item. + /// + /// # Example + /// ```rust + /// # use staging_xcm::v5::{Junctions, Junction::*}; + /// # fn main() { + /// let mut m = Junctions::from([Parachain(2), PalletInstance(3), OnlyChild]); + /// assert_eq!(m.match_and_split(&[Parachain(2), PalletInstance(3)].into()), Some(&OnlyChild)); + /// assert_eq!(m.match_and_split(&[Parachain(2)].into()), None); + /// # } + /// ``` + pub fn match_and_split(&self, prefix: &Junctions) -> Option<&Junction> { + if prefix.len() + 1 != self.len() { + return None + } + for i in 0..prefix.len() { + if prefix.at(i) != self.at(i) { + return None + } + } + return self.at(prefix.len()) + } + + pub fn starts_with(&self, prefix: &Junctions) -> bool { + prefix.len() <= self.len() && prefix.iter().zip(self.iter()).all(|(x, y)| x == y) + } +} + +impl TryFrom for Junctions { + type Error = Location; + fn try_from(x: Location) -> result::Result { + if x.parent_count() > 0 { + Err(x) + } else { + Ok(x.interior().clone()) + } + } +} + +impl> From for Junctions { + fn from(x: T) -> Self { + [x.into()].into() + } +} + +impl From<[Junction; 0]> for Junctions { + fn from(_: [Junction; 0]) -> Self { + Self::Here + } +} + +impl From<()> for Junctions { + fn from(_: ()) -> Self { + Self::Here + } +} + +xcm_procedural::impl_conversion_functions_for_junctions_v5!(); + +#[cfg(test)] +mod tests { + use super::{super::prelude::*, *}; + + #[test] + fn inverting_works() { + let context: InteriorLocation = (Parachain(1000), PalletInstance(42)).into(); + let target = (Parent, PalletInstance(69)).into(); + let expected = (Parent, PalletInstance(42)).into(); + let inverted = context.invert_target(&target).unwrap(); + assert_eq!(inverted, expected); + + let context: InteriorLocation = + (Parachain(1000), PalletInstance(42), GeneralIndex(1)).into(); + let target = (Parent, Parent, PalletInstance(69), GeneralIndex(2)).into(); + let expected = (Parent, Parent, PalletInstance(42), GeneralIndex(1)).into(); + let inverted = context.invert_target(&target).unwrap(); + assert_eq!(inverted, expected); + } + + #[test] + fn relative_to_works() { + use NetworkId::*; + assert_eq!( + Junctions::from([Polkadot.into()]).relative_to(&Junctions::from([Kusama.into()])), + (Parent, Polkadot).into() + ); + let base = Junctions::from([Kusama.into(), Parachain(1), PalletInstance(1)]); + + // Ancestors. + assert_eq!(Here.relative_to(&base), (Parent, Parent, Parent).into()); + assert_eq!(Junctions::from([Kusama.into()]).relative_to(&base), (Parent, Parent).into()); + assert_eq!( + Junctions::from([Kusama.into(), Parachain(1)]).relative_to(&base), + (Parent,).into() + ); + assert_eq!( + Junctions::from([Kusama.into(), Parachain(1), PalletInstance(1)]).relative_to(&base), + Here.into() + ); + + // Ancestors with one child. + assert_eq!( + Junctions::from([Polkadot.into()]).relative_to(&base), + (Parent, Parent, Parent, Polkadot).into() + ); + assert_eq!( + Junctions::from([Kusama.into(), Parachain(2)]).relative_to(&base), + (Parent, Parent, Parachain(2)).into() + ); + assert_eq!( + Junctions::from([Kusama.into(), Parachain(1), PalletInstance(2)]).relative_to(&base), + (Parent, PalletInstance(2)).into() + ); + assert_eq!( + Junctions::from([Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into()]) + .relative_to(&base), + ([1u8; 32],).into() + ); + + // Ancestors with grandchildren. + assert_eq!( + Junctions::from([Polkadot.into(), Parachain(1)]).relative_to(&base), + (Parent, Parent, Parent, Polkadot, Parachain(1)).into() + ); + assert_eq!( + Junctions::from([Kusama.into(), Parachain(2), PalletInstance(1)]).relative_to(&base), + (Parent, Parent, Parachain(2), PalletInstance(1)).into() + ); + assert_eq!( + Junctions::from([Kusama.into(), Parachain(1), PalletInstance(2), [1u8; 32].into()]) + .relative_to(&base), + (Parent, PalletInstance(2), [1u8; 32]).into() + ); + assert_eq!( + Junctions::from([ + Kusama.into(), + Parachain(1), + PalletInstance(1), + [1u8; 32].into(), + 1u128.into() + ]) + .relative_to(&base), + ([1u8; 32], 1u128).into() + ); + } + + #[test] + fn global_consensus_works() { + use NetworkId::*; + assert_eq!(Junctions::from([Polkadot.into()]).global_consensus(), Ok(Polkadot)); + assert_eq!(Junctions::from([Kusama.into(), 1u64.into()]).global_consensus(), Ok(Kusama)); + assert_eq!(Here.global_consensus(), Err(())); + assert_eq!(Junctions::from([1u64.into()]).global_consensus(), Err(())); + assert_eq!(Junctions::from([1u64.into(), Kusama.into()]).global_consensus(), Err(())); + } + + #[test] + fn test_conversion() { + use super::{Junction::*, NetworkId::*}; + let x: Junctions = GlobalConsensus(Polkadot).into(); + assert_eq!(x, Junctions::from([GlobalConsensus(Polkadot)])); + let x: Junctions = Polkadot.into(); + assert_eq!(x, Junctions::from([GlobalConsensus(Polkadot)])); + let x: Junctions = (Polkadot, Kusama).into(); + assert_eq!(x, Junctions::from([GlobalConsensus(Polkadot), GlobalConsensus(Kusama)])); + } + + #[test] + fn encode_decode_junctions_works() { + let original = Junctions::from([ + Polkadot.into(), + Kusama.into(), + 1u64.into(), + GlobalConsensus(Polkadot), + Parachain(123), + PalletInstance(45), + ]); + let encoded = original.encode(); + assert_eq!(encoded, &[6, 9, 2, 9, 3, 2, 0, 4, 9, 2, 0, 237, 1, 4, 45]); + let decoded = Junctions::decode(&mut &encoded[..]).unwrap(); + assert_eq!(decoded, original); + } +} diff --git a/polkadot/xcm/src/v5/location.rs b/polkadot/xcm/src/v5/location.rs new file mode 100644 index 0000000000000000000000000000000000000000..38e8ecdd15ca88155ede9322dd8c714edca7c504 --- /dev/null +++ b/polkadot/xcm/src/v5/location.rs @@ -0,0 +1,755 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! XCM `Location` datatype. + +use super::{traits::Reanchorable, Junction, Junctions}; +use crate::{v4::Location as OldLocation, VersionedLocation}; +use codec::{Decode, Encode, MaxEncodedLen}; +use core::result; +use scale_info::TypeInfo; + +/// A relative path between state-bearing consensus systems. +/// +/// A location in a consensus system is defined as an *isolatable state machine* held within global +/// consensus. The location in question need not have a sophisticated consensus algorithm of its +/// own; a single account within Ethereum, for example, could be considered a location. +/// +/// A very-much non-exhaustive list of types of location include: +/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain. +/// - A layer-0 super-chain, e.g. the Polkadot Relay chain. +/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum. +/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based +/// Substrate chain. +/// - An account. +/// +/// A `Location` is a *relative identifier*, meaning that it can only be used to define the +/// relative path between two locations, and cannot generally be used to refer to a location +/// universally. It is comprised of an integer number of parents specifying the number of times to +/// "escape" upwards into the containing consensus system and then a number of *junctions*, each +/// diving down and specifying some interior portion of state (which may be considered a +/// "sub-consensus" system). +/// +/// This specific `Location` implementation uses a `Junctions` datatype which is a Rust `enum` +/// in order to make pattern matching easier. There are occasions where it is important to ensure +/// that a value is strictly an interior location, in those cases, `Junctions` may be used. +/// +/// The `Location` value of `Null` simply refers to the interpreting consensus system. +#[derive( + Clone, + Decode, + Encode, + Eq, + PartialEq, + Ord, + PartialOrd, + Debug, + TypeInfo, + MaxEncodedLen, + serde::Serialize, + serde::Deserialize, +)] +pub struct Location { + /// The number of parent junctions at the beginning of this `Location`. + pub parents: u8, + /// The interior (i.e. non-parent) junctions that this `Location` contains. + pub interior: Junctions, +} + +impl Default for Location { + fn default() -> Self { + Self::here() + } +} + +/// A relative location which is constrained to be an interior location of the context. +/// +/// See also `Location`. +pub type InteriorLocation = Junctions; + +impl Location { + /// Creates a new `Location` with the given number of parents and interior junctions. + pub fn new(parents: u8, interior: impl Into) -> Location { + Location { parents, interior: interior.into() } + } + + /// Consume `self` and return the equivalent `VersionedLocation` value. + pub const fn into_versioned(self) -> VersionedLocation { + VersionedLocation::V5(self) + } + + /// Creates a new `Location` with 0 parents and a `Here` interior. + /// + /// The resulting `Location` can be interpreted as the "current consensus system". + pub const fn here() -> Location { + Location { parents: 0, interior: Junctions::Here } + } + + /// Creates a new `Location` which evaluates to the parent context. + pub const fn parent() -> Location { + Location { parents: 1, interior: Junctions::Here } + } + + /// Creates a new `Location` with `parents` and an empty (`Here`) interior. + pub const fn ancestor(parents: u8) -> Location { + Location { parents, interior: Junctions::Here } + } + + /// Whether the `Location` has no parents and has a `Here` interior. + pub fn is_here(&self) -> bool { + self.parents == 0 && self.interior.len() == 0 + } + + /// Remove the `NetworkId` value in any interior `Junction`s. + pub fn remove_network_id(&mut self) { + self.interior.remove_network_id(); + } + + /// Return a reference to the interior field. + pub fn interior(&self) -> &Junctions { + &self.interior + } + + /// Return a mutable reference to the interior field. + pub fn interior_mut(&mut self) -> &mut Junctions { + &mut self.interior + } + + /// Returns the number of `Parent` junctions at the beginning of `self`. + pub const fn parent_count(&self) -> u8 { + self.parents + } + + /// Returns the parent count and the interior [`Junctions`] as a tuple. + /// + /// To be used when pattern matching, for example: + /// + /// ```rust + /// # use staging_xcm::v5::{Junctions::*, Junction::*, Location}; + /// fn get_parachain_id(loc: &Location) -> Option { + /// match loc.unpack() { + /// (0, [Parachain(id)]) => Some(*id), + /// _ => None + /// } + /// } + /// ``` + pub fn unpack(&self) -> (u8, &[Junction]) { + (self.parents, self.interior.as_slice()) + } + + /// Returns boolean indicating whether `self` contains only the specified amount of + /// parents and no interior junctions. + pub const fn contains_parents_only(&self, count: u8) -> bool { + matches!(self.interior, Junctions::Here) && self.parents == count + } + + /// Returns the number of parents and junctions in `self`. + pub fn len(&self) -> usize { + self.parent_count() as usize + self.interior.len() + } + + /// Returns the first interior junction, or `None` if the location is empty or contains only + /// parents. + pub fn first_interior(&self) -> Option<&Junction> { + self.interior.first() + } + + /// Returns last junction, or `None` if the location is empty or contains only parents. + pub fn last(&self) -> Option<&Junction> { + self.interior.last() + } + + /// Splits off the first interior junction, returning the remaining suffix (first item in tuple) + /// and the first element (second item in tuple) or `None` if it was empty. + pub fn split_first_interior(self) -> (Location, Option) { + let Location { parents, interior: junctions } = self; + let (suffix, first) = junctions.split_first(); + let location = Location { parents, interior: suffix }; + (location, first) + } + + /// Splits off the last interior junction, returning the remaining prefix (first item in tuple) + /// and the last element (second item in tuple) or `None` if it was empty or if `self` only + /// contains parents. + pub fn split_last_interior(self) -> (Location, Option) { + let Location { parents, interior: junctions } = self; + let (prefix, last) = junctions.split_last(); + let location = Location { parents, interior: prefix }; + (location, last) + } + + /// Mutates `self`, suffixing its interior junctions with `new`. Returns `Err` with `new` in + /// case of overflow. + pub fn push_interior(&mut self, new: impl Into) -> result::Result<(), Junction> { + self.interior.push(new) + } + + /// Mutates `self`, prefixing its interior junctions with `new`. Returns `Err` with `new` in + /// case of overflow. + pub fn push_front_interior( + &mut self, + new: impl Into, + ) -> result::Result<(), Junction> { + self.interior.push_front(new) + } + + /// Consumes `self` and returns a `Location` suffixed with `new`, or an `Err` with + /// the original value of `self` in case of overflow. + pub fn pushed_with_interior( + self, + new: impl Into, + ) -> result::Result { + match self.interior.pushed_with(new) { + Ok(i) => Ok(Location { interior: i, parents: self.parents }), + Err((i, j)) => Err((Location { interior: i, parents: self.parents }, j)), + } + } + + /// Consumes `self` and returns a `Location` prefixed with `new`, or an `Err` with the + /// original value of `self` in case of overflow. + pub fn pushed_front_with_interior( + self, + new: impl Into, + ) -> result::Result { + match self.interior.pushed_front_with(new) { + Ok(i) => Ok(Location { interior: i, parents: self.parents }), + Err((i, j)) => Err((Location { interior: i, parents: self.parents }, j)), + } + } + + /// Returns the junction at index `i`, or `None` if the location is a parent or if the location + /// does not contain that many elements. + pub fn at(&self, i: usize) -> Option<&Junction> { + let num_parents = self.parents as usize; + if i < num_parents { + return None + } + self.interior.at(i - num_parents) + } + + /// Returns a mutable reference to the junction at index `i`, or `None` if the location is a + /// parent or if it doesn't contain that many elements. + pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> { + let num_parents = self.parents as usize; + if i < num_parents { + return None + } + self.interior.at_mut(i - num_parents) + } + + /// Decrements the parent count by 1. + pub fn dec_parent(&mut self) { + self.parents = self.parents.saturating_sub(1); + } + + /// Removes the first interior junction from `self`, returning it + /// (or `None` if it was empty or if `self` contains only parents). + pub fn take_first_interior(&mut self) -> Option { + self.interior.take_first() + } + + /// Removes the last element from `interior`, returning it (or `None` if it was empty or if + /// `self` only contains parents). + pub fn take_last(&mut self) -> Option { + self.interior.take_last() + } + + /// Ensures that `self` has the same number of parents as `prefix`, its junctions begins with + /// the junctions of `prefix` and that it has a single `Junction` item following. + /// If so, returns a reference to this `Junction` item. + /// + /// # Example + /// ```rust + /// # use staging_xcm::v5::{Junctions::*, Junction::*, Location}; + /// # fn main() { + /// let mut m = Location::new(1, [PalletInstance(3), OnlyChild]); + /// assert_eq!( + /// m.match_and_split(&Location::new(1, [PalletInstance(3)])), + /// Some(&OnlyChild), + /// ); + /// assert_eq!(m.match_and_split(&Location::new(1, Here)), None); + /// # } + /// ``` + pub fn match_and_split(&self, prefix: &Location) -> Option<&Junction> { + if self.parents != prefix.parents { + return None + } + self.interior.match_and_split(&prefix.interior) + } + + pub fn starts_with(&self, prefix: &Location) -> bool { + self.parents == prefix.parents && self.interior.starts_with(&prefix.interior) + } + + /// Mutate `self` so that it is suffixed with `suffix`. + /// + /// Does not modify `self` and returns `Err` with `suffix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use staging_xcm::v5::{Junctions::*, Junction::*, Location, Parent}; + /// # fn main() { + /// let mut m: Location = (Parent, Parachain(21), 69u64).into(); + /// assert_eq!(m.append_with((Parent, PalletInstance(3))), Ok(())); + /// assert_eq!(m, Location::new(1, [Parachain(21), PalletInstance(3)])); + /// # } + /// ``` + pub fn append_with(&mut self, suffix: impl Into) -> Result<(), Self> { + let prefix = core::mem::replace(self, suffix.into()); + match self.prepend_with(prefix) { + Ok(()) => Ok(()), + Err(prefix) => Err(core::mem::replace(self, prefix)), + } + } + + /// Consume `self` and return its value suffixed with `suffix`. + /// + /// Returns `Err` with the original value of `self` and `suffix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use staging_xcm::v5::{Junctions::*, Junction::*, Location, Parent}; + /// # fn main() { + /// let mut m: Location = (Parent, Parachain(21), 69u64).into(); + /// let r = m.appended_with((Parent, PalletInstance(3))).unwrap(); + /// assert_eq!(r, Location::new(1, [Parachain(21), PalletInstance(3)])); + /// # } + /// ``` + pub fn appended_with(mut self, suffix: impl Into) -> Result { + match self.append_with(suffix) { + Ok(()) => Ok(self), + Err(suffix) => Err((self, suffix)), + } + } + + /// Mutate `self` so that it is prefixed with `prefix`. + /// + /// Does not modify `self` and returns `Err` with `prefix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use staging_xcm::v5::{Junctions::*, Junction::*, Location, Parent}; + /// # fn main() { + /// let mut m: Location = (Parent, Parent, PalletInstance(3)).into(); + /// assert_eq!(m.prepend_with((Parent, Parachain(21), OnlyChild)), Ok(())); + /// assert_eq!(m, Location::new(1, [PalletInstance(3)])); + /// # } + /// ``` + pub fn prepend_with(&mut self, prefix: impl Into) -> Result<(), Self> { + // prefix self (suffix) + // P .. P I .. I p .. p i .. i + let mut prefix = prefix.into(); + let prepend_interior = prefix.interior.len().saturating_sub(self.parents as usize); + let final_interior = self.interior.len().saturating_add(prepend_interior); + if final_interior > super::junctions::MAX_JUNCTIONS { + return Err(prefix) + } + let suffix_parents = (self.parents as usize).saturating_sub(prefix.interior.len()); + let final_parents = (prefix.parents as usize).saturating_add(suffix_parents); + if final_parents > 255 { + return Err(prefix) + } + + // cancel out the final item on the prefix interior for one of the suffix's parents. + while self.parents > 0 && prefix.take_last().is_some() { + self.dec_parent(); + } + + // now we have either removed all suffix's parents or prefix interior. + // this means we can combine the prefix's and suffix's remaining parents/interior since + // we know that with at least one empty, the overall order will be respected: + // prefix self (suffix) + // P .. P (I) p .. p i .. i => P + p .. (no I) i + // -- or -- + // P .. P I .. I (p) i .. i => P (no p) .. I + i + + self.parents = self.parents.saturating_add(prefix.parents); + for j in prefix.interior.into_iter().rev() { + self.push_front_interior(j) + .expect("final_interior no greater than MAX_JUNCTIONS; qed"); + } + Ok(()) + } + + /// Consume `self` and return its value prefixed with `prefix`. + /// + /// Returns `Err` with the original value of `self` and `prefix` in case of overflow. + /// + /// # Example + /// ```rust + /// # use staging_xcm::v5::{Junctions::*, Junction::*, Location, Parent}; + /// # fn main() { + /// let m: Location = (Parent, Parent, PalletInstance(3)).into(); + /// let r = m.prepended_with((Parent, Parachain(21), OnlyChild)).unwrap(); + /// assert_eq!(r, Location::new(1, [PalletInstance(3)])); + /// # } + /// ``` + pub fn prepended_with(mut self, prefix: impl Into) -> Result { + match self.prepend_with(prefix) { + Ok(()) => Ok(self), + Err(prefix) => Err((self, prefix)), + } + } + + /// Remove any unneeded parents/junctions in `self` based on the given context it will be + /// interpreted in. + pub fn simplify(&mut self, context: &Junctions) { + if context.len() < self.parents as usize { + // Not enough context + return + } + while self.parents > 0 { + let maybe = context.at(context.len() - (self.parents as usize)); + match (self.interior.first(), maybe) { + (Some(i), Some(j)) if i == j => { + self.interior.take_first(); + self.parents -= 1; + }, + _ => break, + } + } + } + + /// Return the Location subsection identifying the chain that `self` points to. + pub fn chain_location(&self) -> Location { + let mut clone = self.clone(); + // start popping junctions until we reach chain identifier + while let Some(j) = clone.last() { + if matches!(j, Junction::Parachain(_) | Junction::GlobalConsensus(_)) { + // return chain subsection + return clone + } else { + (clone, _) = clone.split_last_interior(); + } + } + Location::new(clone.parents, Junctions::Here) + } +} + +impl Reanchorable for Location { + type Error = Self; + + /// Mutate `self` so that it represents the same location from the point of view of `target`. + /// The context of `self` is provided as `context`. + /// + /// Does not modify `self` in case of overflow. + fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> { + // TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this. + + // 1. Use our `context` to figure out how the `target` would address us. + let inverted_target = context.invert_target(target)?; + + // 2. Prepend `inverted_target` to `self` to get self's location from the perspective of + // `target`. + self.prepend_with(inverted_target).map_err(|_| ())?; + + // 3. Given that we know some of `target` context, ensure that any parents in `self` are + // strictly needed. + self.simplify(target.interior()); + + Ok(()) + } + + /// Consume `self` and return a new value representing the same location from the point of view + /// of `target`. The context of `self` is provided as `context`. + /// + /// Returns the original `self` in case of overflow. + fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result { + match self.reanchor(target, context) { + Ok(()) => Ok(self), + Err(()) => Err(self), + } + } +} + +impl TryFrom for Option { + type Error = (); + fn try_from(value: OldLocation) -> result::Result { + Ok(Some(Location::try_from(value)?)) + } +} + +impl TryFrom for Location { + type Error = (); + fn try_from(x: OldLocation) -> result::Result { + Ok(Location { parents: x.parents, interior: x.interior.try_into()? }) + } +} + +/// A unit struct which can be converted into a `Location` of `parents` value 1. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Parent; +impl From for Location { + fn from(_: Parent) -> Self { + Location { parents: 1, interior: Junctions::Here } + } +} + +/// A tuple struct which can be converted into a `Location` of `parents` value 1 with the inner +/// interior. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct ParentThen(pub Junctions); +impl From for Location { + fn from(ParentThen(interior): ParentThen) -> Self { + Location { parents: 1, interior } + } +} + +/// A unit struct which can be converted into a `Location` of the inner `parents` value. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Ancestor(pub u8); +impl From for Location { + fn from(Ancestor(parents): Ancestor) -> Self { + Location { parents, interior: Junctions::Here } + } +} + +/// A unit struct which can be converted into a `Location` of the inner `parents` value and the +/// inner interior. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct AncestorThen(pub u8, pub Interior); +impl> From> for Location { + fn from(AncestorThen(parents, interior): AncestorThen) -> Self { + Location { parents, interior: interior.into() } + } +} + +impl From<[u8; 32]> for Location { + fn from(bytes: [u8; 32]) -> Self { + let junction: Junction = bytes.into(); + junction.into() + } +} + +impl From for Location { + fn from(id: sp_runtime::AccountId32) -> Self { + Junction::AccountId32 { network: None, id: id.into() }.into() + } +} + +xcm_procedural::impl_conversion_functions_for_location_v5!(); + +#[cfg(test)] +mod tests { + use crate::v5::prelude::*; + use codec::{Decode, Encode}; + + #[test] + fn conversion_works() { + let x: Location = Parent.into(); + assert_eq!(x, Location { parents: 1, interior: Here }); + // let x: Location = (Parent,).into(); + // assert_eq!(x, Location { parents: 1, interior: Here }); + // let x: Location = (Parent, Parent).into(); + // assert_eq!(x, Location { parents: 2, interior: Here }); + let x: Location = (Parent, Parent, OnlyChild).into(); + assert_eq!(x, Location { parents: 2, interior: OnlyChild.into() }); + let x: Location = OnlyChild.into(); + assert_eq!(x, Location { parents: 0, interior: OnlyChild.into() }); + let x: Location = (OnlyChild,).into(); + assert_eq!(x, Location { parents: 0, interior: OnlyChild.into() }); + } + + #[test] + fn simplify_basic_works() { + let mut location: Location = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + let context = [Parachain(1000), PalletInstance(42)].into(); + let expected = GeneralIndex(69).into(); + location.simplify(&context); + assert_eq!(location, expected); + + let mut location: Location = (Parent, PalletInstance(42), GeneralIndex(69)).into(); + let context = [PalletInstance(42)].into(); + let expected = GeneralIndex(69).into(); + location.simplify(&context); + assert_eq!(location, expected); + + let mut location: Location = (Parent, PalletInstance(42), GeneralIndex(69)).into(); + let context = [Parachain(1000), PalletInstance(42)].into(); + let expected = GeneralIndex(69).into(); + location.simplify(&context); + assert_eq!(location, expected); + + let mut location: Location = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + let context = [OnlyChild, Parachain(1000), PalletInstance(42)].into(); + let expected = GeneralIndex(69).into(); + location.simplify(&context); + assert_eq!(location, expected); + } + + #[test] + fn simplify_incompatible_location_fails() { + let mut location: Location = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + let context = [Parachain(1000), PalletInstance(42), GeneralIndex(42)].into(); + let expected = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + location.simplify(&context); + assert_eq!(location, expected); + + let mut location: Location = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + let context = [Parachain(1000)].into(); + let expected = + (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); + location.simplify(&context); + assert_eq!(location, expected); + } + + #[test] + fn reanchor_works() { + let mut id: Location = (Parent, Parachain(1000), GeneralIndex(42)).into(); + let context = Parachain(2000).into(); + let target = (Parent, Parachain(1000)).into(); + let expected = GeneralIndex(42).into(); + id.reanchor(&target, &context).unwrap(); + assert_eq!(id, expected); + } + + #[test] + fn encode_and_decode_works() { + let m = Location { + parents: 1, + interior: [Parachain(42), AccountIndex64 { network: None, index: 23 }].into(), + }; + let encoded = m.encode(); + assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec()); + let decoded = Location::decode(&mut &encoded[..]); + assert_eq!(decoded, Ok(m)); + } + + #[test] + fn match_and_split_works() { + let m = Location { + parents: 1, + interior: [Parachain(42), AccountIndex64 { network: None, index: 23 }].into(), + }; + assert_eq!(m.match_and_split(&Location { parents: 1, interior: Here }), None); + assert_eq!( + m.match_and_split(&Location { parents: 1, interior: [Parachain(42)].into() }), + Some(&AccountIndex64 { network: None, index: 23 }) + ); + assert_eq!(m.match_and_split(&m), None); + } + + #[test] + fn append_with_works() { + let acc = AccountIndex64 { network: None, index: 23 }; + let mut m = Location { parents: 1, interior: [Parachain(42)].into() }; + assert_eq!(m.append_with([PalletInstance(3), acc]), Ok(())); + assert_eq!( + m, + Location { parents: 1, interior: [Parachain(42), PalletInstance(3), acc].into() } + ); + + // cannot append to create overly long location + let acc = AccountIndex64 { network: None, index: 23 }; + let m = Location { + parents: 254, + interior: [Parachain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild].into(), + }; + let suffix: Location = (PalletInstance(3), acc, OnlyChild, OnlyChild).into(); + assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix)); + } + + #[test] + fn prepend_with_works() { + let mut m = Location { + parents: 1, + interior: [Parachain(42), AccountIndex64 { network: None, index: 23 }].into(), + }; + assert_eq!(m.prepend_with(Location { parents: 1, interior: [OnlyChild].into() }), Ok(())); + assert_eq!( + m, + Location { + parents: 1, + interior: [Parachain(42), AccountIndex64 { network: None, index: 23 }].into() + } + ); + + // cannot prepend to create overly long location + let mut m = Location { parents: 254, interior: [Parachain(42)].into() }; + let prefix = Location { parents: 2, interior: Here }; + assert_eq!(m.prepend_with(prefix.clone()), Err(prefix)); + + let prefix = Location { parents: 1, interior: Here }; + assert_eq!(m.prepend_with(prefix.clone()), Ok(())); + assert_eq!(m, Location { parents: 255, interior: [Parachain(42)].into() }); + } + + #[test] + fn double_ended_ref_iteration_works() { + let m: Junctions = [Parachain(1000), Parachain(3), PalletInstance(5)].into(); + let mut iter = m.iter(); + + let first = iter.next().unwrap(); + assert_eq!(first, &Parachain(1000)); + let third = iter.next_back().unwrap(); + assert_eq!(third, &PalletInstance(5)); + let second = iter.next_back().unwrap(); + assert_eq!(iter.next(), None); + assert_eq!(iter.next_back(), None); + assert_eq!(second, &Parachain(3)); + + let res = Here + .pushed_with(*first) + .unwrap() + .pushed_with(*second) + .unwrap() + .pushed_with(*third) + .unwrap(); + assert_eq!(m, res); + + // make sure there's no funny business with the 0 indexing + let m = Here; + let mut iter = m.iter(); + + assert_eq!(iter.next(), None); + assert_eq!(iter.next_back(), None); + } + + #[test] + fn conversion_from_other_types_works() { + use crate::v4; + + fn takes_location>(_arg: Arg) {} + + takes_location(Parent); + takes_location(Here); + takes_location([Parachain(42)]); + takes_location((Ancestor(255), PalletInstance(8))); + takes_location((Ancestor(5), Parachain(1), PalletInstance(3))); + takes_location((Ancestor(2), Here)); + takes_location(AncestorThen( + 3, + [Parachain(43), AccountIndex64 { network: None, index: 155 }], + )); + takes_location((Parent, AccountId32 { network: None, id: [0; 32] })); + takes_location((Parent, Here)); + takes_location(ParentThen([Parachain(75)].into())); + takes_location([Parachain(100), PalletInstance(3)]); + + assert_eq!(v4::Location::from(v4::Junctions::Here).try_into(), Ok(Location::here())); + assert_eq!(v4::Location::from(v4::Parent).try_into(), Ok(Location::parent())); + assert_eq!( + v4::Location::from((v4::Parent, v4::Parent, v4::Junction::GeneralIndex(42u128),)) + .try_into(), + Ok(Location { parents: 2, interior: [GeneralIndex(42u128)].into() }), + ); + } +} diff --git a/polkadot/xcm/src/v5/mod.rs b/polkadot/xcm/src/v5/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..d455fa48adae8df00b9b6dc88f4916e5423388ad --- /dev/null +++ b/polkadot/xcm/src/v5/mod.rs @@ -0,0 +1,1585 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Version 5 of the Cross-Consensus Message format data structures. + +pub use super::v3::GetWeight; +use super::v4::{ + Instruction as OldInstruction, PalletInfo as OldPalletInfo, + QueryResponseInfo as OldQueryResponseInfo, Response as OldResponse, Xcm as OldXcm, +}; +use crate::DoubleEncoded; +use alloc::{vec, vec::Vec}; +use bounded_collections::{parameter_types, BoundedVec}; +use codec::{ + self, decode_vec_with_len, Compact, Decode, Encode, Error as CodecError, Input as CodecInput, + MaxEncodedLen, +}; +use core::{fmt::Debug, result}; +use derivative::Derivative; +use scale_info::TypeInfo; + +mod asset; +mod junction; +pub(crate) mod junctions; +mod location; +mod traits; + +pub use asset::{ + Asset, AssetFilter, AssetId, AssetInstance, AssetTransferFilter, Assets, Fungibility, + WildAsset, WildFungibility, MAX_ITEMS_IN_ASSETS, +}; +pub use junction::{ + BodyId, BodyPart, Junction, NetworkId, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH, +}; +pub use junctions::Junctions; +pub use location::{Ancestor, AncestorThen, InteriorLocation, Location, Parent, ParentThen}; +pub use traits::{ + send_xcm, validate_send, Error, ExecuteXcm, Outcome, PreparedMessage, Reanchorable, Result, + SendError, SendResult, SendXcm, Weight, XcmHash, +}; +// These parts of XCM v4 are unchanged in XCM v5, and are re-imported here. +pub use super::v4::{MaxDispatchErrorLen, MaybeErrorCode, OriginKind, WeightLimit}; + +pub const VERSION: super::Version = 5; + +/// An identifier for a query. +pub type QueryId = u64; + +#[derive(Derivative, Default, Encode, TypeInfo)] +#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] +#[codec(encode_bound())] +#[codec(decode_bound())] +#[scale_info(bounds(), skip_type_params(Call))] +pub struct Xcm(pub Vec>); + +pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100; + +environmental::environmental!(instructions_count: u8); + +impl Decode for Xcm { + fn decode(input: &mut I) -> core::result::Result { + instructions_count::using_once(&mut 0, || { + let number_of_instructions: u32 = >::decode(input)?.into(); + instructions_count::with(|count| { + *count = count.saturating_add(number_of_instructions as u8); + if *count > MAX_INSTRUCTIONS_TO_DECODE { + return Err(CodecError::from("Max instructions exceeded")) + } + Ok(()) + }) + .expect("Called in `using` context and thus can not return `None`; qed")?; + let decoded_instructions = decode_vec_with_len(input, number_of_instructions as usize)?; + Ok(Self(decoded_instructions)) + }) + } +} + +impl Xcm { + /// Create an empty instance. + pub fn new() -> Self { + Self(vec![]) + } + + /// Return `true` if no instructions are held in `self`. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Return the number of instructions held in `self`. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Return a reference to the inner value. + pub fn inner(&self) -> &[Instruction] { + &self.0 + } + + /// Return a mutable reference to the inner value. + pub fn inner_mut(&mut self) -> &mut Vec> { + &mut self.0 + } + + /// Consume and return the inner value. + pub fn into_inner(self) -> Vec> { + self.0 + } + + /// Return an iterator over references to the items. + pub fn iter(&self) -> impl Iterator> { + self.0.iter() + } + + /// Return an iterator over mutable references to the items. + pub fn iter_mut(&mut self) -> impl Iterator> { + self.0.iter_mut() + } + + /// Consume and return an iterator over the items. + pub fn into_iter(self) -> impl Iterator> { + self.0.into_iter() + } + + /// Consume and either return `self` if it contains some instructions, or if it's empty, then + /// instead return the result of `f`. + pub fn or_else(self, f: impl FnOnce() -> Self) -> Self { + if self.0.is_empty() { + f() + } else { + self + } + } + + /// Return the first instruction, if any. + pub fn first(&self) -> Option<&Instruction> { + self.0.first() + } + + /// Return the last instruction, if any. + pub fn last(&self) -> Option<&Instruction> { + self.0.last() + } + + /// Return the only instruction, contained in `Self`, iff only one exists (`None` otherwise). + pub fn only(&self) -> Option<&Instruction> { + if self.0.len() == 1 { + self.0.first() + } else { + None + } + } + + /// Return the only instruction, contained in `Self`, iff only one exists (returns `self` + /// otherwise). + pub fn into_only(mut self) -> core::result::Result, Self> { + if self.0.len() == 1 { + self.0.pop().ok_or(self) + } else { + Err(self) + } + } +} + +impl From>> for Xcm { + fn from(c: Vec>) -> Self { + Self(c) + } +} + +impl From> for Vec> { + fn from(c: Xcm) -> Self { + c.0 + } +} + +/// A prelude for importing all types typically used when interacting with XCM messages. +pub mod prelude { + mod contents { + pub use super::super::{ + send_xcm, validate_send, Ancestor, AncestorThen, Asset, + AssetFilter::{self, *}, + AssetId, + AssetInstance::{self, *}, + Assets, BodyId, BodyPart, Error as XcmError, ExecuteXcm, + Fungibility::{self, *}, + Instruction::*, + InteriorLocation, + Junction::{self, *}, + Junctions::{self, Here}, + Location, MaybeErrorCode, + NetworkId::{self, *}, + OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId, + QueryResponseInfo, Reanchorable, Response, Result as XcmResult, SendError, SendResult, + SendXcm, Weight, + WeightLimit::{self, *}, + WildAsset::{self, *}, + WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, + XcmContext, XcmHash, XcmWeightInfo, VERSION as XCM_VERSION, + }; + } + pub use super::{Instruction, Xcm}; + pub use contents::*; + pub mod opaque { + pub use super::{ + super::opaque::{Instruction, Xcm}, + contents::*, + }; + } +} + +parameter_types! { + pub MaxPalletNameLen: u32 = 48; + pub MaxPalletsInfo: u32 = 64; +} + +#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] +pub struct PalletInfo { + #[codec(compact)] + pub index: u32, + pub name: BoundedVec, + pub module_name: BoundedVec, + #[codec(compact)] + pub major: u32, + #[codec(compact)] + pub minor: u32, + #[codec(compact)] + pub patch: u32, +} + +impl TryInto for PalletInfo { + type Error = (); + + fn try_into(self) -> result::Result { + OldPalletInfo::new( + self.index, + self.name.into_inner(), + self.module_name.into_inner(), + self.major, + self.minor, + self.patch, + ) + .map_err(|_| ()) + } +} + +impl PalletInfo { + pub fn new( + index: u32, + name: Vec, + module_name: Vec, + major: u32, + minor: u32, + patch: u32, + ) -> result::Result { + let name = BoundedVec::try_from(name).map_err(|_| Error::Overflow)?; + let module_name = BoundedVec::try_from(module_name).map_err(|_| Error::Overflow)?; + + Ok(Self { index, name, module_name, major, minor, patch }) + } +} + +/// Response data to a query. +#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] +pub enum Response { + /// No response. Serves as a neutral default. + Null, + /// Some assets. + Assets(Assets), + /// The outcome of an XCM instruction. + ExecutionResult(Option<(u32, Error)>), + /// An XCM version. + Version(super::Version), + /// The index, instance name, pallet name and version of some pallets. + PalletsInfo(BoundedVec), + /// The status of a dispatch attempt using `Transact`. + DispatchResult(MaybeErrorCode), +} + +impl Default for Response { + fn default() -> Self { + Self::Null + } +} + +impl TryFrom for Response { + type Error = (); + + fn try_from(old: OldResponse) -> result::Result { + use OldResponse::*; + Ok(match old { + Null => Self::Null, + Assets(assets) => Self::Assets(assets.try_into()?), + ExecutionResult(result) => Self::ExecutionResult( + result + .map(|(num, old_error)| (num, old_error.try_into())) + .map(|(num, result)| result.map(|inner| (num, inner))) + .transpose()?, + ), + Version(version) => Self::Version(version), + PalletsInfo(pallet_info) => { + let inner = pallet_info + .into_iter() + .map(TryInto::try_into) + .collect::, _>>()?; + Self::PalletsInfo( + BoundedVec::::try_from(inner).map_err(|_| ())?, + ) + }, + DispatchResult(maybe_error) => Self::DispatchResult(maybe_error), + }) + } +} + +/// Information regarding the composition of a query response. +#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)] +pub struct QueryResponseInfo { + /// The destination to which the query response message should be send. + pub destination: Location, + /// The `query_id` field of the `QueryResponse` message. + #[codec(compact)] + pub query_id: QueryId, + /// The `max_weight` field of the `QueryResponse` message. + pub max_weight: Weight, +} + +impl TryFrom for QueryResponseInfo { + type Error = (); + + fn try_from(old: OldQueryResponseInfo) -> result::Result { + Ok(Self { + destination: old.destination.try_into()?, + query_id: old.query_id, + max_weight: old.max_weight, + }) + } +} + +/// Contextual data pertaining to a specific list of XCM instructions. +#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)] +pub struct XcmContext { + /// The current value of the Origin register of the `XCVM`. + pub origin: Option, + /// The identity of the XCM; this may be a hash of its versioned encoding but could also be + /// a high-level identity set by an appropriate barrier. + pub message_id: XcmHash, + /// The current value of the Topic register of the `XCVM`. + pub topic: Option<[u8; 32]>, +} + +impl XcmContext { + /// Constructor which sets the message ID to the supplied parameter and leaves the origin and + /// topic unset. + pub fn with_message_id(message_id: XcmHash) -> XcmContext { + XcmContext { origin: None, message_id, topic: None } + } +} + +/// Cross-Consensus Message: A message from one consensus system to another. +/// +/// Consensus systems that may send and receive messages include blockchains and smart contracts. +/// +/// All messages are delivered from a known *origin*, expressed as a `Location`. +/// +/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the +/// outer XCM format, known as `VersionedXcm`. +#[derive( + Derivative, + Encode, + Decode, + TypeInfo, + xcm_procedural::XcmWeightInfoTrait, + xcm_procedural::Builder, +)] +#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] +#[codec(encode_bound())] +#[codec(decode_bound())] +#[scale_info(bounds(), skip_type_params(Call))] +pub enum Instruction { + /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into the Holding + /// Register. + /// + /// - `assets`: The asset(s) to be withdrawn into holding. + /// + /// Kind: *Command*. + /// + /// Errors: + #[builder(loads_holding)] + WithdrawAsset(Assets), + + /// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` + /// system and equivalent derivatives should be placed into the Holding Register. + /// + /// - `assets`: The asset(s) that are minted into holding. + /// + /// Safety: `origin` must be trusted to have received and be storing `assets` such that they + /// may later be withdrawn should this system send a corresponding message. + /// + /// Kind: *Trusted Indication*. + /// + /// Errors: + #[builder(loads_holding)] + ReserveAssetDeposited(Assets), + + /// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should + /// be created and placed into the Holding Register. + /// + /// - `assets`: The asset(s) that are minted into the Holding Register. + /// + /// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding `assets` + /// prior as a consequence of sending this message. + /// + /// Kind: *Trusted Indication*. + /// + /// Errors: + #[builder(loads_holding)] + ReceiveTeleportedAsset(Assets), + + /// Respond with information that the local system is expecting. + /// + /// - `query_id`: The identifier of the query that resulted in this message being sent. + /// - `response`: The message content. + /// - `max_weight`: The maximum weight that handling this response should take. + /// - `querier`: The location responsible for the initiation of the response, if there is one. + /// In general this will tend to be the same location as the receiver of this message. NOTE: + /// As usual, this is interpreted from the perspective of the receiving consensus system. + /// + /// Safety: Since this is information only, there are no immediate concerns. However, it should + /// be remembered that even if the Origin behaves reasonably, it can always be asked to make + /// a response to a third-party chain who may or may not be expecting the response. Therefore + /// the `querier` should be checked to match the expected value. + /// + /// Kind: *Information*. + /// + /// Errors: + QueryResponse { + #[codec(compact)] + query_id: QueryId, + response: Response, + max_weight: Weight, + querier: Option, + }, + + /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets + /// under the ownership of `beneficiary`. + /// + /// - `assets`: The asset(s) to be withdrawn. + /// - `beneficiary`: The new owner for the assets. + /// + /// Safety: No concerns. + /// + /// Kind: *Command*. + /// + /// Errors: + TransferAsset { assets: Assets, beneficiary: Location }, + + /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets + /// under the ownership of `dest` within this consensus system (i.e. its sovereign account). + /// + /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given + /// `xcm`. + /// + /// - `assets`: The asset(s) to be withdrawn. + /// - `dest`: The location whose sovereign account will own the assets and thus the effective + /// beneficiary for the assets and the notification target for the reserve asset deposit + /// message. + /// - `xcm`: The instructions that should follow the `ReserveAssetDeposited` instruction, which + /// is sent onwards to `dest`. + /// + /// Safety: No concerns. + /// + /// Kind: *Command*. + /// + /// Errors: + TransferReserveAsset { assets: Assets, dest: Location, xcm: Xcm<()> }, + + /// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed + /// by the kind of origin `origin_kind`. + /// + /// The Transact Status Register is set according to the result of dispatching the call. + /// + /// - `origin_kind`: The means of expressing the message origin as a dispatch origin. + /// - `call`: The encoded transaction to be applied. + /// + /// Safety: No concerns. + /// + /// Kind: *Command*. + /// + /// Errors: + Transact { origin_kind: OriginKind, call: DoubleEncoded }, + + /// A message to notify about a new incoming HRMP channel. This message is meant to be sent by + /// the relay-chain to a para. + /// + /// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel + /// opening. + /// - `max_message_size`: The maximum size of a message proposed by the sender. + /// - `max_capacity`: The maximum number of messages that can be queued in the channel. + /// + /// Safety: The message should originate directly from the relay-chain. + /// + /// Kind: *System Notification* + HrmpNewChannelOpenRequest { + #[codec(compact)] + sender: u32, + #[codec(compact)] + max_message_size: u32, + #[codec(compact)] + max_capacity: u32, + }, + + /// A message to notify about that a previously sent open channel request has been accepted by + /// the recipient. That means that the channel will be opened during the next relay-chain + /// session change. This message is meant to be sent by the relay-chain to a para. + /// + /// Safety: The message should originate directly from the relay-chain. + /// + /// Kind: *System Notification* + /// + /// Errors: + HrmpChannelAccepted { + // NOTE: We keep this as a structured item to a) keep it consistent with the other Hrmp + // items; and b) because the field's meaning is not obvious/mentioned from the item name. + #[codec(compact)] + recipient: u32, + }, + + /// A message to notify that the other party in an open channel decided to close it. In + /// particular, `initiator` is going to close the channel opened from `sender` to the + /// `recipient`. The close will be enacted at the next relay-chain session change. This message + /// is meant to be sent by the relay-chain to a para. + /// + /// Safety: The message should originate directly from the relay-chain. + /// + /// Kind: *System Notification* + /// + /// Errors: + HrmpChannelClosing { + #[codec(compact)] + initiator: u32, + #[codec(compact)] + sender: u32, + #[codec(compact)] + recipient: u32, + }, + + /// Clear the origin. + /// + /// This may be used by the XCM author to ensure that later instructions cannot command the + /// authority of the origin (e.g. if they are being relayed from an untrusted source, as often + /// the case with `ReserveAssetDeposited`). + /// + /// Safety: No concerns. + /// + /// Kind: *Command*. + /// + /// Errors: + ClearOrigin, + + /// Mutate the origin to some interior location. + /// + /// Kind: *Command* + /// + /// Errors: + DescendOrigin(InteriorLocation), + + /// Immediately report the contents of the Error Register to the given destination via XCM. + /// + /// A `QueryResponse` message of type `ExecutionOutcome` is sent to the described destination. + /// + /// - `response_info`: Information for making the response. + /// + /// Kind: *Command* + /// + /// Errors: + ReportError(QueryResponseInfo), + + /// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets under + /// the ownership of `beneficiary` within this consensus system. + /// + /// - `assets`: The asset(s) to remove from holding. + /// - `beneficiary`: The new owner for the assets. + /// + /// Kind: *Command* + /// + /// Errors: + DepositAsset { assets: AssetFilter, beneficiary: Location }, + + /// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets under + /// the ownership of `dest` within this consensus system (i.e. deposit them into its sovereign + /// account). + /// + /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`. + /// + /// - `assets`: The asset(s) to remove from holding. + /// - `dest`: The location whose sovereign account will own the assets and thus the effective + /// beneficiary for the assets and the notification target for the reserve asset deposit + /// message. + /// - `xcm`: The orders that should follow the `ReserveAssetDeposited` instruction which is + /// sent onwards to `dest`. + /// + /// Kind: *Command* + /// + /// Errors: + DepositReserveAsset { assets: AssetFilter, dest: Location, xcm: Xcm<()> }, + + /// Remove the asset(s) (`want`) from the Holding Register and replace them with alternative + /// assets. + /// + /// The minimum amount of assets to be received into the Holding Register for the order not to + /// fail may be stated. + /// + /// - `give`: The maximum amount of assets to remove from holding. + /// - `want`: The minimum amount of assets which `give` should be exchanged for. + /// - `maximal`: If `true`, then prefer to give as much as possible up to the limit of `give` + /// and receive accordingly more. If `false`, then prefer to give as little as possible in + /// order to receive as little as possible while receiving at least `want`. + /// + /// Kind: *Command* + /// + /// Errors: + ExchangeAsset { give: AssetFilter, want: Assets, maximal: bool }, + + /// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a + /// reserve location. + /// + /// - `assets`: The asset(s) to remove from holding. + /// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The + /// sovereign account of this consensus system *on the reserve location* will have + /// appropriate assets withdrawn and `effects` will be executed on them. There will typically + /// be only one valid location on any given asset/chain combination. + /// - `xcm`: The instructions to execute on the assets once withdrawn *on the reserve + /// location*. + /// + /// Kind: *Command* + /// + /// Errors: + InitiateReserveWithdraw { assets: AssetFilter, reserve: Location, xcm: Xcm<()> }, + + /// Remove the asset(s) (`assets`) from holding and send a `ReceiveTeleportedAsset` XCM message + /// to a `dest` location. + /// + /// - `assets`: The asset(s) to remove from holding. + /// - `dest`: A valid location that respects teleports coming from this location. + /// - `xcm`: The instructions to execute on the assets once arrived *on the destination + /// location*. + /// + /// NOTE: The `dest` location *MUST* respect this origin as a valid teleportation origin for + /// all `assets`. If it does not, then the assets may be lost. + /// + /// Kind: *Command* + /// + /// Errors: + InitiateTeleport { assets: AssetFilter, dest: Location, xcm: Xcm<()> }, + + /// Report to a given destination the contents of the Holding Register. + /// + /// A `QueryResponse` message of type `Assets` is sent to the described destination. + /// + /// - `response_info`: Information for making the response. + /// - `assets`: A filter for the assets that should be reported back. The assets reported back + /// will be, asset-wise, *the lesser of this value and the holding register*. No wildcards + /// will be used when reporting assets back. + /// + /// Kind: *Command* + /// + /// Errors: + ReportHolding { response_info: QueryResponseInfo, assets: AssetFilter }, + + /// Pay for the execution of some XCM `xcm` and `orders` with up to `weight` + /// picoseconds of execution time, paying for this with up to `fees` from the Holding Register. + /// + /// - `fees`: The asset(s) to remove from the Holding Register to pay for fees. + /// - `weight_limit`: The maximum amount of weight to purchase; this must be at least the + /// expected maximum weight of the total XCM to be executed for the + /// `AllowTopLevelPaidExecutionFrom` barrier to allow the XCM be executed. + /// + /// Kind: *Command* + /// + /// Errors: + #[builder(pays_fees)] + BuyExecution { fees: Asset, weight_limit: WeightLimit }, + + /// Refund any surplus weight previously bought with `BuyExecution`. + /// + /// Kind: *Command* + /// + /// Errors: None. + RefundSurplus, + + /// Set the Error Handler Register. This is code that should be called in the case of an error + /// happening. + /// + /// An error occurring within execution of this code will _NOT_ result in the error register + /// being set, nor will an error handler be called due to it. The error handler and appendix + /// may each still be set. + /// + /// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing + /// weight however includes only the difference between the previous handler and the new + /// handler, which can reasonably be negative, which would result in a surplus. + /// + /// Kind: *Command* + /// + /// Errors: None. + SetErrorHandler(Xcm), + + /// Set the Appendix Register. This is code that should be called after code execution + /// (including the error handler if any) is finished. This will be called regardless of whether + /// an error occurred. + /// + /// Any error occurring due to execution of this code will result in the error register being + /// set, and the error handler (if set) firing. + /// + /// The apparent weight of this instruction is inclusive of the inner `Xcm`; the executing + /// weight however includes only the difference between the previous appendix and the new + /// appendix, which can reasonably be negative, which would result in a surplus. + /// + /// Kind: *Command* + /// + /// Errors: None. + SetAppendix(Xcm), + + /// Clear the Error Register. + /// + /// Kind: *Command* + /// + /// Errors: None. + ClearError, + + /// Set asset claimer for all the trapped assets during the execution. + /// + /// - `location`: The claimer of any assets potentially trapped during the execution of current + /// XCM. It can be an arbitrary location, not necessarily the caller or origin. + /// + /// Kind: *Command* + /// + /// Errors: None. + SetAssetClaimer { location: Location }, + /// Create some assets which are being held on behalf of the origin. + /// + /// - `assets`: The assets which are to be claimed. This must match exactly with the assets + /// claimable by the origin of the ticket. + /// - `ticket`: The ticket of the asset; this is an abstract identifier to help locate the + /// asset. + /// + /// Kind: *Command* + /// + /// Errors: + #[builder(loads_holding)] + ClaimAsset { assets: Assets, ticket: Location }, + + /// Always throws an error of type `Trap`. + /// + /// Kind: *Command* + /// + /// Errors: + /// - `Trap`: All circumstances, whose inner value is the same as this item's inner value. + Trap(#[codec(compact)] u64), + + /// Ask the destination system to respond with the most recent version of XCM that they + /// support in a `QueryResponse` instruction. Any changes to this should also elicit similar + /// responses when they happen. + /// + /// - `query_id`: An identifier that will be replicated into the returned XCM message. + /// - `max_response_weight`: The maximum amount of weight that the `QueryResponse` item which + /// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the + /// response may not execute at all. + /// + /// Kind: *Command* + /// + /// Errors: *Fallible* + SubscribeVersion { + #[codec(compact)] + query_id: QueryId, + max_response_weight: Weight, + }, + + /// Cancel the effect of a previous `SubscribeVersion` instruction. + /// + /// Kind: *Command* + /// + /// Errors: *Fallible* + UnsubscribeVersion, + + /// Reduce Holding by up to the given assets. + /// + /// Holding is reduced by as much as possible up to the assets in the parameter. It is not an + /// error if the Holding does not contain the assets (to make this an error, use `ExpectAsset` + /// prior). + /// + /// Kind: *Command* + /// + /// Errors: *Infallible* + BurnAsset(Assets), + + /// Throw an error if Holding does not contain at least the given assets. + /// + /// Kind: *Command* + /// + /// Errors: + /// - `ExpectationFalse`: If Holding Register does not contain the assets in the parameter. + ExpectAsset(Assets), + + /// Ensure that the Origin Register equals some given value and throw an error if not. + /// + /// Kind: *Command* + /// + /// Errors: + /// - `ExpectationFalse`: If Origin Register is not equal to the parameter. + ExpectOrigin(Option), + + /// Ensure that the Error Register equals some given value and throw an error if not. + /// + /// Kind: *Command* + /// + /// Errors: + /// - `ExpectationFalse`: If the value of the Error Register is not equal to the parameter. + ExpectError(Option<(u32, Error)>), + + /// Ensure that the Transact Status Register equals some given value and throw an error if + /// not. + /// + /// Kind: *Command* + /// + /// Errors: + /// - `ExpectationFalse`: If the value of the Transact Status Register is not equal to the + /// parameter. + ExpectTransactStatus(MaybeErrorCode), + + /// Query the existence of a particular pallet type. + /// + /// - `module_name`: The module name of the pallet to query. + /// - `response_info`: Information for making the response. + /// + /// Sends a `QueryResponse` to Origin whose data field `PalletsInfo` containing the information + /// of all pallets on the local chain whose name is equal to `name`. This is empty in the case + /// that the local chain is not based on Substrate Frame. + /// + /// Safety: No concerns. + /// + /// Kind: *Command* + /// + /// Errors: *Fallible*. + QueryPallet { module_name: Vec, response_info: QueryResponseInfo }, + + /// Ensure that a particular pallet with a particular version exists. + /// + /// - `index: Compact`: The index which identifies the pallet. An error if no pallet exists at + /// this index. + /// - `name: Vec`: Name which must be equal to the name of the pallet. + /// - `module_name: Vec`: Module name which must be equal to the name of the module in + /// which the pallet exists. + /// - `crate_major: Compact`: Version number which must be equal to the major version of the + /// crate which implements the pallet. + /// - `min_crate_minor: Compact`: Version number which must be at most the minor version of the + /// crate which implements the pallet. + /// + /// Safety: No concerns. + /// + /// Kind: *Command* + /// + /// Errors: + /// - `ExpectationFalse`: In case any of the expectations are broken. + ExpectPallet { + #[codec(compact)] + index: u32, + name: Vec, + module_name: Vec, + #[codec(compact)] + crate_major: u32, + #[codec(compact)] + min_crate_minor: u32, + }, + + /// Send a `QueryResponse` message containing the value of the Transact Status Register to some + /// destination. + /// + /// - `query_response_info`: The information needed for constructing and sending the + /// `QueryResponse` message. + /// + /// Safety: No concerns. + /// + /// Kind: *Command* + /// + /// Errors: *Fallible*. + ReportTransactStatus(QueryResponseInfo), + + /// Set the Transact Status Register to its default, cleared, value. + /// + /// Safety: No concerns. + /// + /// Kind: *Command* + /// + /// Errors: *Infallible*. + ClearTransactStatus, + + /// Set the Origin Register to be some child of the Universal Ancestor. + /// + /// Safety: Should only be usable if the Origin is trusted to represent the Universal Ancestor + /// child in general. In general, no Origin should be able to represent the Universal Ancestor + /// child which is the root of the local consensus system since it would by extension + /// allow it to act as any location within the local consensus. + /// + /// The `Junction` parameter should generally be a `GlobalConsensus` variant since it is only + /// these which are children of the Universal Ancestor. + /// + /// Kind: *Command* + /// + /// Errors: *Fallible*. + UniversalOrigin(Junction), + + /// Send a message on to Non-Local Consensus system. + /// + /// This will tend to utilize some extra-consensus mechanism, the obvious one being a bridge. + /// A fee may be charged; this may be determined based on the contents of `xcm`. It will be + /// taken from the Holding register. + /// + /// - `network`: The remote consensus system to which the message should be exported. + /// - `destination`: The location relative to the remote consensus system to which the message + /// should be sent on arrival. + /// - `xcm`: The message to be exported. + /// + /// As an example, to export a message for execution on Statemine (parachain #1000 in the + /// Kusama network), you would call with `network: NetworkId::Kusama` and + /// `destination: [Parachain(1000)].into()`. Alternatively, to export a message for execution + /// on Polkadot, you would call with `network: NetworkId:: Polkadot` and `destination: Here`. + /// + /// Kind: *Command* + /// + /// Errors: *Fallible*. + ExportMessage { network: NetworkId, destination: InteriorLocation, xcm: Xcm<()> }, + + /// Lock the locally held asset and prevent further transfer or withdrawal. + /// + /// This restriction may be removed by the `UnlockAsset` instruction being called with an + /// Origin of `unlocker` and a `target` equal to the current `Origin`. + /// + /// If the locking is successful, then a `NoteUnlockable` instruction is sent to `unlocker`. + /// + /// - `asset`: The asset(s) which should be locked. + /// - `unlocker`: The value which the Origin must be for a corresponding `UnlockAsset` + /// instruction to work. + /// + /// Kind: *Command*. + /// + /// Errors: + LockAsset { asset: Asset, unlocker: Location }, + + /// Remove the lock over `asset` on this chain and (if nothing else is preventing it) allow the + /// asset to be transferred. + /// + /// - `asset`: The asset to be unlocked. + /// - `target`: The owner of the asset on the local chain. + /// + /// Safety: No concerns. + /// + /// Kind: *Command*. + /// + /// Errors: + UnlockAsset { asset: Asset, target: Location }, + + /// Asset (`asset`) has been locked on the `origin` system and may not be transferred. It may + /// only be unlocked with the receipt of the `UnlockAsset` instruction from this chain. + /// + /// - `asset`: The asset(s) which are now unlockable from this origin. + /// - `owner`: The owner of the asset on the chain in which it was locked. This may be a + /// location specific to the origin network. + /// + /// Safety: `origin` must be trusted to have locked the corresponding `asset` + /// prior as a consequence of sending this message. + /// + /// Kind: *Trusted Indication*. + /// + /// Errors: + NoteUnlockable { asset: Asset, owner: Location }, + + /// Send an `UnlockAsset` instruction to the `locker` for the given `asset`. + /// + /// This may fail if the local system is making use of the fact that the asset is locked or, + /// of course, if there is no record that the asset actually is locked. + /// + /// - `asset`: The asset(s) to be unlocked. + /// - `locker`: The location from which a previous `NoteUnlockable` was sent and to which an + /// `UnlockAsset` should be sent. + /// + /// Kind: *Command*. + /// + /// Errors: + RequestUnlock { asset: Asset, locker: Location }, + + /// Sets the Fees Mode Register. + /// + /// - `jit_withdraw`: The fees mode item; if set to `true` then fees for any instructions are + /// withdrawn as needed using the same mechanism as `WithdrawAssets`. + /// + /// Kind: *Command*. + /// + /// Errors: + SetFeesMode { jit_withdraw: bool }, + + /// Set the Topic Register. + /// + /// The 32-byte array identifier in the parameter is not guaranteed to be + /// unique; if such a property is desired, it is up to the code author to + /// enforce uniqueness. + /// + /// Safety: No concerns. + /// + /// Kind: *Command* + /// + /// Errors: + SetTopic([u8; 32]), + + /// Clear the Topic Register. + /// + /// Kind: *Command* + /// + /// Errors: None. + ClearTopic, + + /// Alter the current Origin to another given origin. + /// + /// Kind: *Command* + /// + /// Errors: If the existing state would not allow such a change. + AliasOrigin(Location), + + /// A directive to indicate that the origin expects free execution of the message. + /// + /// At execution time, this instruction just does a check on the Origin register. + /// However, at the barrier stage messages starting with this instruction can be disregarded if + /// the origin is not acceptable for free execution or the `weight_limit` is `Limited` and + /// insufficient. + /// + /// Kind: *Indication* + /// + /// Errors: If the given origin is `Some` and not equal to the current Origin register. + UnpaidExecution { weight_limit: WeightLimit, check_origin: Option }, + + /// Pay Fees. + /// + /// Successor to `BuyExecution`. + /// Defined in fellowship RFC 105. + #[builder(pays_fees)] + PayFees { asset: Asset }, + + /// Initiates cross-chain transfer as follows: + /// + /// Assets in the holding register are matched using the given list of `AssetTransferFilter`s, + /// they are then transferred based on their specified transfer type: + /// + /// - teleport: burn local assets and append a `ReceiveTeleportedAsset` XCM instruction to the + /// XCM program to be sent onward to the `destination` location, + /// + /// - reserve deposit: place assets under the ownership of `destination` within this consensus + /// system (i.e. its sovereign account), and append a `ReserveAssetDeposited` XCM instruction + /// to the XCM program to be sent onward to the `destination` location, + /// + /// - reserve withdraw: burn local assets and append a `WithdrawAsset` XCM instruction to the + /// XCM program to be sent onward to the `destination` location, + /// + /// The onward XCM is then appended a `ClearOrigin` to allow safe execution of any following + /// custom XCM instructions provided in `remote_xcm`. + /// + /// The onward XCM also contains either a `PayFees` or `UnpaidExecution` instruction based + /// on the presence of the `remote_fees` parameter (see below). + /// + /// If an XCM program requires going through multiple hops, it can compose this instruction to + /// be used at every chain along the path, describing that specific leg of the flow. + /// + /// Parameters: + /// - `destination`: The location of the program next hop. + /// - `remote_fees`: If set to `Some(asset_xfer_filter)`, the single asset matching + /// `asset_xfer_filter` in the holding register will be transferred first in the remote XCM + /// program, followed by a `PayFees(fee)`, then rest of transfers follow. This guarantees + /// `remote_xcm` will successfully pass a `AllowTopLevelPaidExecutionFrom` barrier. If set to + /// `None`, a `UnpaidExecution` instruction is appended instead. Please note that these + /// assets are **reserved** for fees, they are sent to the fees register rather than holding. + /// Best practice is to only add here enough to cover fees, and transfer the rest through the + /// `assets` parameter. + /// - `preserve_origin`: Specifies whether the original origin should be preserved or cleared, + /// using the instructions `AliasOrigin` or `ClearOrigin` respectively. + /// - `assets`: List of asset filters matched against existing assets in holding. These are + /// transferred over to `destination` using the specified transfer type, and deposited to + /// holding on `destination`. + /// - `remote_xcm`: Custom instructions that will be executed on the `destination` chain. Note + /// that these instructions will be executed after a `ClearOrigin` so their origin will be + /// `None`. + /// + /// Safety: No concerns. + /// + /// Kind: *Command* + InitiateTransfer { + destination: Location, + remote_fees: Option, + preserve_origin: bool, + assets: Vec, + remote_xcm: Xcm<()>, + }, +} + +impl Xcm { + pub fn into(self) -> Xcm { + Xcm::from(self) + } + pub fn from(xcm: Xcm) -> Self { + Self(xcm.0.into_iter().map(Instruction::::from).collect()) + } +} + +impl Instruction { + pub fn into(self) -> Instruction { + Instruction::from(self) + } + pub fn from(xcm: Instruction) -> Self { + use Instruction::*; + match xcm { + WithdrawAsset(assets) => WithdrawAsset(assets), + ReserveAssetDeposited(assets) => ReserveAssetDeposited(assets), + ReceiveTeleportedAsset(assets) => ReceiveTeleportedAsset(assets), + QueryResponse { query_id, response, max_weight, querier } => + QueryResponse { query_id, response, max_weight, querier }, + TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary }, + TransferReserveAsset { assets, dest, xcm } => + TransferReserveAsset { assets, dest, xcm }, + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, + HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, + HrmpChannelClosing { initiator, sender, recipient } => + HrmpChannelClosing { initiator, sender, recipient }, + Transact { origin_kind, call } => Transact { origin_kind, call: call.into() }, + ReportError(response_info) => ReportError(response_info), + DepositAsset { assets, beneficiary } => DepositAsset { assets, beneficiary }, + DepositReserveAsset { assets, dest, xcm } => DepositReserveAsset { assets, dest, xcm }, + ExchangeAsset { give, want, maximal } => ExchangeAsset { give, want, maximal }, + InitiateReserveWithdraw { assets, reserve, xcm } => + InitiateReserveWithdraw { assets, reserve, xcm }, + InitiateTeleport { assets, dest, xcm } => InitiateTeleport { assets, dest, xcm }, + ReportHolding { response_info, assets } => ReportHolding { response_info, assets }, + BuyExecution { fees, weight_limit } => BuyExecution { fees, weight_limit }, + ClearOrigin => ClearOrigin, + DescendOrigin(who) => DescendOrigin(who), + RefundSurplus => RefundSurplus, + SetErrorHandler(xcm) => SetErrorHandler(xcm.into()), + SetAppendix(xcm) => SetAppendix(xcm.into()), + ClearError => ClearError, + SetAssetClaimer { location } => SetAssetClaimer { location }, + ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket }, + Trap(code) => Trap(code), + SubscribeVersion { query_id, max_response_weight } => + SubscribeVersion { query_id, max_response_weight }, + UnsubscribeVersion => UnsubscribeVersion, + BurnAsset(assets) => BurnAsset(assets), + ExpectAsset(assets) => ExpectAsset(assets), + ExpectOrigin(origin) => ExpectOrigin(origin), + ExpectError(error) => ExpectError(error), + ExpectTransactStatus(transact_status) => ExpectTransactStatus(transact_status), + QueryPallet { module_name, response_info } => + QueryPallet { module_name, response_info }, + ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => + ExpectPallet { index, name, module_name, crate_major, min_crate_minor }, + ReportTransactStatus(response_info) => ReportTransactStatus(response_info), + ClearTransactStatus => ClearTransactStatus, + UniversalOrigin(j) => UniversalOrigin(j), + ExportMessage { network, destination, xcm } => + ExportMessage { network, destination, xcm }, + LockAsset { asset, unlocker } => LockAsset { asset, unlocker }, + UnlockAsset { asset, target } => UnlockAsset { asset, target }, + NoteUnlockable { asset, owner } => NoteUnlockable { asset, owner }, + RequestUnlock { asset, locker } => RequestUnlock { asset, locker }, + SetFeesMode { jit_withdraw } => SetFeesMode { jit_withdraw }, + SetTopic(topic) => SetTopic(topic), + ClearTopic => ClearTopic, + AliasOrigin(location) => AliasOrigin(location), + UnpaidExecution { weight_limit, check_origin } => + UnpaidExecution { weight_limit, check_origin }, + PayFees { asset } => PayFees { asset }, + InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } => + InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm }, + } + } +} + +// TODO: Automate Generation +impl> GetWeight for Instruction { + fn weight(&self) -> Weight { + use Instruction::*; + match self { + WithdrawAsset(assets) => W::withdraw_asset(assets), + ReserveAssetDeposited(assets) => W::reserve_asset_deposited(assets), + ReceiveTeleportedAsset(assets) => W::receive_teleported_asset(assets), + QueryResponse { query_id, response, max_weight, querier } => + W::query_response(query_id, response, max_weight, querier), + TransferAsset { assets, beneficiary } => W::transfer_asset(assets, beneficiary), + TransferReserveAsset { assets, dest, xcm } => + W::transfer_reserve_asset(&assets, dest, xcm), + Transact { origin_kind, call } => W::transact(origin_kind, call), + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity), + HrmpChannelAccepted { recipient } => W::hrmp_channel_accepted(recipient), + HrmpChannelClosing { initiator, sender, recipient } => + W::hrmp_channel_closing(initiator, sender, recipient), + ClearOrigin => W::clear_origin(), + DescendOrigin(who) => W::descend_origin(who), + ReportError(response_info) => W::report_error(&response_info), + DepositAsset { assets, beneficiary } => W::deposit_asset(assets, beneficiary), + DepositReserveAsset { assets, dest, xcm } => + W::deposit_reserve_asset(assets, dest, xcm), + ExchangeAsset { give, want, maximal } => W::exchange_asset(give, want, maximal), + InitiateReserveWithdraw { assets, reserve, xcm } => + W::initiate_reserve_withdraw(assets, reserve, xcm), + InitiateTeleport { assets, dest, xcm } => W::initiate_teleport(assets, dest, xcm), + ReportHolding { response_info, assets } => W::report_holding(&response_info, &assets), + BuyExecution { fees, weight_limit } => W::buy_execution(fees, weight_limit), + RefundSurplus => W::refund_surplus(), + SetErrorHandler(xcm) => W::set_error_handler(xcm), + SetAppendix(xcm) => W::set_appendix(xcm), + ClearError => W::clear_error(), + SetAssetClaimer { location } => W::set_asset_claimer(location), + ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket), + Trap(code) => W::trap(code), + SubscribeVersion { query_id, max_response_weight } => + W::subscribe_version(query_id, max_response_weight), + UnsubscribeVersion => W::unsubscribe_version(), + BurnAsset(assets) => W::burn_asset(assets), + ExpectAsset(assets) => W::expect_asset(assets), + ExpectOrigin(origin) => W::expect_origin(origin), + ExpectError(error) => W::expect_error(error), + ExpectTransactStatus(transact_status) => W::expect_transact_status(transact_status), + QueryPallet { module_name, response_info } => + W::query_pallet(module_name, response_info), + ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => + W::expect_pallet(index, name, module_name, crate_major, min_crate_minor), + ReportTransactStatus(response_info) => W::report_transact_status(response_info), + ClearTransactStatus => W::clear_transact_status(), + UniversalOrigin(j) => W::universal_origin(j), + ExportMessage { network, destination, xcm } => + W::export_message(network, destination, xcm), + LockAsset { asset, unlocker } => W::lock_asset(asset, unlocker), + UnlockAsset { asset, target } => W::unlock_asset(asset, target), + NoteUnlockable { asset, owner } => W::note_unlockable(asset, owner), + RequestUnlock { asset, locker } => W::request_unlock(asset, locker), + SetFeesMode { jit_withdraw } => W::set_fees_mode(jit_withdraw), + SetTopic(topic) => W::set_topic(topic), + ClearTopic => W::clear_topic(), + AliasOrigin(location) => W::alias_origin(location), + UnpaidExecution { weight_limit, check_origin } => + W::unpaid_execution(weight_limit, check_origin), + PayFees { asset } => W::pay_fees(asset), + InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } => + W::initiate_transfer(destination, remote_fees, preserve_origin, assets, remote_xcm), + } + } +} + +pub mod opaque { + /// The basic concrete type of `Xcm`, which doesn't make any assumptions about the + /// format of a call other than it is pre-encoded. + pub type Xcm = super::Xcm<()>; + + /// The basic concrete type of `Instruction`, which doesn't make any assumptions about the + /// format of a call other than it is pre-encoded. + pub type Instruction = super::Instruction<()>; +} + +// Convert from a v4 XCM to a v5 XCM +impl TryFrom> for Xcm { + type Error = (); + fn try_from(old_xcm: OldXcm) -> result::Result { + Ok(Xcm(old_xcm.0.into_iter().map(TryInto::try_into).collect::>()?)) + } +} + +// Convert from a v4 instruction to a v5 instruction +impl TryFrom> for Instruction { + type Error = (); + fn try_from(old_instruction: OldInstruction) -> result::Result { + use OldInstruction::*; + Ok(match old_instruction { + WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?), + ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?), + ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?), + QueryResponse { query_id, response, max_weight, querier: Some(querier) } => + Self::QueryResponse { + query_id, + querier: querier.try_into()?, + response: response.try_into()?, + max_weight, + }, + QueryResponse { query_id, response, max_weight, querier: None } => + Self::QueryResponse { + query_id, + querier: None, + response: response.try_into()?, + max_weight, + }, + TransferAsset { assets, beneficiary } => Self::TransferAsset { + assets: assets.try_into()?, + beneficiary: beneficiary.try_into()?, + }, + TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset { + assets: assets.try_into()?, + dest: dest.try_into()?, + xcm: xcm.try_into()?, + }, + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, + HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient }, + HrmpChannelClosing { initiator, sender, recipient } => + Self::HrmpChannelClosing { initiator, sender, recipient }, + Transact { origin_kind, require_weight_at_most: _, call } => + Self::Transact { origin_kind, call: call.into() }, + ReportError(response_info) => Self::ReportError(QueryResponseInfo { + query_id: response_info.query_id, + destination: response_info.destination.try_into().map_err(|_| ())?, + max_weight: response_info.max_weight, + }), + DepositAsset { assets, beneficiary } => { + let beneficiary = beneficiary.try_into()?; + let assets = assets.try_into()?; + Self::DepositAsset { assets, beneficiary } + }, + DepositReserveAsset { assets, dest, xcm } => { + let dest = dest.try_into()?; + let xcm = xcm.try_into()?; + let assets = assets.try_into()?; + Self::DepositReserveAsset { assets, dest, xcm } + }, + ExchangeAsset { give, want, maximal } => { + let give = give.try_into()?; + let want = want.try_into()?; + Self::ExchangeAsset { give, want, maximal } + }, + InitiateReserveWithdraw { assets, reserve, xcm } => { + let assets = assets.try_into()?; + let reserve = reserve.try_into()?; + let xcm = xcm.try_into()?; + Self::InitiateReserveWithdraw { assets, reserve, xcm } + }, + InitiateTeleport { assets, dest, xcm } => { + let assets = assets.try_into()?; + let dest = dest.try_into()?; + let xcm = xcm.try_into()?; + Self::InitiateTeleport { assets, dest, xcm } + }, + ReportHolding { response_info, assets } => { + let response_info = QueryResponseInfo { + destination: response_info.destination.try_into().map_err(|_| ())?, + query_id: response_info.query_id, + max_weight: response_info.max_weight, + }; + Self::ReportHolding { response_info, assets: assets.try_into()? } + }, + BuyExecution { fees, weight_limit } => { + let fees = fees.try_into()?; + let weight_limit = weight_limit.into(); + Self::BuyExecution { fees, weight_limit } + }, + ClearOrigin => Self::ClearOrigin, + DescendOrigin(who) => Self::DescendOrigin(who.try_into()?), + RefundSurplus => Self::RefundSurplus, + SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?), + SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?), + ClearError => Self::ClearError, + ClaimAsset { assets, ticket } => { + let assets = assets.try_into()?; + let ticket = ticket.try_into()?; + Self::ClaimAsset { assets, ticket } + }, + Trap(code) => Self::Trap(code), + SubscribeVersion { query_id, max_response_weight } => + Self::SubscribeVersion { query_id, max_response_weight }, + UnsubscribeVersion => Self::UnsubscribeVersion, + BurnAsset(assets) => Self::BurnAsset(assets.try_into()?), + ExpectAsset(assets) => Self::ExpectAsset(assets.try_into()?), + ExpectOrigin(maybe_location) => Self::ExpectOrigin( + maybe_location.map(|location| location.try_into()).transpose().map_err(|_| ())?, + ), + ExpectError(maybe_error) => Self::ExpectError( + maybe_error + .map(|(num, old_error)| (num, old_error.try_into())) + .map(|(num, result)| result.map(|inner| (num, inner))) + .transpose() + .map_err(|_| ())?, + ), + ExpectTransactStatus(maybe_error_code) => Self::ExpectTransactStatus(maybe_error_code), + QueryPallet { module_name, response_info } => Self::QueryPallet { + module_name, + response_info: response_info.try_into().map_err(|_| ())?, + }, + ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => + Self::ExpectPallet { index, name, module_name, crate_major, min_crate_minor }, + ReportTransactStatus(response_info) => + Self::ReportTransactStatus(response_info.try_into().map_err(|_| ())?), + ClearTransactStatus => Self::ClearTransactStatus, + UniversalOrigin(junction) => + Self::UniversalOrigin(junction.try_into().map_err(|_| ())?), + ExportMessage { network, destination, xcm } => Self::ExportMessage { + network: network.into(), + destination: destination.try_into().map_err(|_| ())?, + xcm: xcm.try_into().map_err(|_| ())?, + }, + LockAsset { asset, unlocker } => Self::LockAsset { + asset: asset.try_into().map_err(|_| ())?, + unlocker: unlocker.try_into().map_err(|_| ())?, + }, + UnlockAsset { asset, target } => Self::UnlockAsset { + asset: asset.try_into().map_err(|_| ())?, + target: target.try_into().map_err(|_| ())?, + }, + NoteUnlockable { asset, owner } => Self::NoteUnlockable { + asset: asset.try_into().map_err(|_| ())?, + owner: owner.try_into().map_err(|_| ())?, + }, + RequestUnlock { asset, locker } => Self::RequestUnlock { + asset: asset.try_into().map_err(|_| ())?, + locker: locker.try_into().map_err(|_| ())?, + }, + SetFeesMode { jit_withdraw } => Self::SetFeesMode { jit_withdraw }, + SetTopic(topic) => Self::SetTopic(topic), + ClearTopic => Self::ClearTopic, + AliasOrigin(location) => Self::AliasOrigin(location.try_into().map_err(|_| ())?), + UnpaidExecution { weight_limit, check_origin } => Self::UnpaidExecution { + weight_limit, + check_origin: check_origin + .map(|location| location.try_into()) + .transpose() + .map_err(|_| ())?, + }, + }) + } +} + +#[cfg(test)] +mod tests { + use super::{prelude::*, *}; + use crate::v4::{ + AssetFilter as OldAssetFilter, Junctions::Here as OldHere, WildAsset as OldWildAsset, + }; + + #[test] + fn basic_roundtrip_works() { + let xcm = Xcm::<()>(vec![TransferAsset { + assets: (Here, 1u128).into(), + beneficiary: Here.into(), + }]); + let old_xcm = OldXcm::<()>(vec![OldInstruction::TransferAsset { + assets: (OldHere, 1u128).into(), + beneficiary: OldHere.into(), + }]); + assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); + let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); + assert_eq!(new_xcm, xcm); + } + + #[test] + fn teleport_roundtrip_works() { + let xcm = Xcm::<()>(vec![ + ReceiveTeleportedAsset((Here, 1u128).into()), + ClearOrigin, + DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, + ]); + let old_xcm: OldXcm<()> = OldXcm::<()>(vec![ + OldInstruction::ReceiveTeleportedAsset((OldHere, 1u128).into()), + OldInstruction::ClearOrigin, + OldInstruction::DepositAsset { + assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)), + beneficiary: OldHere.into(), + }, + ]); + assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); + let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); + assert_eq!(new_xcm, xcm); + } + + #[test] + fn reserve_deposit_roundtrip_works() { + let xcm = Xcm::<()>(vec![ + ReserveAssetDeposited((Here, 1u128).into()), + ClearOrigin, + BuyExecution { + fees: (Here, 1u128).into(), + weight_limit: Some(Weight::from_parts(1, 1)).into(), + }, + DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, + ]); + let old_xcm = OldXcm::<()>(vec![ + OldInstruction::ReserveAssetDeposited((OldHere, 1u128).into()), + OldInstruction::ClearOrigin, + OldInstruction::BuyExecution { + fees: (OldHere, 1u128).into(), + weight_limit: WeightLimit::Limited(Weight::from_parts(1, 1)), + }, + OldInstruction::DepositAsset { + assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)), + beneficiary: OldHere.into(), + }, + ]); + assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); + let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); + assert_eq!(new_xcm, xcm); + } + + #[test] + fn deposit_asset_roundtrip_works() { + let xcm = Xcm::<()>(vec![ + WithdrawAsset((Here, 1u128).into()), + DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() }, + ]); + let old_xcm = OldXcm::<()>(vec![ + OldInstruction::WithdrawAsset((OldHere, 1u128).into()), + OldInstruction::DepositAsset { + assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)), + beneficiary: OldHere.into(), + }, + ]); + assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); + let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); + assert_eq!(new_xcm, xcm); + } + + #[test] + fn deposit_reserve_asset_roundtrip_works() { + let xcm = Xcm::<()>(vec![ + WithdrawAsset((Here, 1u128).into()), + DepositReserveAsset { + assets: Wild(AllCounted(1)), + dest: Here.into(), + xcm: Xcm::<()>(vec![]), + }, + ]); + let old_xcm = OldXcm::<()>(vec![ + OldInstruction::WithdrawAsset((OldHere, 1u128).into()), + OldInstruction::DepositReserveAsset { + assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)), + dest: OldHere.into(), + xcm: OldXcm::<()>(vec![]), + }, + ]); + assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap()); + let new_xcm: Xcm<()> = old_xcm.try_into().unwrap(); + assert_eq!(new_xcm, xcm); + } + + #[test] + fn decoding_respects_limit() { + let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]); + let encoded = max_xcm.encode(); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok()); + + let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]); + let encoded = big_xcm.encode(); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); + + let nested_xcm = Xcm::<()>(vec![ + DepositReserveAsset { + assets: All.into(), + dest: Here.into(), + xcm: max_xcm, + }; + (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize + ]); + let encoded = nested_xcm.encode(); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); + + let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]); + let encoded = even_more_nested_xcm.encode(); + assert_eq!(encoded.len(), 342530); + // This should not decode since the limit is 100 + assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition"); + assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err()); + } +} diff --git a/polkadot/xcm/src/v5/traits.rs b/polkadot/xcm/src/v5/traits.rs new file mode 100644 index 0000000000000000000000000000000000000000..1f5041ca8d8422d7c3ead0f8c9e84124c4d7f5dd --- /dev/null +++ b/polkadot/xcm/src/v5/traits.rs @@ -0,0 +1,525 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Cross-Consensus Message format data structures. + +pub use crate::v3::{Error as OldError, SendError, XcmHash}; +use codec::{Decode, Encode}; +use core::result; +use scale_info::TypeInfo; + +pub use sp_weights::Weight; + +use super::*; + +/// Error codes used in XCM. The first errors codes have explicit indices and are part of the XCM +/// format. Those trailing are merely part of the XCM implementation; there is no expectation that +/// they will retain the same index over time. +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +#[scale_info(replace_segment("staging_xcm", "xcm"))] +#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))] +pub enum Error { + // Errors that happen due to instructions being executed. These alone are defined in the + // XCM specification. + /// An arithmetic overflow happened. + #[codec(index = 0)] + Overflow, + /// The instruction is intentionally unsupported. + #[codec(index = 1)] + Unimplemented, + /// Origin Register does not contain a value value for a reserve transfer notification. + #[codec(index = 2)] + UntrustedReserveLocation, + /// Origin Register does not contain a value value for a teleport notification. + #[codec(index = 3)] + UntrustedTeleportLocation, + /// `MultiLocation` value too large to descend further. + #[codec(index = 4)] + LocationFull, + /// `MultiLocation` value ascend more parents than known ancestors of local location. + #[codec(index = 5)] + LocationNotInvertible, + /// The Origin Register does not contain a valid value for instruction. + #[codec(index = 6)] + BadOrigin, + /// The location parameter is not a valid value for the instruction. + #[codec(index = 7)] + InvalidLocation, + /// The given asset is not handled. + #[codec(index = 8)] + AssetNotFound, + /// An asset transaction (like withdraw or deposit) failed (typically due to type conversions). + #[codec(index = 9)] + FailedToTransactAsset(#[codec(skip)] &'static str), + /// An asset cannot be withdrawn, potentially due to lack of ownership, availability or rights. + #[codec(index = 10)] + NotWithdrawable, + /// An asset cannot be deposited under the ownership of a particular location. + #[codec(index = 11)] + LocationCannotHold, + /// Attempt to send a message greater than the maximum supported by the transport protocol. + #[codec(index = 12)] + ExceedsMaxMessageSize, + /// The given message cannot be translated into a format supported by the destination. + #[codec(index = 13)] + DestinationUnsupported, + /// Destination is routable, but there is some issue with the transport mechanism. + #[codec(index = 14)] + Transport(#[codec(skip)] &'static str), + /// Destination is known to be unroutable. + #[codec(index = 15)] + Unroutable, + /// Used by `ClaimAsset` when the given claim could not be recognized/found. + #[codec(index = 16)] + UnknownClaim, + /// Used by `Transact` when the functor cannot be decoded. + #[codec(index = 17)] + FailedToDecode, + /// Used by `Transact` to indicate that the given weight limit could be breached by the + /// functor. + #[codec(index = 18)] + MaxWeightInvalid, + /// Used by `BuyExecution` when the Holding Register does not contain payable fees. + #[codec(index = 19)] + NotHoldingFees, + /// Used by `BuyExecution` when the fees declared to purchase weight are insufficient. + #[codec(index = 20)] + TooExpensive, + /// Used by the `Trap` instruction to force an error intentionally. Its code is included. + #[codec(index = 21)] + Trap(u64), + /// Used by `ExpectAsset`, `ExpectError` and `ExpectOrigin` when the expectation was not true. + #[codec(index = 22)] + ExpectationFalse, + /// The provided pallet index was not found. + #[codec(index = 23)] + PalletNotFound, + /// The given pallet's name is different to that expected. + #[codec(index = 24)] + NameMismatch, + /// The given pallet's version has an incompatible version to that expected. + #[codec(index = 25)] + VersionIncompatible, + /// The given operation would lead to an overflow of the Holding Register. + #[codec(index = 26)] + HoldingWouldOverflow, + /// The message was unable to be exported. + #[codec(index = 27)] + ExportError, + /// `MultiLocation` value failed to be reanchored. + #[codec(index = 28)] + ReanchorFailed, + /// No deal is possible under the given constraints. + #[codec(index = 29)] + NoDeal, + /// Fees were required which the origin could not pay. + #[codec(index = 30)] + FeesNotMet, + /// Some other error with locking. + #[codec(index = 31)] + LockError, + /// The state was not in a condition where the operation was valid to make. + #[codec(index = 32)] + NoPermission, + /// The universal location of the local consensus is improper. + #[codec(index = 33)] + Unanchored, + /// An asset cannot be deposited, probably because (too much of) it already exists. + #[codec(index = 34)] + NotDepositable, + /// Too many assets matched the given asset filter. + #[codec(index = 35)] + TooManyAssets, + + // Errors that happen prior to instructions being executed. These fall outside of the XCM + // spec. + /// XCM version not able to be handled. + UnhandledXcmVersion, + /// Execution of the XCM would potentially result in a greater weight used than weight limit. + WeightLimitReached(Weight), + /// The XCM did not pass the barrier condition for execution. + /// + /// The barrier condition differs on different chains and in different circumstances, but + /// generally it means that the conditions surrounding the message were not such that the chain + /// considers the message worth spending time executing. Since most chains lift the barrier to + /// execution on appropriate payment, presentation of an NFT voucher, or based on the message + /// origin, it means that none of those were the case. + Barrier, + /// The weight of an XCM message is not computable ahead of execution. + WeightNotComputable, + /// Recursion stack limit reached + // TODO(https://github.com/paritytech/polkadot-sdk/issues/6199): This should have a fixed index since + // we use it in `FrameTransactionalProcessor` // which is used in instructions. + // Or we should create a different error for that. + ExceedsStackLimit, +} + +impl TryFrom for Error { + type Error = (); + fn try_from(old_error: OldError) -> result::Result { + use OldError::*; + Ok(match old_error { + Overflow => Self::Overflow, + Unimplemented => Self::Unimplemented, + UntrustedReserveLocation => Self::UntrustedReserveLocation, + UntrustedTeleportLocation => Self::UntrustedTeleportLocation, + LocationFull => Self::LocationFull, + LocationNotInvertible => Self::LocationNotInvertible, + BadOrigin => Self::BadOrigin, + InvalidLocation => Self::InvalidLocation, + AssetNotFound => Self::AssetNotFound, + FailedToTransactAsset(s) => Self::FailedToTransactAsset(s), + NotWithdrawable => Self::NotWithdrawable, + LocationCannotHold => Self::LocationCannotHold, + ExceedsMaxMessageSize => Self::ExceedsMaxMessageSize, + DestinationUnsupported => Self::DestinationUnsupported, + Transport(s) => Self::Transport(s), + Unroutable => Self::Unroutable, + UnknownClaim => Self::UnknownClaim, + FailedToDecode => Self::FailedToDecode, + MaxWeightInvalid => Self::MaxWeightInvalid, + NotHoldingFees => Self::NotHoldingFees, + TooExpensive => Self::TooExpensive, + Trap(i) => Self::Trap(i), + ExpectationFalse => Self::ExpectationFalse, + PalletNotFound => Self::PalletNotFound, + NameMismatch => Self::NameMismatch, + VersionIncompatible => Self::VersionIncompatible, + HoldingWouldOverflow => Self::HoldingWouldOverflow, + ExportError => Self::ExportError, + ReanchorFailed => Self::ReanchorFailed, + NoDeal => Self::NoDeal, + FeesNotMet => Self::FeesNotMet, + LockError => Self::LockError, + NoPermission => Self::NoPermission, + Unanchored => Self::Unanchored, + NotDepositable => Self::NotDepositable, + UnhandledXcmVersion => Self::UnhandledXcmVersion, + WeightLimitReached(weight) => Self::WeightLimitReached(weight), + Barrier => Self::Barrier, + WeightNotComputable => Self::WeightNotComputable, + ExceedsStackLimit => Self::ExceedsStackLimit, + }) + } +} + +impl MaxEncodedLen for Error { + fn max_encoded_len() -> usize { + // TODO: max_encoded_len doesn't quite work here as it tries to take notice of the fields + // marked `codec(skip)`. We can hard-code it with the right answer for now. + 1 + } +} + +impl From for Error { + fn from(e: SendError) -> Self { + match e { + SendError::NotApplicable | SendError::Unroutable | SendError::MissingArgument => + Error::Unroutable, + SendError::Transport(s) => Error::Transport(s), + SendError::DestinationUnsupported => Error::DestinationUnsupported, + SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize, + SendError::Fees => Error::FeesNotMet, + } + } +} + +pub type Result = result::Result<(), Error>; + +/// Outcome of an XCM execution. +#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +pub enum Outcome { + /// Execution completed successfully; given weight was used. + Complete { used: Weight }, + /// Execution started, but did not complete successfully due to the given error; given weight + /// was used. + Incomplete { used: Weight, error: Error }, + /// Execution did not start due to the given error. + Error { error: Error }, +} + +impl Outcome { + pub fn ensure_complete(self) -> Result { + match self { + Outcome::Complete { .. } => Ok(()), + Outcome::Incomplete { error, .. } => Err(error), + Outcome::Error { error, .. } => Err(error), + } + } + pub fn ensure_execution(self) -> result::Result { + match self { + Outcome::Complete { used, .. } => Ok(used), + Outcome::Incomplete { used, .. } => Ok(used), + Outcome::Error { error, .. } => Err(error), + } + } + /// How much weight was used by the XCM execution attempt. + pub fn weight_used(&self) -> Weight { + match self { + Outcome::Complete { used, .. } => *used, + Outcome::Incomplete { used, .. } => *used, + Outcome::Error { .. } => Weight::zero(), + } + } +} + +impl From for Outcome { + fn from(error: Error) -> Self { + Self::Error { error } + } +} + +pub trait PreparedMessage { + fn weight_of(&self) -> Weight; +} + +/// Type of XCM message executor. +pub trait ExecuteXcm { + type Prepared: PreparedMessage; + fn prepare(message: Xcm) -> result::Result>; + fn execute( + origin: impl Into, + pre: Self::Prepared, + id: &mut XcmHash, + weight_credit: Weight, + ) -> Outcome; + fn prepare_and_execute( + origin: impl Into, + message: Xcm, + id: &mut XcmHash, + weight_limit: Weight, + weight_credit: Weight, + ) -> Outcome { + let pre = match Self::prepare(message) { + Ok(x) => x, + Err(_) => return Outcome::Error { error: Error::WeightNotComputable }, + }; + let xcm_weight = pre.weight_of(); + if xcm_weight.any_gt(weight_limit) { + return Outcome::Error { error: Error::WeightLimitReached(xcm_weight) } + } + Self::execute(origin, pre, id, weight_credit) + } + + /// Deduct some `fees` to the sovereign account of the given `location` and place them as per + /// the convention for fees. + fn charge_fees(location: impl Into, fees: Assets) -> Result; +} + +pub enum Weightless {} +impl PreparedMessage for Weightless { + fn weight_of(&self) -> Weight { + unreachable!() + } +} + +impl ExecuteXcm for () { + type Prepared = Weightless; + fn prepare(message: Xcm) -> result::Result> { + Err(message) + } + fn execute(_: impl Into, _: Self::Prepared, _: &mut XcmHash, _: Weight) -> Outcome { + unreachable!() + } + fn charge_fees(_location: impl Into, _fees: Assets) -> Result { + Err(Error::Unimplemented) + } +} + +pub trait Reanchorable: Sized { + /// Type to return in case of an error. + type Error: Debug; + + /// Mutate `self` so that it represents the same location from the point of view of `target`. + /// The context of `self` is provided as `context`. + /// + /// Does not modify `self` in case of overflow. + fn reanchor( + &mut self, + target: &Location, + context: &InteriorLocation, + ) -> core::result::Result<(), ()>; + + /// Consume `self` and return a new value representing the same location from the point of view + /// of `target`. The context of `self` is provided as `context`. + /// + /// Returns the original `self` in case of overflow. + fn reanchored( + self, + target: &Location, + context: &InteriorLocation, + ) -> core::result::Result; +} + +/// Result value when attempting to send an XCM message. +pub type SendResult = result::Result<(T, Assets), SendError>; + +/// Utility for sending an XCM message to a given location. +/// +/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each +/// router might return `NotApplicable` to pass the execution to the next sender item. Note that +/// each `NotApplicable` might alter the destination and the XCM message for to the next router. +/// +/// # Example +/// ```rust +/// # use codec::Encode; +/// # use staging_xcm::v5::{prelude::*, Weight}; +/// # use staging_xcm::VersionedXcm; +/// # use std::convert::Infallible; +/// +/// /// A sender that only passes the message through and does nothing. +/// struct Sender1; +/// impl SendXcm for Sender1 { +/// type Ticket = Infallible; +/// fn validate(_: &mut Option, _: &mut Option>) -> SendResult { +/// Err(SendError::NotApplicable) +/// } +/// fn deliver(_: Infallible) -> Result { +/// unreachable!() +/// } +/// } +/// +/// /// A sender that accepts a message that has two junctions, otherwise stops the routing. +/// struct Sender2; +/// impl SendXcm for Sender2 { +/// type Ticket = (); +/// fn validate(destination: &mut Option, message: &mut Option>) -> SendResult<()> { +/// match destination.as_ref().ok_or(SendError::MissingArgument)?.unpack() { +/// (0, [j1, j2]) => Ok(((), Assets::new())), +/// _ => Err(SendError::Unroutable), +/// } +/// } +/// fn deliver(_: ()) -> Result { +/// Ok([0; 32]) +/// } +/// } +/// +/// /// A sender that accepts a message from a parent, passing through otherwise. +/// struct Sender3; +/// impl SendXcm for Sender3 { +/// type Ticket = (); +/// fn validate(destination: &mut Option, message: &mut Option>) -> SendResult<()> { +/// match destination.as_ref().ok_or(SendError::MissingArgument)?.unpack() { +/// (1, []) => Ok(((), Assets::new())), +/// _ => Err(SendError::NotApplicable), +/// } +/// } +/// fn deliver(_: ()) -> Result { +/// Ok([0; 32]) +/// } +/// } +/// +/// // A call to send via XCM. We don't really care about this. +/// # fn main() { +/// let call: Vec = ().encode(); +/// let message = Xcm(vec![Instruction::Transact { +/// origin_kind: OriginKind::Superuser, +/// call: call.into(), +/// }]); +/// let message_hash = message.using_encoded(sp_io::hashing::blake2_256); +/// +/// // Sender2 will block this. +/// assert!(send_xcm::<(Sender1, Sender2, Sender3)>(Parent.into(), message.clone()).is_err()); +/// +/// // Sender3 will catch this. +/// assert!(send_xcm::<(Sender1, Sender3)>(Parent.into(), message.clone()).is_ok()); +/// # } +/// ``` +pub trait SendXcm { + /// Intermediate value which connects the two phases of the send operation. + type Ticket; + + /// Check whether the given `_message` is deliverable to the given `_destination` and if so + /// determine the cost which will be paid by this chain to do so, returning a `Validated` token + /// which can be used to enact delivery. + /// + /// The `destination` and `message` must be `Some` (or else an error will be returned) and they + /// may only be consumed if the `Err` is not `NotApplicable`. + /// + /// If it is not a destination which can be reached with this type but possibly could by others, + /// then this *MUST* return `NotApplicable`. Any other error will cause the tuple + /// implementation to exit early without trying other type fields. + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult; + + /// Actually carry out the delivery operation for a previously validated message sending. + fn deliver(ticket: Self::Ticket) -> result::Result; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl SendXcm for Tuple { + for_tuples! { type Ticket = (#( Option ),* ); } + + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + let mut maybe_cost: Option = None; + let one_ticket: Self::Ticket = (for_tuples! { #( + if maybe_cost.is_some() { + None + } else { + match Tuple::validate(destination, message) { + Err(SendError::NotApplicable) => None, + Err(e) => { return Err(e) }, + Ok((v, c)) => { + maybe_cost = Some(c); + Some(v) + }, + } + } + ),* }); + if let Some(cost) = maybe_cost { + Ok((one_ticket, cost)) + } else { + Err(SendError::NotApplicable) + } + } + + fn deliver(one_ticket: Self::Ticket) -> result::Result { + for_tuples!( #( + if let Some(validated) = one_ticket.Tuple { + return Tuple::deliver(validated); + } + )* ); + Err(SendError::Unroutable) + } +} + +/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps +/// both in `Some` before passing them as as mutable references into `T::send_xcm`. +pub fn validate_send(dest: Location, msg: Xcm<()>) -> SendResult { + T::validate(&mut Some(dest), &mut Some(msg)) +} + +/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps +/// both in `Some` before passing them as as mutable references into `T::send_xcm`. +/// +/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message +/// could not be sent. +/// +/// Generally you'll want to validate and get the price first to ensure that the sender can pay it +/// before actually doing the delivery. +pub fn send_xcm( + dest: Location, + msg: Xcm<()>, +) -> result::Result<(XcmHash, Assets), SendError> { + let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg))?; + let hash = T::deliver(ticket)?; + Ok((hash, price)) +} diff --git a/polkadot/xcm/xcm-builder/src/barriers.rs b/polkadot/xcm/xcm-builder/src/barriers.rs index c995361ea8a3c73208701903518e89817b896e58..56a8493ef0ab2f519bd4a34440a234eb12600f12 100644 --- a/polkadot/xcm/xcm-builder/src/barriers.rs +++ b/polkadot/xcm/xcm-builder/src/barriers.rs @@ -108,6 +108,7 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFrom *weight_limit = Limited(max_weight); Ok(()) }, + PayFees { .. } => Ok(()), _ => Err(ProcessMessageError::Overweight(max_weight)), })?; Ok(()) diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index bec3bdcb05a0b0adeaabc13024be4d657bbdd807..3d68d8ed16ae1459f7ac2dd39f1df80ecb44131e 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -108,7 +108,7 @@ pub use nonfungible_adapter::{ }; mod origin_aliases; -pub use origin_aliases::AliasForeignAccountId32; +pub use origin_aliases::*; mod origin_conversion; pub use origin_conversion::{ diff --git a/polkadot/xcm/xcm-builder/src/origin_aliases.rs b/polkadot/xcm/xcm-builder/src/origin_aliases.rs index d568adc3127ce0c83207e04c87a27294f45c34fa..5bc8f0ca32b938883f996cac67a9894de792d56e 100644 --- a/polkadot/xcm/xcm-builder/src/origin_aliases.rs +++ b/polkadot/xcm/xcm-builder/src/origin_aliases.rs @@ -17,7 +17,7 @@ //! Implementation for `ContainsPair`. use core::marker::PhantomData; -use frame_support::traits::{Contains, ContainsPair}; +use frame_support::traits::{Contains, ContainsPair, Get}; use xcm::latest::prelude::*; /// Alias a Foreign `AccountId32` with a local `AccountId32` if the foreign `AccountId32` matches @@ -38,3 +38,34 @@ impl> ContainsPair false } } + +/// Alias a descendant location of the original origin. +pub struct AliasChildLocation; +impl ContainsPair for AliasChildLocation { + fn contains(origin: &Location, target: &Location) -> bool { + return target.starts_with(origin) + } +} + +/// Alias a location if it passes `Filter` and the original origin is root of `Origin`. +/// +/// This can be used to allow (trusted) system chains root to alias into other locations. +/// **Warning**: do not use with untrusted `Origin` chains. +pub struct AliasOriginRootUsingFilter(PhantomData<(Origin, Filter)>); +impl ContainsPair for AliasOriginRootUsingFilter +where + Origin: Get, + Filter: Contains, +{ + fn contains(origin: &Location, target: &Location) -> bool { + // check that `origin` is a root location + match origin.unpack() { + (1, [Parachain(_)]) | + (2, [GlobalConsensus(_)]) | + (2, [GlobalConsensus(_), Parachain(_)]) => (), + _ => return false, + }; + // check that `origin` matches `Origin` and `target` matches `Filter` + return Origin::get().eq(origin) && Filter::contains(target) + } +} diff --git a/polkadot/xcm/xcm-builder/src/process_xcm_message.rs b/polkadot/xcm/xcm-builder/src/process_xcm_message.rs index 2e6f8c5fb566166f600bd9d9a7ef20f8a39fae40..8dafbf66adf0a8db5775d2f07d05fae52e721ed1 100644 --- a/polkadot/xcm/xcm-builder/src/process_xcm_message.rs +++ b/polkadot/xcm/xcm-builder/src/process_xcm_message.rs @@ -18,7 +18,10 @@ use codec::{Decode, FullCodec, MaxEncodedLen}; use core::{fmt::Debug, marker::PhantomData}; -use frame_support::traits::{ProcessMessage, ProcessMessageError}; +use frame_support::{ + dispatch::GetDispatchInfo, + traits::{ProcessMessage, ProcessMessageError}, +}; use scale_info::TypeInfo; use sp_weights::{Weight, WeightMeter}; use xcm::prelude::*; @@ -32,7 +35,7 @@ pub struct ProcessXcmMessage( impl< MessageOrigin: Into + FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug, XcmExecutor: ExecuteXcm, - Call, + Call: Decode + GetDispatchInfo, > ProcessMessage for ProcessXcmMessage { type Origin = MessageOrigin; @@ -125,7 +128,7 @@ mod tests { traits::{ProcessMessageError, ProcessMessageError::*}, }; use polkadot_test_runtime::*; - use xcm::{v3, v4, VersionedXcm}; + use xcm::{v3, v4, v5, VersionedXcm}; const ORIGIN: Junction = Junction::OnlyChild; /// The processor to use for tests. @@ -137,13 +140,15 @@ mod tests { // ClearOrigin works. assert!(process(v3_xcm(true)).unwrap()); assert!(process(v4_xcm(true)).unwrap()); + assert!(process(v5_xcm(true)).unwrap()); } #[test] fn process_message_trivial_fails() { // Trap makes it fail. assert!(!process(v3_xcm(false)).unwrap()); - assert!(!process(v3_xcm(false)).unwrap()); + assert!(!process(v4_xcm(false)).unwrap()); + assert!(!process(v5_xcm(false)).unwrap()); } #[test] @@ -179,7 +184,7 @@ mod tests { type Processor = ProcessXcmMessage; - let xcm = VersionedXcm::V4(xcm::latest::Xcm::<()>(vec![ + let xcm = VersionedXcm::from(xcm::latest::Xcm::<()>(vec![ xcm::latest::Instruction::<()>::ClearOrigin, ])); assert_err!( @@ -235,6 +240,15 @@ mod tests { VersionedXcm::V4(v4::Xcm::(vec![instr])) } + fn v5_xcm(success: bool) -> VersionedXcm { + let instr = if success { + v5::Instruction::::ClearOrigin + } else { + v5::Instruction::::Trap(1) + }; + VersionedXcm::V5(v5::Xcm::(vec![instr])) + } + fn process(msg: VersionedXcm) -> Result { process_raw(msg.encode().as_slice()) } diff --git a/polkadot/xcm/xcm-builder/src/tests/aliases.rs b/polkadot/xcm/xcm-builder/src/tests/aliases.rs index 89c17b09396d02ceb94d8faa290f8bd7294f4737..dc8b016a6aa4090dd4fb857a71ce041f8a398b8f 100644 --- a/polkadot/xcm/xcm-builder/src/tests/aliases.rs +++ b/polkadot/xcm/xcm-builder/src/tests/aliases.rs @@ -88,3 +88,164 @@ fn alias_origin_should_work() { ); assert_eq!(r, Outcome::Complete { used: Weight::from_parts(10, 10) }); } + +#[test] +fn alias_child_location() { + // parents differ + assert!(!AliasChildLocation::contains( + &Location::new(0, Parachain(1)), + &Location::new(1, Parachain(1)), + )); + assert!(!AliasChildLocation::contains( + &Location::new(0, Here), + &Location::new(1, Parachain(1)), + )); + assert!(!AliasChildLocation::contains(&Location::new(1, Here), &Location::new(2, Here),)); + + // interiors differ + assert!(!AliasChildLocation::contains( + &Location::new(1, Parachain(1)), + &Location::new(1, OnlyChild), + )); + assert!(!AliasChildLocation::contains( + &Location::new(1, Parachain(1)), + &Location::new(1, Parachain(12)), + )); + assert!(!AliasChildLocation::contains( + &Location::new(1, [Parachain(1), AccountId32 { network: None, id: [0; 32] }]), + &Location::new(1, [Parachain(1), AccountId32 { network: None, id: [1; 32] }]), + )); + assert!(!AliasChildLocation::contains( + &Location::new(1, [Parachain(1), AccountId32 { network: None, id: [0; 32] }]), + &Location::new(1, [Parachain(1), AccountId32 { network: None, id: [1; 32] }]), + )); + + // child to parent not allowed + assert!(!AliasChildLocation::contains( + &Location::new(1, [Parachain(1), AccountId32 { network: None, id: [0; 32] }]), + &Location::new(1, [Parachain(1)]), + )); + assert!(!AliasChildLocation::contains( + &Location::new(1, [Parachain(1), AccountId32 { network: None, id: [0; 32] }]), + &Location::new(1, Here), + )); + + // parent to child should work + assert!(AliasChildLocation::contains( + &Location::new(1, Here), + &Location::new(1, [Parachain(1), AccountId32 { network: None, id: [1; 32] }]), + )); + assert!( + AliasChildLocation::contains(&Location::new(1, Here), &Location::new(1, Parachain(1)),) + ); + assert!(AliasChildLocation::contains( + &Location::new(0, Here), + &Location::new(0, PalletInstance(42)), + )); + assert!(AliasChildLocation::contains( + &Location::new(2, GlobalConsensus(Kusama)), + &Location::new(2, [GlobalConsensus(Kusama), Parachain(42), GeneralIndex(12)]), + )); +} + +#[test] +fn alias_trusted_root_location() { + const ALICE: [u8; 32] = [111u8; 32]; + const BOB: [u8; 32] = [222u8; 32]; + const BOB_ON_ETH: [u8; 20] = [222u8; 20]; + + parameter_types! { + pub AliceOnAssetHub: Location = Location::new(1, [Parachain(1000), AccountId32 { id: ALICE, network: None }]); + pub SystemAssetHubLocation: Location = Location::new(1, [Parachain(1000)]); + } + + struct MatchSiblingAccounts; + impl Contains for MatchSiblingAccounts { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (1, [Parachain(_), AccountId32 { .. }])) + } + } + + struct MatchOtherGlobalConsensus; + impl Contains for MatchOtherGlobalConsensus { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (2, [GlobalConsensus(_)]) | (2, [GlobalConsensus(_), _])) + } + } + + type AliceOnAssetHubAliasesSiblingAccounts = + AliasOriginRootUsingFilter; + type AssetHubAliasesSiblingAccounts = + AliasOriginRootUsingFilter; + type AssetHubAliasesOtherGlobalConsensus = + AliasOriginRootUsingFilter; + + // Fails if origin is not the root of a chain. + assert!(!AliceOnAssetHubAliasesSiblingAccounts::contains( + &Location::new(1, [Parachain(1000), AccountId32 { id: ALICE, network: None }]), + &Location::new(1, [Parachain(1000), AccountId32 { id: BOB, network: None }]), + )); + assert!(!AliceOnAssetHubAliasesSiblingAccounts::contains( + &Location::new(1, [Parachain(1000), AccountId32 { id: ALICE, network: None }]), + &Location::new(2, [GlobalConsensus(NetworkId::Ethereum { chain_id: 1 })]), + )); + assert!(!AliceOnAssetHubAliasesSiblingAccounts::contains( + &Location::new(1, [Parachain(1000), AccountId32 { id: ALICE, network: None }]), + &Location::new( + 2, + [ + GlobalConsensus(NetworkId::Ethereum { chain_id: 1 }), + AccountKey20 { key: BOB_ON_ETH, network: None } + ] + ), + )); + // Fails if origin doesn't match. + assert!(!AssetHubAliasesSiblingAccounts::contains( + &Location::new(1, [Parachain(1001)]), + &Location::new(1, [Parachain(1000), AccountId32 { id: BOB, network: None }]), + )); + assert!(!AssetHubAliasesOtherGlobalConsensus::contains( + &Location::new(1, [Parachain(1001)]), + &Location::new( + 2, + [ + GlobalConsensus(NetworkId::Ethereum { chain_id: 1 }), + AccountKey20 { key: BOB_ON_ETH, network: None } + ] + ), + )); + // Fails if filter doesn't match. + assert!(!AssetHubAliasesSiblingAccounts::contains( + &Location::new(1, [Parachain(1000)]), + &Location::new(2, [GlobalConsensus(NetworkId::Ethereum { chain_id: 1 })]), + )); + assert!(!AssetHubAliasesSiblingAccounts::contains( + &Location::new(1, [Parachain(1000)]), + &Location::new( + 2, + [ + GlobalConsensus(NetworkId::Ethereum { chain_id: 1 }), + AccountKey20 { key: BOB_ON_ETH, network: None } + ] + ), + )); + assert!(!AssetHubAliasesOtherGlobalConsensus::contains( + &Location::new(1, [Parachain(1000)]), + &Location::new(1, [Parachain(1000), AccountId32 { id: BOB, network: None }]), + )); + // Works when origin is a chain that matches Origin and filter also matches. + assert!(AssetHubAliasesSiblingAccounts::contains( + &Location::new(1, [Parachain(1000)]), + &Location::new(1, [Parachain(1000), AccountId32 { id: BOB, network: None }]), + )); + assert!(AssetHubAliasesOtherGlobalConsensus::contains( + &Location::new(1, [Parachain(1000)]), + &Location::new( + 2, + [ + GlobalConsensus(NetworkId::Ethereum { chain_id: 1 }), + AccountKey20 { key: BOB_ON_ETH, network: None } + ] + ), + )); +} diff --git a/polkadot/xcm/xcm-builder/src/tests/transacting.rs b/polkadot/xcm/xcm-builder/src/tests/transacting.rs index a85c8b9986c85b079c4edcecb014e1609b49393b..8963e7147fdc03fbb6a68cf77308f9ee99940e46 100644 --- a/polkadot/xcm/xcm-builder/src/tests/transacting.rs +++ b/polkadot/xcm/xcm-builder/src/tests/transacting.rs @@ -22,7 +22,6 @@ fn transacting_should_work() { let message = Xcm::(vec![Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), call: TestCall::Any(Weight::from_parts(50, 50), None).encode().into(), }]); let mut hash = fake_message_hash(&message); @@ -43,7 +42,6 @@ fn transacting_should_respect_max_weight_requirement() { let message = Xcm::(vec![Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(40, 40), call: TestCall::Any(Weight::from_parts(50, 50), None).encode().into(), }]); let mut hash = fake_message_hash(&message); @@ -55,10 +53,7 @@ fn transacting_should_respect_max_weight_requirement() { weight_limit, Weight::zero(), ); - assert_eq!( - r, - Outcome::Incomplete { used: Weight::from_parts(50, 50), error: XcmError::MaxWeightInvalid } - ); + assert_eq!(r, Outcome::Complete { used: Weight::from_parts(60, 60) }); } #[test] @@ -67,7 +62,6 @@ fn transacting_should_refund_weight() { let message = Xcm::(vec![Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), call: TestCall::Any(Weight::from_parts(50, 50), Some(Weight::from_parts(30, 30))) .encode() .into(), @@ -98,7 +92,6 @@ fn paid_transacting_should_refund_payment_for_unused_weight() { BuyExecution { fees, weight_limit: Limited(Weight::from_parts(100, 100)) }, Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), // call estimated at 50 but only takes 10. call: TestCall::Any(Weight::from_parts(50, 50), Some(Weight::from_parts(10, 10))) .encode() @@ -130,7 +123,6 @@ fn report_successful_transact_status_should_work() { let message = Xcm::(vec![ Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), call: TestCall::Any(Weight::from_parts(50, 50), None).encode().into(), }, ReportTransactStatus(QueryResponseInfo { @@ -166,7 +158,6 @@ fn report_failed_transact_status_should_work() { let message = Xcm::(vec![ Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), call: TestCall::OnlyRoot(Weight::from_parts(50, 50), None).encode().into(), }, ReportTransactStatus(QueryResponseInfo { @@ -202,7 +193,6 @@ fn expect_successful_transact_status_should_work() { let message = Xcm::(vec![ Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), call: TestCall::Any(Weight::from_parts(50, 50), None).encode().into(), }, ExpectTransactStatus(MaybeErrorCode::Success), @@ -221,7 +211,6 @@ fn expect_successful_transact_status_should_work() { let message = Xcm::(vec![ Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), call: TestCall::OnlyRoot(Weight::from_parts(50, 50), None).encode().into(), }, ExpectTransactStatus(MaybeErrorCode::Success), @@ -248,7 +237,6 @@ fn expect_failed_transact_status_should_work() { let message = Xcm::(vec![ Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), call: TestCall::OnlyRoot(Weight::from_parts(50, 50), None).encode().into(), }, ExpectTransactStatus(vec![2].into()), @@ -267,7 +255,6 @@ fn expect_failed_transact_status_should_work() { let message = Xcm::(vec![ Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), call: TestCall::Any(Weight::from_parts(50, 50), None).encode().into(), }, ExpectTransactStatus(vec![2].into()), @@ -294,7 +281,6 @@ fn clear_transact_status_should_work() { let message = Xcm::(vec![ Transact { origin_kind: OriginKind::Native, - require_weight_at_most: Weight::from_parts(50, 50), call: TestCall::OnlyRoot(Weight::from_parts(50, 50), None).encode().into(), }, ClearTransactStatus, diff --git a/polkadot/xcm/xcm-builder/src/weight.rs b/polkadot/xcm/xcm-builder/src/weight.rs index 7861fdcc2e579b54b03fe509956c9a46e3405c2c..f8c0275d0f5459a254b37b12b8c4e8f2c82d806f 100644 --- a/polkadot/xcm/xcm-builder/src/weight.rs +++ b/polkadot/xcm/xcm-builder/src/weight.rs @@ -43,26 +43,28 @@ impl, C: Decode + GetDispatchInfo, M: Get> WeightBounds let mut instructions_left = M::get(); Self::weight_with_limit(message, &mut instructions_left) } - fn instr_weight(instruction: &Instruction) -> Result { + fn instr_weight(instruction: &mut Instruction) -> Result { Self::instr_weight_with_limit(instruction, &mut u32::max_value()) } } impl, C: Decode + GetDispatchInfo, M> FixedWeightBounds { - fn weight_with_limit(message: &Xcm, instrs_limit: &mut u32) -> Result { + fn weight_with_limit(message: &mut Xcm, instrs_limit: &mut u32) -> Result { let mut r: Weight = Weight::zero(); *instrs_limit = instrs_limit.checked_sub(message.0.len() as u32).ok_or(())?; - for m in message.0.iter() { - r = r.checked_add(&Self::instr_weight_with_limit(m, instrs_limit)?).ok_or(())?; + for instruction in message.0.iter_mut() { + r = r + .checked_add(&Self::instr_weight_with_limit(instruction, instrs_limit)?) + .ok_or(())?; } Ok(r) } fn instr_weight_with_limit( - instruction: &Instruction, + instruction: &mut Instruction, instrs_limit: &mut u32, ) -> Result { let instr_weight = match instruction { - Transact { require_weight_at_most, .. } => *require_weight_at_most, + Transact { ref mut call, .. } => call.ensure_decoded()?.get_dispatch_info().call_weight, SetErrorHandler(xcm) | SetAppendix(xcm) => Self::weight_with_limit(xcm, instrs_limit)?, _ => Weight::zero(), }; @@ -83,7 +85,7 @@ where let mut instructions_left = M::get(); Self::weight_with_limit(message, &mut instructions_left) } - fn instr_weight(instruction: &Instruction) -> Result { + fn instr_weight(instruction: &mut Instruction) -> Result { Self::instr_weight_with_limit(instruction, &mut u32::max_value()) } } @@ -95,20 +97,22 @@ where M: Get, Instruction: xcm::latest::GetWeight, { - fn weight_with_limit(message: &Xcm, instrs_limit: &mut u32) -> Result { + fn weight_with_limit(message: &mut Xcm, instrs_limit: &mut u32) -> Result { let mut r: Weight = Weight::zero(); *instrs_limit = instrs_limit.checked_sub(message.0.len() as u32).ok_or(())?; - for m in message.0.iter() { - r = r.checked_add(&Self::instr_weight_with_limit(m, instrs_limit)?).ok_or(())?; + for instruction in message.0.iter_mut() { + r = r + .checked_add(&Self::instr_weight_with_limit(instruction, instrs_limit)?) + .ok_or(())?; } Ok(r) } fn instr_weight_with_limit( - instruction: &Instruction, + instruction: &mut Instruction, instrs_limit: &mut u32, ) -> Result { let instr_weight = match instruction { - Transact { require_weight_at_most, .. } => *require_weight_at_most, + Transact { ref mut call, .. } => call.ensure_decoded()?.get_dispatch_info().call_weight, SetErrorHandler(xcm) | SetAppendix(xcm) => Self::weight_with_limit(xcm, instrs_limit)?, _ => Weight::zero(), }; @@ -227,7 +231,7 @@ impl< log::trace!(target: "xcm::weight", "UsingComponents::buy_weight weight: {:?}, payment: {:?}, context: {:?}", weight, payment, context); let amount = WeightToFee::weight_to_fee(&weight); let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?; - let required = (AssetId(AssetIdValue::get()), u128_amount).into(); + let required = Asset { id: AssetId(AssetIdValue::get()), fun: Fungible(u128_amount) }; let unused = payment.checked_sub(required).map_err(|_| XcmError::TooExpensive)?; self.0 = self.0.saturating_add(weight); self.1 = self.1.saturating_add(amount); diff --git a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs index e95473c5407eacb78c0b8937d8ffba86eb6e0879..9b918fd7eeed95a246be9a188ac66c6d65d3b10b 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs +++ b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs @@ -17,7 +17,7 @@ #![cfg(test)] use codec::Encode; -use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; +use frame_support::weights::Weight; use polkadot_test_client::{ BlockBuilderExt, ClientBlockImportExt, DefaultTestClientBuilderExt, InitPolkadotBlockBuilder, TestClientBuilder, TestClientBuilderExt, @@ -79,11 +79,7 @@ fn transact_recursion_limit_works() { Xcm(vec![ WithdrawAsset((Here, 1_000).into()), BuyExecution { fees: (Here, 1).into(), weight_limit: Unlimited }, - Transact { - origin_kind: OriginKind::Native, - require_weight_at_most: call.get_dispatch_info().call_weight, - call: call.encode().into(), - }, + Transact { origin_kind: OriginKind::Native, call: call.encode().into() }, ]) }; let mut call: Option = None; @@ -241,7 +237,7 @@ fn query_response_fires() { assert_eq!( polkadot_test_runtime::Xcm::query(query_id), Some(QueryStatus::Ready { - response: VersionedResponse::V4(Response::ExecutionResult(None)), + response: VersionedResponse::from(Response::ExecutionResult(None)), at: 2u32.into() }), ) diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index e3addfa3e794a2b045ee0361af62bb721472c525..a823dc6fec78912595612d6712f2fb9df8721111 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -29,7 +29,7 @@ use frame_support::{ use sp_core::defer; use sp_io::hashing::blake2_128; use sp_weights::Weight; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, AssetTransferFilter}; pub mod traits; use traits::{ @@ -47,6 +47,9 @@ pub use assets::AssetsInHolding; mod config; pub use config::Config; +#[cfg(test)] +mod tests; + /// A struct to specify how fees are being paid. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct FeesMode { @@ -83,13 +86,17 @@ pub struct XcmExecutor { appendix_weight: Weight, transact_status: MaybeErrorCode, fees_mode: FeesMode, + fees: AssetsInHolding, /// Asset provided in last `BuyExecution` instruction (if any) in current XCM program. Same /// asset type will be used for paying any potential delivery fees incurred by the program. - asset_used_for_fees: Option, + asset_used_in_buy_execution: Option, + /// Stores the current message's weight. + message_weight: Weight, + asset_claimer: Option, _config: PhantomData, } -#[cfg(feature = "runtime-benchmarks")] +#[cfg(any(test, feature = "runtime-benchmarks"))] impl XcmExecutor { pub fn holding(&self) -> &AssetsInHolding { &self.holding @@ -175,12 +182,24 @@ impl XcmExecutor { pub fn set_fees_mode(&mut self, v: FeesMode) { self.fees_mode = v } + pub fn fees(&self) -> &AssetsInHolding { + &self.fees + } + pub fn set_fees(&mut self, value: AssetsInHolding) { + self.fees = value; + } pub fn topic(&self) -> &Option<[u8; 32]> { &self.context.topic } pub fn set_topic(&mut self, v: Option<[u8; 32]>) { self.context.topic = v; } + pub fn asset_claimer(&self) -> Option { + self.asset_claimer.clone() + } + pub fn set_message_weight(&mut self, weight: Weight) { + self.message_weight = weight; + } } pub struct WeighedMessage(Weight, Xcm); @@ -249,6 +268,7 @@ impl ExecuteXcm for XcmExecutor XcmExecutor { appendix_weight: Weight::zero(), transact_status: Default::default(), fees_mode: FeesMode { jit_withdraw: false }, - asset_used_for_fees: None, + fees: AssetsInHolding::new(), + asset_used_in_buy_execution: None, + message_weight: Weight::zero(), + asset_claimer: None, _config: PhantomData, } } @@ -346,9 +369,12 @@ impl XcmExecutor { original_origin = ?self.original_origin, "Trapping assets in holding register", ); - let effective_origin = self.context.origin.as_ref().unwrap_or(&self.original_origin); - let trap_weight = - Config::AssetTrap::drop_assets(effective_origin, self.holding, &self.context); + let claimer = if let Some(asset_claimer) = self.asset_claimer.as_ref() { + asset_claimer + } else { + self.context.origin.as_ref().unwrap_or(&self.original_origin) + }; + let trap_weight = Config::AssetTrap::drop_assets(claimer, self.holding, &self.context); weight_used.saturating_accrue(trap_weight); }; @@ -466,6 +492,11 @@ impl XcmExecutor { self.holding.subsume_assets(w.into()); } } + // If there are any leftover `fees`, merge them with `holding`. + if !self.fees.is_empty() { + let leftover_fees = self.fees.saturating_take(Wild(All)); + self.holding.subsume_assets(leftover_fees); + } tracing::trace!( target: "xcm::refund_surplus", total_refunded = ?self.total_refunded, @@ -490,7 +521,7 @@ impl XcmExecutor { Some(fee) => fee, None => return Ok(()), // No delivery fees need to be paid. }; - // If `BuyExecution` was called, we use that asset for delivery fees as well. + // If `BuyExecution` or `PayFees` was called, we use that asset for delivery fees as well. let asset_to_pay_for_fees = self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone()); tracing::trace!(target: "xcm::fees", ?asset_to_pay_for_fees); @@ -505,15 +536,31 @@ impl XcmExecutor { tracing::trace!(target: "xcm::fees", ?asset_needed_for_fees); asset_to_pay_for_fees.clone().into() } else { - let assets_taken_from_holding_to_pay_delivery_fees = self - .holding - .try_take(asset_to_pay_for_fees.clone().into()) - .map_err(|e| { - tracing::error!(target: "xcm::fees", ?e, ?asset_to_pay_for_fees, "Failed to take asset_to_pay_for_fees from holding"); - XcmError::NotHoldingFees - })?; - tracing::trace!(target: "xcm::fees", ?assets_taken_from_holding_to_pay_delivery_fees); - let mut iter = assets_taken_from_holding_to_pay_delivery_fees.fungible_assets_iter(); + // This condition exists to support `BuyExecution` while the ecosystem + // transitions to `PayFees`. + let assets_to_pay_delivery_fees: AssetsInHolding = if self.fees.is_empty() { + // Means `BuyExecution` was used, we'll find the fees in the `holding` register. + self.holding + .try_take(asset_to_pay_for_fees.clone().into()) + .map_err(|e| { + tracing::error!(target: "xcm::fees", ?e, ?asset_to_pay_for_fees, + "Holding doesn't hold enough for fees"); + XcmError::NotHoldingFees + })? + .into() + } else { + // Means `PayFees` was used, we'll find the fees in the `fees` register. + self.fees + .try_take(asset_to_pay_for_fees.clone().into()) + .map_err(|e| { + tracing::error!(target: "xcm::fees", ?e, ?asset_to_pay_for_fees, + "Fees register doesn't hold enough for fees"); + XcmError::NotHoldingFees + })? + .into() + }; + tracing::trace!(target: "xcm::fees", ?assets_to_pay_delivery_fees); + let mut iter = assets_to_pay_delivery_fees.fungible_assets_iter(); let asset = iter.next().ok_or(XcmError::NotHoldingFees)?; asset.into() }; @@ -544,41 +591,45 @@ impl XcmExecutor { Ok(()) } - /// Calculates the amount of `self.asset_used_for_fees` required to swap for - /// `asset_needed_for_fees`. + /// Calculates the amount of asset used in `PayFees` or `BuyExecution` that would be + /// charged for swapping to `asset_needed_for_fees`. /// /// The calculation is done by `Config::AssetExchanger`. - /// If `self.asset_used_for_fees` is not set, it will just return `asset_needed_for_fees`. + /// If neither `PayFees` or `BuyExecution` were not used, or no swap is required, + /// it will just return `asset_needed_for_fees`. fn calculate_asset_for_delivery_fees(&self, asset_needed_for_fees: Asset) -> Asset { - if let Some(asset_wanted_for_fees) = &self.asset_used_for_fees { - if *asset_wanted_for_fees != asset_needed_for_fees.id { - match Config::AssetExchanger::quote_exchange_price( - &(asset_wanted_for_fees.clone(), Fungible(0)).into(), - &asset_needed_for_fees.clone().into(), - false, // Minimal. - ) { - Some(necessary_assets) => - // We only use the first asset for fees. - // If this is not enough to swap for the fee asset then it will error later down - // the line. - necessary_assets.get(0).unwrap_or(&asset_needed_for_fees.clone()).clone(), - // If we can't convert, then we return the original asset. - // It will error later in any case. - None => { - tracing::trace!( - target: "xcm::calculate_asset_for_delivery_fees", - ?asset_wanted_for_fees, - "Could not convert fees", - ); - asset_needed_for_fees.clone() - }, - } - } else { - asset_needed_for_fees - } - } else { + let Some(asset_wanted_for_fees) = + // we try to swap first asset in the fees register (should only ever be one), + self.fees.fungible.first_key_value().map(|(id, _)| id).or_else(|| { + // or the one used in BuyExecution + self.asset_used_in_buy_execution.as_ref() + }) + // if it is different than what we need + .filter(|&id| asset_needed_for_fees.id.ne(id)) + else { + // either nothing to swap or we're already holding the right asset + return asset_needed_for_fees + }; + Config::AssetExchanger::quote_exchange_price( + &(asset_wanted_for_fees.clone(), Fungible(0)).into(), + &asset_needed_for_fees.clone().into(), + false, // Minimal. + ) + .and_then(|necessary_assets| { + // We only use the first asset for fees. + // If this is not enough to swap for the fee asset then it will error later down + // the line. + necessary_assets.into_inner().into_iter().next() + }) + .unwrap_or_else(|| { + // If we can't convert, then we return the original asset. + // It will error later in any case. + tracing::trace!( + target: "xcm::calculate_asset_for_delivery_fees", + ?asset_wanted_for_fees, "Could not convert fees", + ); asset_needed_for_fees - } + }) } /// Calculates what `local_querier` would be from the perspective of `destination`. @@ -614,6 +665,74 @@ impl XcmExecutor { self.send(destination, message, fee_reason) } + fn do_reserve_deposit_assets( + assets: AssetsInHolding, + dest: &Location, + remote_xcm: &mut Vec>, + context: Option<&XcmContext>, + ) -> Result { + Self::deposit_assets_with_retry(&assets, dest, context)?; + // Note that we pass `None` as `maybe_failed_bin` and drop any assets which + // cannot be reanchored, because we have already called `deposit_asset` on + // all assets. + let reanchored_assets = Self::reanchored(assets, dest, None); + remote_xcm.push(ReserveAssetDeposited(reanchored_assets.clone())); + + Ok(reanchored_assets) + } + + fn do_reserve_withdraw_assets( + assets: AssetsInHolding, + failed_bin: &mut AssetsInHolding, + reserve: &Location, + remote_xcm: &mut Vec>, + ) -> Result { + // Must ensure that we recognise the assets as being managed by the destination. + #[cfg(not(any(test, feature = "runtime-benchmarks")))] + for asset in assets.assets_iter() { + ensure!( + Config::IsReserve::contains(&asset, &reserve), + XcmError::UntrustedReserveLocation + ); + } + // Note that here we are able to place any assets which could not be + // reanchored back into Holding. + let reanchored_assets = Self::reanchored(assets, reserve, Some(failed_bin)); + remote_xcm.push(WithdrawAsset(reanchored_assets.clone())); + + Ok(reanchored_assets) + } + + fn do_teleport_assets( + assets: AssetsInHolding, + dest: &Location, + remote_xcm: &mut Vec>, + context: &XcmContext, + ) -> Result { + for asset in assets.assets_iter() { + // Must ensure that we have teleport trust with destination for these assets. + #[cfg(not(any(test, feature = "runtime-benchmarks")))] + ensure!( + Config::IsTeleporter::contains(&asset, &dest), + XcmError::UntrustedTeleportLocation + ); + // We should check that the asset can actually be teleported out (for + // this to be in error, there would need to be an accounting violation + // by ourselves, so it's unlikely, but we don't want to allow that kind + // of bug to leak into a trusted chain. + Config::AssetTransactor::can_check_out(dest, &asset, context)?; + } + for asset in assets.assets_iter() { + Config::AssetTransactor::check_out(dest, &asset, context); + } + // Note that we pass `None` as `maybe_failed_bin` and drop any assets which + // cannot be reanchored, because we have already checked all assets out. + let reanchored_assets = Self::reanchored(assets, dest, None); + remote_xcm.push(ReceiveTeleportedAsset(reanchored_assets.clone())); + + Ok(reanchored_assets) + } + fn try_reanchor( reanchorable: T, destination: &Location, @@ -638,11 +757,16 @@ impl XcmExecutor { assets.into_assets_iter().collect::>().into() } - #[cfg(feature = "runtime-benchmarks")] + #[cfg(any(test, feature = "runtime-benchmarks"))] pub fn bench_process(&mut self, xcm: Xcm) -> Result<(), ExecutorError> { self.process(xcm) } + #[cfg(any(test, feature = "runtime-benchmarks"))] + pub fn bench_post_process(self, xcm_weight: Weight) -> Outcome { + self.post_process(xcm_weight) + } + fn process(&mut self, xcm: Xcm) -> Result<(), ExecutorError> { tracing::trace!( target: "xcm::process", @@ -652,7 +776,7 @@ impl XcmExecutor { error_handler_weight = ?self.error_handler_weight, ); let mut result = Ok(()); - for (i, instr) in xcm.0.into_iter().enumerate() { + for (i, mut instr) in xcm.0.into_iter().enumerate() { match &mut result { r @ Ok(()) => { // Initialize the recursion count only the first time we hit this code in our @@ -688,7 +812,7 @@ impl XcmExecutor { } }, Err(ref mut error) => - if let Ok(x) = Config::Weigher::instr_weight(&instr) { + if let Ok(x) = Config::Weigher::instr_weight(&mut instr) { error.weight.saturating_accrue(x) }, } @@ -805,7 +929,7 @@ impl XcmExecutor { Ok(()) }) }, - Transact { origin_kind, require_weight_at_most, mut call } => { + Transact { origin_kind, mut call } => { // We assume that the Relay-chain is allowed to use transact on this parachain. let origin = self.cloned_origin().ok_or_else(|| { tracing::trace!( @@ -862,18 +986,6 @@ impl XcmExecutor { ); let weight = message_call.get_dispatch_info().call_weight; - - if !weight.all_lte(require_weight_at_most) { - tracing::trace!( - target: "xcm::process_instruction::transact", - %weight, - %require_weight_at_most, - "Max weight bigger than require at most", - ); - - return Err(XcmError::MaxWeightInvalid) - } - let maybe_actual_weight = match Config::CallDispatcher::dispatch(message_call, dispatch_origin) { Ok(post_info) => { @@ -898,9 +1010,7 @@ impl XcmExecutor { }; let actual_weight = maybe_actual_weight.unwrap_or(weight); let surplus = weight.saturating_sub(actual_weight); - // We assume that the `Config::Weigher` will count the `require_weight_at_most` - // for the estimate of how much weight this instruction will take. Now that we know - // that it's less, we credit it. + // If the actual weight of the call was less than the specified weight, we credit it. // // We make the adjustment for the total surplus, which is used eventually // reported back to the caller and this ensures that they account for the total @@ -950,7 +1060,7 @@ impl XcmExecutor { let old_holding = self.holding.clone(); let result = Config::TransactionalProcessor::process(|| { let deposited = self.holding.saturating_take(assets); - self.deposit_assets_with_retry(&deposited, &beneficiary) + Self::deposit_assets_with_retry(&deposited, &beneficiary, Some(&self.context)) }); if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { self.holding = old_holding; @@ -960,42 +1070,28 @@ impl XcmExecutor { DepositReserveAsset { assets, dest, xcm } => { let old_holding = self.holding.clone(); let result = Config::TransactionalProcessor::process(|| { - // we need to do this take/put cycle to solve wildcards and get exact assets to - // be weighed - let to_weigh = self.holding.saturating_take(assets.clone()); - self.holding.subsume_assets(to_weigh.clone()); - let to_weigh_reanchored = Self::reanchored(to_weigh, &dest, None); - let mut message_to_weigh = - vec![ReserveAssetDeposited(to_weigh_reanchored), ClearOrigin]; - message_to_weigh.extend(xcm.0.clone().into_iter()); - let (_, fee) = - validate_send::(dest.clone(), Xcm(message_to_weigh))?; - let maybe_delivery_fee = fee.get(0).map(|asset_needed_for_fees| { - tracing::trace!( - target: "xcm::DepositReserveAsset", - "Asset provided to pay for fees {:?}, asset required for delivery fees: {:?}", - self.asset_used_for_fees, asset_needed_for_fees, - ); - let asset_to_pay_for_fees = - self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone()); - // set aside fee to be charged by XcmSender - let delivery_fee = - self.holding.saturating_take(asset_to_pay_for_fees.into()); - tracing::trace!(target: "xcm::DepositReserveAsset", ?delivery_fee); - delivery_fee - }); + let maybe_delivery_fee_from_holding = if self.fees.is_empty() { + self.get_delivery_fee_from_holding(&assets, &dest, &xcm)? + } else { + None + }; + + let mut message = Vec::with_capacity(xcm.len() + 2); // now take assets to deposit (after having taken delivery fees) let deposited = self.holding.saturating_take(assets); tracing::trace!(target: "xcm::DepositReserveAsset", ?deposited, "Assets except delivery fee"); - self.deposit_assets_with_retry(&deposited, &dest)?; - // Note that we pass `None` as `maybe_failed_bin` and drop any assets which - // cannot be reanchored because we have already called `deposit_asset` on all - // assets. - let assets = Self::reanchored(deposited, &dest, None); - let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; + Self::do_reserve_deposit_assets( + deposited, + &dest, + &mut message, + Some(&self.context), + )?; + // clear origin for subsequent custom instructions + message.push(ClearOrigin); + // append custom instructions message.extend(xcm.0.into_iter()); - // put back delivery_fee in holding register to be charged by XcmSender - if let Some(delivery_fee) = maybe_delivery_fee { + if let Some(delivery_fee) = maybe_delivery_fee_from_holding { + // Put back delivery_fee in holding register to be charged by XcmSender. self.holding.subsume_assets(delivery_fee); } self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?; @@ -1010,18 +1106,16 @@ impl XcmExecutor { let old_holding = self.holding.clone(); let result = Config::TransactionalProcessor::process(|| { let assets = self.holding.saturating_take(assets); - // Must ensure that we recognise the assets as being managed by the destination. - #[cfg(not(feature = "runtime-benchmarks"))] - for asset in assets.assets_iter() { - ensure!( - Config::IsReserve::contains(&asset, &reserve), - XcmError::UntrustedReserveLocation - ); - } - // Note that here we are able to place any assets which could not be reanchored - // back into Holding. - let assets = Self::reanchored(assets, &reserve, Some(&mut self.holding)); - let mut message = vec![WithdrawAsset(assets), ClearOrigin]; + let mut message = Vec::with_capacity(xcm.len() + 2); + Self::do_reserve_withdraw_assets( + assets, + &mut self.holding, + &reserve, + &mut message, + )?; + // clear origin for subsequent custom instructions + message.push(ClearOrigin); + // append custom instructions message.extend(xcm.0.into_iter()); self.send(reserve, Xcm(message), FeeReason::InitiateReserveWithdraw)?; Ok(()) @@ -1033,37 +1127,131 @@ impl XcmExecutor { }, InitiateTeleport { assets, dest, xcm } => { let old_holding = self.holding.clone(); - let result = (|| -> Result<(), XcmError> { - // We must do this first in order to resolve wildcards. + let result = Config::TransactionalProcessor::process(|| { let assets = self.holding.saturating_take(assets); - // Must ensure that we have teleport trust with destination for these assets. - #[cfg(not(feature = "runtime-benchmarks"))] - for asset in assets.assets_iter() { - ensure!( - Config::IsTeleporter::contains(&asset, &dest), - XcmError::UntrustedTeleportLocation - ); - } - for asset in assets.assets_iter() { - // We should check that the asset can actually be teleported out (for this - // to be in error, there would need to be an accounting violation by - // ourselves, so it's unlikely, but we don't want to allow that kind of bug - // to leak into a trusted chain. - Config::AssetTransactor::can_check_out(&dest, &asset, &self.context)?; - } - // Note that we pass `None` as `maybe_failed_bin` and drop any assets which - // cannot be reanchored because we have already checked all assets out. - let reanchored_assets = Self::reanchored(assets.clone(), &dest, None); - let mut message = vec![ReceiveTeleportedAsset(reanchored_assets), ClearOrigin]; + let mut message = Vec::with_capacity(xcm.len() + 2); + Self::do_teleport_assets(assets, &dest, &mut message, &self.context)?; + // clear origin for subsequent custom instructions + message.push(ClearOrigin); + // append custom instructions message.extend(xcm.0.into_iter()); self.send(dest.clone(), Xcm(message), FeeReason::InitiateTeleport)?; + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; + } + result + }, + InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } => { + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + let mut message = Vec::with_capacity(assets.len() + remote_xcm.len() + 2); + + // We need to transfer the fees and buy execution on remote chain _BEFORE_ + // transferring the other assets. This is required to satisfy the + // `MAX_ASSETS_FOR_BUY_EXECUTION` limit in the `AllowTopLevelPaidExecutionFrom` + // barrier. + if let Some(remote_fees) = remote_fees { + let reanchored_fees = match remote_fees { + AssetTransferFilter::Teleport(fees_filter) => { + let teleport_fees = self + .holding + .try_take(fees_filter) + .map_err(|_| XcmError::NotHoldingFees)?; + Self::do_teleport_assets( + teleport_fees, + &destination, + &mut message, + &self.context, + )? + }, + AssetTransferFilter::ReserveDeposit(fees_filter) => { + let reserve_deposit_fees = self + .holding + .try_take(fees_filter) + .map_err(|_| XcmError::NotHoldingFees)?; + Self::do_reserve_deposit_assets( + reserve_deposit_fees, + &destination, + &mut message, + Some(&self.context), + )? + }, + AssetTransferFilter::ReserveWithdraw(fees_filter) => { + let reserve_withdraw_fees = self + .holding + .try_take(fees_filter) + .map_err(|_| XcmError::NotHoldingFees)?; + Self::do_reserve_withdraw_assets( + reserve_withdraw_fees, + &mut self.holding, + &destination, + &mut message, + )? + }, + }; + ensure!(reanchored_fees.len() == 1, XcmError::TooManyAssets); + let fees = + reanchored_fees.into_inner().pop().ok_or(XcmError::NotHoldingFees)?; + // move these assets to the fees register for covering execution and paying + // any subsequent fees + message.push(PayFees { asset: fees }); + } else { + // unpaid execution + message + .push(UnpaidExecution { weight_limit: Unlimited, check_origin: None }); + } - for asset in assets.assets_iter() { - Config::AssetTransactor::check_out(&dest, &asset, &self.context); + // add any extra asset transfers + for asset_filter in assets { + match asset_filter { + AssetTransferFilter::Teleport(assets) => Self::do_teleport_assets( + self.holding.saturating_take(assets), + &destination, + &mut message, + &self.context, + )?, + AssetTransferFilter::ReserveDeposit(assets) => + Self::do_reserve_deposit_assets( + self.holding.saturating_take(assets), + &destination, + &mut message, + Some(&self.context), + )?, + AssetTransferFilter::ReserveWithdraw(assets) => + Self::do_reserve_withdraw_assets( + self.holding.saturating_take(assets), + &mut self.holding, + &destination, + &mut message, + )?, + }; } + if preserve_origin { + // preserve current origin for subsequent user-controlled instructions on + // remote chain + let original_origin = self + .origin_ref() + .cloned() + .and_then(|origin| { + Self::try_reanchor(origin, &destination) + .map(|(reanchored, _)| reanchored) + .ok() + }) + .ok_or(XcmError::BadOrigin)?; + message.push(AliasOrigin(original_origin)); + } else { + // clear origin for subsequent user-controlled instructions on remote chain + message.push(ClearOrigin); + } + // append custom instructions + message.extend(remote_xcm.0.into_iter()); + // send the onward XCM + self.send(destination, Xcm(message), FeeReason::InitiateTransfer)?; Ok(()) - })(); - if result.is_err() { + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { self.holding = old_holding; } result @@ -1090,24 +1278,61 @@ impl XcmExecutor { let old_holding = self.holding.clone(); // Save the asset being used for execution fees, so we later know what should be // used for delivery fees. - self.asset_used_for_fees = Some(fees.id.clone()); - tracing::trace!(target: "xcm::executor::BuyExecution", asset_used_for_fees = ?self.asset_used_for_fees); + self.asset_used_in_buy_execution = Some(fees.id.clone()); + tracing::trace!( + target: "xcm::executor::BuyExecution", + asset_used_in_buy_execution = ?self.asset_used_in_buy_execution + ); // pay for `weight` using up to `fees` of the holding register. let max_fee = self.holding.try_take(fees.clone().into()).map_err(|e| { - tracing::error!(target: "xcm::process_instruction::buy_execution", ?e, ?fees, "Failed to take fees from holding"); + tracing::error!(target: "xcm::process_instruction::buy_execution", ?e, ?fees, + "Failed to take fees from holding"); XcmError::NotHoldingFees })?; - let result = || -> Result<(), XcmError> { + let result = Config::TransactionalProcessor::process(|| { let unspent = self.trader.buy_weight(weight, max_fee, &self.context)?; self.holding.subsume_assets(unspent); Ok(()) - }(); + }); if result.is_err() { self.holding = old_holding; } result }, + PayFees { asset } => { + // Message was not weighed, there is nothing to pay. + if self.message_weight == Weight::zero() { + tracing::warn!( + target: "xcm::executor::PayFees", + "Message was not weighed or weight was 0. Nothing will be charged.", + ); + return Ok(()); + } + // Record old holding in case we need to rollback. + let old_holding = self.holding.clone(); + // The max we're willing to pay for fees is decided by the `asset` operand. + tracing::trace!( + target: "xcm::executor::PayFees", + asset_for_fees = ?asset, + message_weight = ?self.message_weight, + ); + let max_fee = + self.holding.try_take(asset.into()).map_err(|_| XcmError::NotHoldingFees)?; + // Pay for execution fees. + let result = Config::TransactionalProcessor::process(|| { + let unspent = + self.trader.buy_weight(self.message_weight, max_fee, &self.context)?; + // Move unspent to the `fees` register. + self.fees.subsume_assets(unspent); + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + // Rollback. + self.holding = old_holding; + } + result + }, RefundSurplus => self.refund_surplus(), SetErrorHandler(mut handler) => { let handler_weight = Config::Weigher::weight(&mut handler) @@ -1129,6 +1354,10 @@ impl XcmExecutor { self.error = None; Ok(()) }, + SetAssetClaimer { location } => { + self.asset_claimer = Some(location); + Ok(()) + }, ClaimAsset { assets, ticket } => { let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; self.ensure_can_subsume_assets(assets.len())?; @@ -1338,7 +1567,7 @@ impl XcmExecutor { ExchangeAsset { give, want, maximal } => { let old_holding = self.holding.clone(); let give = self.holding.saturating_take(give); - let result = (|| -> Result<(), XcmError> { + let result = Config::TransactionalProcessor::process(|| { self.ensure_can_subsume_assets(want.len())?; let exchange_result = Config::AssetExchanger::exchange_asset( self.origin_ref(), @@ -1352,7 +1581,7 @@ impl XcmExecutor { } else { Err(XcmError::NoDeal) } - })(); + }); if result.is_err() { self.holding = old_holding; } @@ -1414,16 +1643,15 @@ impl XcmExecutor { /// This function can write into storage and also return an error at the same time, it should /// always be called within a transactional context. fn deposit_assets_with_retry( - &mut self, to_deposit: &AssetsInHolding, beneficiary: &Location, + context: Option<&XcmContext>, ) -> Result<(), XcmError> { let mut failed_deposits = Vec::with_capacity(to_deposit.len()); let mut deposit_result = Ok(()); for asset in to_deposit.assets_iter() { - deposit_result = - Config::AssetTransactor::deposit_asset(&asset, &beneficiary, Some(&self.context)); + deposit_result = Config::AssetTransactor::deposit_asset(&asset, &beneficiary, context); // if deposit failed for asset, mark it for retry after depositing the others. if deposit_result.is_err() { failed_deposits.push(asset); @@ -1441,8 +1669,43 @@ impl XcmExecutor { // retry previously failed deposits, this time short-circuiting on any error. for asset in failed_deposits { - Config::AssetTransactor::deposit_asset(&asset, &beneficiary, Some(&self.context))?; + Config::AssetTransactor::deposit_asset(&asset, &beneficiary, context)?; } Ok(()) } + + /// Gets the necessary delivery fee to send a reserve transfer message to `destination` from + /// holding. + /// + /// Will be removed once the transition from `BuyExecution` to `PayFees` is complete. + fn get_delivery_fee_from_holding( + &mut self, + assets: &AssetFilter, + destination: &Location, + xcm: &Xcm<()>, + ) -> Result, XcmError> { + // we need to do this take/put cycle to solve wildcards and get exact assets to + // be weighed + let to_weigh = self.holding.saturating_take(assets.clone()); + self.holding.subsume_assets(to_weigh.clone()); + let to_weigh_reanchored = Self::reanchored(to_weigh, &destination, None); + let mut message_to_weigh = vec![ReserveAssetDeposited(to_weigh_reanchored), ClearOrigin]; + message_to_weigh.extend(xcm.0.clone().into_iter()); + let (_, fee) = + validate_send::(destination.clone(), Xcm(message_to_weigh))?; + let maybe_delivery_fee = fee.get(0).map(|asset_needed_for_fees| { + tracing::trace!( + target: "xcm::fees::DepositReserveAsset", + "Asset provided to pay for fees {:?}, asset required for delivery fees: {:?}", + self.asset_used_in_buy_execution, asset_needed_for_fees, + ); + let asset_to_pay_for_fees = + self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone()); + // set aside fee to be charged by XcmSender + let delivery_fee = self.holding.saturating_take(asset_to_pay_for_fees.into()); + tracing::trace!(target: "xcm::fees::DepositReserveAsset", ?delivery_fee); + delivery_fee + }); + Ok(maybe_delivery_fee) + } } diff --git a/polkadot/xcm/xcm-executor/src/tests/initiate_transfer.rs b/polkadot/xcm/xcm-executor/src/tests/initiate_transfer.rs new file mode 100644 index 0000000000000000000000000000000000000000..09ed1f44cc4af4b34b570c5e00aad7bf2770883d --- /dev/null +++ b/polkadot/xcm/xcm-executor/src/tests/initiate_transfer.rs @@ -0,0 +1,106 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Unit tests related to the `InitiateTransfer` instruction. +//! +//! See [Fellowship RFC 100](https://github.com/polkadot-fellows/rfCs/pull/100), +//! [Fellowship RFC 122](https://github.com/polkadot-fellows/rfCs/pull/122), and the +//! [specification](https://github.com/polkadot-fellows/xcm-format) for more information. + +use xcm::{latest::AssetTransferFilter, prelude::*}; + +use super::mock::*; + +// The sender and recipient we use across these tests. +const SENDER: [u8; 32] = [0; 32]; +const RECIPIENT: [u8; 32] = [1; 32]; + +#[test] +fn clears_origin() { + // Make sure the sender has enough funds to withdraw. + add_asset(SENDER, (Here, 100u128)); + + let xcm_on_dest = + Xcm(vec![RefundSurplus, DepositAsset { assets: Wild(All), beneficiary: RECIPIENT.into() }]); + let assets: Assets = (Here, 90u128).into(); + let xcm = Xcm::(vec![ + WithdrawAsset((Here, 100u128).into()), + PayFees { asset: (Here, 10u128).into() }, + InitiateTransfer { + destination: Parent.into(), + remote_fees: Some(AssetTransferFilter::ReserveDeposit(assets.into())), + preserve_origin: false, + assets: vec![], + remote_xcm: xcm_on_dest, + }, + ]); + + let (mut vm, _) = instantiate_executor(SENDER, xcm.clone()); + + // Program runs successfully. + let res = vm.bench_process(xcm); + assert!(res.is_ok(), "execution error {:?}", res); + + let (dest, sent_message) = sent_xcm().pop().unwrap(); + assert_eq!(dest, Parent.into()); + assert_eq!(sent_message.len(), 5); + let mut instr = sent_message.inner().iter(); + assert!(matches!(instr.next().unwrap(), ReserveAssetDeposited(..))); + assert!(matches!(instr.next().unwrap(), PayFees { .. })); + assert!(matches!(instr.next().unwrap(), ClearOrigin)); + assert!(matches!(instr.next().unwrap(), RefundSurplus)); + assert!(matches!(instr.next().unwrap(), DepositAsset { .. })); +} + +#[test] +fn preserves_origin() { + // Make sure the sender has enough funds to withdraw. + add_asset(SENDER, (Here, 100u128)); + + let xcm_on_dest = + Xcm(vec![RefundSurplus, DepositAsset { assets: Wild(All), beneficiary: RECIPIENT.into() }]); + let assets: Assets = (Here, 90u128).into(); + let xcm = Xcm::(vec![ + WithdrawAsset((Here, 100u128).into()), + PayFees { asset: (Here, 10u128).into() }, + InitiateTransfer { + destination: Parent.into(), + remote_fees: Some(AssetTransferFilter::ReserveDeposit(assets.into())), + preserve_origin: true, + assets: vec![], + remote_xcm: xcm_on_dest, + }, + ]); + + let (mut vm, _) = instantiate_executor(SENDER, xcm.clone()); + + // Program runs successfully. + let res = vm.bench_process(xcm); + assert!(res.is_ok(), "execution error {:?}", res); + + let (dest, sent_message) = sent_xcm().pop().unwrap(); + assert_eq!(dest, Parent.into()); + assert_eq!(sent_message.len(), 5); + let mut instr = sent_message.inner().iter(); + assert!(matches!(instr.next().unwrap(), ReserveAssetDeposited(..))); + assert!(matches!(instr.next().unwrap(), PayFees { .. })); + assert!(matches!( + instr.next().unwrap(), + AliasOrigin(origin) if matches!(origin.unpack(), (0, [Parachain(1000), AccountId32 { id: SENDER, network: None }])) + )); + assert!(matches!(instr.next().unwrap(), RefundSurplus)); + assert!(matches!(instr.next().unwrap(), DepositAsset { .. })); +} diff --git a/polkadot/xcm/xcm-executor/src/tests/mock.rs b/polkadot/xcm/xcm-executor/src/tests/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..9cf258331f38a4ff65558c3441b8d3dddbdca638 --- /dev/null +++ b/polkadot/xcm/xcm-executor/src/tests/mock.rs @@ -0,0 +1,279 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Mock types and XcmConfig for all executor unit tests. + +use alloc::collections::btree_map::BTreeMap; +use codec::{Decode, Encode}; +use core::cell::RefCell; +use frame_support::{ + dispatch::{DispatchInfo, DispatchResultWithPostInfo, GetDispatchInfo, PostDispatchInfo}, + parameter_types, + traits::{Everything, Nothing, ProcessMessageError}, + weights::Weight, +}; +use sp_runtime::traits::Dispatchable; +use xcm::prelude::*; + +use crate::{ + traits::{DropAssets, Properties, ShouldExecute, TransactAsset, WeightBounds, WeightTrader}, + AssetsInHolding, Config, XcmExecutor, +}; + +/// We create an XCVM instance instead of calling `XcmExecutor::<_>::prepare_and_execute` so we +/// can inspect its fields. +pub fn instantiate_executor( + origin: impl Into, + message: Xcm<::RuntimeCall>, +) -> (XcmExecutor, Weight) { + let mut vm = + XcmExecutor::::new(origin, message.using_encoded(sp_io::hashing::blake2_256)); + let weight = XcmExecutor::::prepare(message.clone()).unwrap().weight_of(); + vm.message_weight = weight; + (vm, weight) +} + +parameter_types! { + pub const MaxAssetsIntoHolding: u32 = 10; + pub const BaseXcmWeight: Weight = Weight::from_parts(1, 1); + pub const MaxInstructions: u32 = 10; + pub UniversalLocation: InteriorLocation = [GlobalConsensus(ByGenesis([0; 32])), Parachain(1000)].into(); +} + +/// Test origin. +#[derive(Debug)] +pub struct TestOrigin; + +/// Test call. +/// +/// Doesn't dispatch anything, has an empty implementation of [`Dispatchable`] that +/// just returns `Ok` with an empty [`PostDispatchInfo`]. +#[derive(Debug, Encode, Decode, Eq, PartialEq, Clone, Copy, scale_info::TypeInfo)] +pub struct TestCall; +impl Dispatchable for TestCall { + type RuntimeOrigin = TestOrigin; + type Config = (); + type Info = (); + type PostInfo = PostDispatchInfo; + + fn dispatch(self, _origin: Self::RuntimeOrigin) -> DispatchResultWithPostInfo { + Ok(PostDispatchInfo::default()) + } +} +impl GetDispatchInfo for TestCall { + fn get_dispatch_info(&self) -> DispatchInfo { + DispatchInfo::default() + } +} + +/// Test weigher that just returns a fixed weight for every program. +pub struct TestWeigher; +impl WeightBounds for TestWeigher { + fn weight(_message: &mut Xcm) -> Result { + Ok(Weight::from_parts(2, 2)) + } + + fn instr_weight(_instruction: &mut Instruction) -> Result { + Ok(Weight::from_parts(2, 2)) + } +} + +thread_local! { + pub static ASSETS: RefCell> = RefCell::new(BTreeMap::new()); + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); +} + +pub fn add_asset(who: impl Into, what: impl Into) { + ASSETS.with(|a| { + a.borrow_mut() + .entry(who.into()) + .or_insert(AssetsInHolding::new()) + .subsume(what.into()) + }); +} + +pub fn asset_list(who: impl Into) -> Vec { + Assets::from(assets(who)).into_inner() +} + +pub fn assets(who: impl Into) -> AssetsInHolding { + ASSETS.with(|a| a.borrow().get(&who.into()).cloned()).unwrap_or_default() +} + +pub fn get_first_fungible(assets: &AssetsInHolding) -> Option { + assets.fungible_assets_iter().next() +} + +/// Test asset transactor that withdraws from and deposits to a thread local assets storage. +pub struct TestAssetTransactor; +impl TransactAsset for TestAssetTransactor { + fn deposit_asset( + what: &Asset, + who: &Location, + _context: Option<&XcmContext>, + ) -> Result<(), XcmError> { + add_asset(who.clone(), what.clone()); + Ok(()) + } + + fn withdraw_asset( + what: &Asset, + who: &Location, + _context: Option<&XcmContext>, + ) -> Result { + ASSETS.with(|a| { + a.borrow_mut() + .get_mut(who) + .ok_or(XcmError::NotWithdrawable)? + .try_take(what.clone().into()) + .map_err(|_| XcmError::NotWithdrawable) + }) + } +} + +/// Test barrier that just lets everything through. +pub struct TestBarrier; +impl ShouldExecute for TestBarrier { + fn should_execute( + _origin: &Location, + _instructions: &mut [Instruction], + _max_weight: Weight, + _properties: &mut Properties, + ) -> Result<(), ProcessMessageError> { + Ok(()) + } +} + +/// Test weight to fee that just multiplies `Weight.ref_time` and `Weight.proof_size`. +pub struct WeightToFee; +impl WeightToFee { + pub fn weight_to_fee(weight: &Weight) -> u128 { + weight.ref_time() as u128 * weight.proof_size() as u128 + } +} + +/// Test weight trader that just buys weight with the native asset (`Here`) and +/// uses the test `WeightToFee`. +pub struct TestTrader { + weight_bought_so_far: Weight, +} +impl WeightTrader for TestTrader { + fn new() -> Self { + Self { weight_bought_so_far: Weight::zero() } + } + + fn buy_weight( + &mut self, + weight: Weight, + payment: AssetsInHolding, + _context: &XcmContext, + ) -> Result { + let amount = WeightToFee::weight_to_fee(&weight); + let required: Asset = (Here, amount).into(); + let unused = payment.checked_sub(required).map_err(|_| XcmError::TooExpensive)?; + self.weight_bought_so_far.saturating_add(weight); + Ok(unused) + } + + fn refund_weight(&mut self, weight: Weight, _context: &XcmContext) -> Option { + let weight = weight.min(self.weight_bought_so_far); + let amount = WeightToFee::weight_to_fee(&weight); + self.weight_bought_so_far -= weight; + if amount > 0 { + Some((Here, amount).into()) + } else { + None + } + } +} + +/// Account where all dropped assets are deposited. +pub const TRAPPED_ASSETS: [u8; 32] = [255; 32]; + +/// Test asset trap that moves all dropped assets to the `TRAPPED_ASSETS` account. +pub struct TestAssetTrap; +impl DropAssets for TestAssetTrap { + fn drop_assets(_origin: &Location, assets: AssetsInHolding, _context: &XcmContext) -> Weight { + ASSETS.with(|a| { + a.borrow_mut() + .entry(TRAPPED_ASSETS.into()) + .or_insert(AssetsInHolding::new()) + .subsume_assets(assets) + }); + Weight::zero() + } +} + +/// Test sender that always succeeds and puts messages in a dummy queue. +/// +/// It charges `1` for the delivery fee. +pub struct TestSender; +impl SendXcm for TestSender { + type Ticket = (Location, Xcm<()>); + + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + let ticket = (destination.take().unwrap(), message.take().unwrap()); + let delivery_fee: Asset = (Here, 1u128).into(); + Ok((ticket, delivery_fee.into())) + } + + fn deliver(ticket: Self::Ticket) -> Result { + SENT_XCM.with(|q| q.borrow_mut().push(ticket)); + Ok([0; 32]) + } +} + +/// Gets queued test messages. +pub fn sent_xcm() -> Vec<(Location, Xcm<()>)> { + SENT_XCM.with(|q| (*q.borrow()).clone()) +} + +/// Test XcmConfig that uses all the test implementations in this file. +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = TestCall; + type XcmSender = TestSender; + type AssetTransactor = TestAssetTransactor; + type OriginConverter = (); + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = TestBarrier; + type Weigher = TestWeigher; + type Trader = TestTrader; + type ResponseHandler = (); + type AssetTrap = TestAssetTrap; + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = Self::RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern/no_buy_execution.rs b/polkadot/xcm/xcm-executor/src/tests/mod.rs similarity index 70% rename from polkadot/xcm/procedural/tests/ui/builder_pattern/no_buy_execution.rs rename to polkadot/xcm/xcm-executor/src/tests/mod.rs index 1ed8dd38cbad5b32bb9ce1a38470fb579b3cb5c2..5c133871f0bf351298526914548ee834b506b383 100644 --- a/polkadot/xcm/procedural/tests/ui/builder_pattern/no_buy_execution.rs +++ b/polkadot/xcm/xcm-executor/src/tests/mod.rs @@ -14,16 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Test error when there's no `BuyExecution` instruction. - -use xcm_procedural::Builder; - -struct Xcm(pub Vec>); - -#[derive(Builder)] -enum Instruction { - UnpaidExecution { weight_limit: (u32, u32) }, - Transact { call: Call }, -} - -fn main() {} +//! Unit tests for the XCM executor. +//! +//! These exclude any cross-chain functionality. For those, look at the +//! `xcm-emulator` based tests in the cumulus folder. +//! These tests deal with internal state changes of the XCVM. + +mod initiate_transfer; +mod mock; +mod pay_fees; +mod set_asset_claimer; diff --git a/polkadot/xcm/xcm-executor/src/tests/pay_fees.rs b/polkadot/xcm/xcm-executor/src/tests/pay_fees.rs new file mode 100644 index 0000000000000000000000000000000000000000..4c196831e6a46d6d4099bc9564ef31e3ce04d01d --- /dev/null +++ b/polkadot/xcm/xcm-executor/src/tests/pay_fees.rs @@ -0,0 +1,257 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Unit tests related to the `fees` register and `PayFees` instruction. +//! +//! See [Fellowship RFC 105](https://github.com/polkadot-fellows/rfCs/pull/105) +//! and the [specification](https://github.com/polkadot-fellows/xcm-format) for more information. + +use xcm::prelude::*; + +use super::mock::*; + +// The sender and recipient we use across these tests. +const SENDER: [u8; 32] = [0; 32]; +const RECIPIENT: [u8; 32] = [1; 32]; + +// ===== Happy path ===== + +// This is a sort of backwards compatibility test. +// Since `PayFees` is a replacement for `BuyExecution`, we need to make sure it at least +// manages to do the same thing, paying for execution fees. +#[test] +fn works_for_execution_fees() { + // Make sure the sender has enough funds to withdraw. + add_asset(SENDER, (Here, 100u128)); + + // Build xcm. + let xcm = Xcm::::builder() + .withdraw_asset((Here, 100u128)) + .pay_fees((Here, 10u128)) // 10% destined for fees, not more. + .deposit_asset(All, RECIPIENT) + .build(); + + let (mut vm, weight) = instantiate_executor(SENDER, xcm.clone()); + + // Program runs successfully. + assert!(vm.bench_process(xcm).is_ok()); + + // Nothing is left in the `holding` register. + assert_eq!(get_first_fungible(vm.holding()), None); + // Execution fees were 4, so we still have 6 left in the `fees` register. + assert_eq!(get_first_fungible(vm.fees()).unwrap(), (Here, 6u128).into()); + + // The recipient received all the assets in the holding register, so `100` that + // were withdrawn, minus the `10` that were destinated for fee payment. + assert_eq!(asset_list(RECIPIENT), [(Here, 90u128).into()]); + + // Leftover fees get trapped. + assert!(vm.bench_post_process(weight).ensure_complete().is_ok()); + assert_eq!(asset_list(TRAPPED_ASSETS), [(Here, 6u128).into()]) +} + +// This tests the new functionality provided by `PayFees`, being able to pay for +// delivery fees from the `fees` register. +#[test] +fn works_for_delivery_fees() { + // Make sure the sender has enough funds to withdraw. + add_asset(SENDER, (Here, 100u128)); + + // Information to send messages. + // We don't care about the specifics since we're not actually sending them. + let query_response_info = + QueryResponseInfo { destination: Parent.into(), query_id: 0, max_weight: Weight::zero() }; + + // Build xcm. + let xcm = Xcm::::builder() + .withdraw_asset((Here, 100u128)) + .pay_fees((Here, 10u128)) + // Send a bunch of messages, each charging delivery fees. + .report_error(query_response_info.clone()) + .report_error(query_response_info.clone()) + .report_error(query_response_info) + .deposit_asset(All, RECIPIENT) + .build(); + + let (mut vm, _) = instantiate_executor(SENDER, xcm.clone()); + + // Program runs successfully. + assert!(vm.bench_process(xcm).is_ok()); + + // Nothing is left in the `holding` register. + assert_eq!(get_first_fungible(vm.holding()), None); + // Execution fees were 4, delivery were 3, so we are left with only 3 in the `fees` register. + assert_eq!(get_first_fungible(vm.fees()).unwrap(), (Here, 3u128).into()); + + // The recipient received all the assets in the holding register, so `100` that + // were withdrawn, minus the `10` that were destinated for fee payment. + assert_eq!(asset_list(RECIPIENT), [(Here, 90u128).into()]); + + let querier: Location = + (Parachain(1000), AccountId32 { id: SENDER.into(), network: None }).into(); + let sent_message = Xcm(vec![QueryResponse { + query_id: 0, + response: Response::ExecutionResult(None), + max_weight: Weight::zero(), + querier: Some(querier), + }]); + + // The messages were "sent" successfully. + assert_eq!( + sent_xcm(), + vec![ + (Parent.into(), sent_message.clone()), + (Parent.into(), sent_message.clone()), + (Parent.into(), sent_message.clone()) + ] + ); +} + +// Tests the support for `BuyExecution` while the ecosystem transitions to `PayFees`. +#[test] +fn buy_execution_works_as_before() { + // Make sure the sender has enough funds to withdraw. + add_asset(SENDER, (Here, 100u128)); + + // Build xcm. + let xcm = Xcm::::builder() + .withdraw_asset((Here, 100u128)) + // We can put everything here, since excess will be returned to holding. + // We have to specify `Limited` here to actually work, it's normally + // set in the `AllowTopLevelPaidExecutionFrom` barrier. + .buy_execution((Here, 100u128), Limited(Weight::from_parts(2, 2))) + .deposit_asset(All, RECIPIENT) + .build(); + + let (mut vm, _) = instantiate_executor(SENDER, xcm.clone()); + + // Program runs successfully. + assert!(vm.bench_process(xcm).is_ok()); + + // Nothing is left in the `holding` register. + assert_eq!(get_first_fungible(vm.holding()), None); + // `BuyExecution` does not interact with the `fees` register. + assert_eq!(get_first_fungible(vm.fees()), None); + + // The recipient received all the assets in the holding register, so `100` that + // were withdrawn, minus the `4` from paying the execution fees. + assert_eq!(asset_list(RECIPIENT), [(Here, 96u128).into()]); +} + +// Tests the interaction between `PayFees` and `RefundSurplus`. +#[test] +fn fees_can_be_refunded() { + // Make sure the sender has enough funds to withdraw. + add_asset(SENDER, (Here, 100u128)); + + // Build xcm. + let xcm = Xcm::::builder() + .withdraw_asset((Here, 100u128)) + .pay_fees((Here, 10u128)) // 10% destined for fees, not more. + .deposit_asset(All, RECIPIENT) + .refund_surplus() + .deposit_asset(All, SENDER) + .build(); + + let (mut vm, _) = instantiate_executor(SENDER, xcm.clone()); + + // Program runs successfully. + assert!(vm.bench_process(xcm).is_ok()); + + // Nothing is left in the `holding` register. + assert_eq!(get_first_fungible(vm.holding()), None); + // Nothing was left in the `fees` register since it was refunded. + assert_eq!(get_first_fungible(vm.fees()), None); + + // The recipient received all the assets in the holding register, so `100` that + // were withdrawn, minus the `10` that were destinated for fee payment. + assert_eq!(asset_list(RECIPIENT), [(Here, 90u128).into()]); + + // The sender got back `6` from unused assets. + assert_eq!(asset_list(SENDER), [(Here, 6u128).into()]); +} + +// ===== Unhappy path ===== + +#[test] +fn putting_all_assets_in_pay_fees() { + // Make sure the sender has enough funds to withdraw. + add_asset(SENDER, (Here, 100u128)); + + // Build xcm. + let xcm = Xcm::::builder() + .withdraw_asset((Here, 100u128)) + .pay_fees((Here, 100u128)) // 100% destined for fees, this is not going to end well... + .deposit_asset(All, RECIPIENT) + .build(); + + let (mut vm, _) = instantiate_executor(SENDER, xcm.clone()); + + // Program runs successfully. + assert!(vm.bench_process(xcm).is_ok()); + + // Nothing is left in the `holding` register. + assert_eq!(get_first_fungible(vm.holding()), None); + // We destined `100` for fee payment, after `4` for execution fees, we are left with `96`. + assert_eq!(get_first_fungible(vm.fees()).unwrap(), (Here, 96u128).into()); + + // The recipient received no assets since they were all destined for fee payment. + assert_eq!(asset_list(RECIPIENT), []); +} + +#[test] +fn refunding_too_early() { + // Make sure the sender has enough funds to withdraw. + add_asset(SENDER, (Here, 100u128)); + + // Information to send messages. + // We don't care about the specifics since we're not actually sending them. + let query_response_info = + QueryResponseInfo { destination: Parent.into(), query_id: 0, max_weight: Weight::zero() }; + + // Build xcm. + let xcm = Xcm::::builder() + .withdraw_asset((Here, 100u128)) + .pay_fees((Here, 10u128)) // 10% destined for fees, not more. + .deposit_asset(All, RECIPIENT) + .refund_surplus() + .deposit_asset(All, SENDER) + // `refund_surplus` cleared the `fees` register. + // `holding` is used as a fallback, but we also cleared that. + // The instruction will error and the message won't be sent :(. + .report_error(query_response_info) + .build(); + + let (mut vm, _) = instantiate_executor(SENDER, xcm.clone()); + + // Program fails to run. + assert!(vm.bench_process(xcm).is_err()); + + // Nothing is left in the `holding` register. + assert_eq!(get_first_fungible(vm.holding()), None); + // Nothing was left in the `fees` register since it was refunded. + assert_eq!(get_first_fungible(vm.fees()), None); + + // The recipient received all the assets in the holding register, so `100` that + // were withdrawn, minus the `10` that were destinated for fee payment. + assert_eq!(asset_list(RECIPIENT), [(Here, 90u128).into()]); + + // The sender got back `6` from unused assets. + assert_eq!(asset_list(SENDER), [(Here, 6u128).into()]); + + // No messages were "sent". + assert_eq!(sent_xcm(), Vec::new()); +} diff --git a/polkadot/xcm/xcm-executor/src/tests/set_asset_claimer.rs b/polkadot/xcm/xcm-executor/src/tests/set_asset_claimer.rs new file mode 100644 index 0000000000000000000000000000000000000000..bc504b8db2a296000bcff463bbad5837e8534cd0 --- /dev/null +++ b/polkadot/xcm/xcm-executor/src/tests/set_asset_claimer.rs @@ -0,0 +1,138 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Unit tests related to the `fees` register and `PayFees` instruction. +//! +//! See [Fellowship RFC 105](https://github.com/polkadot-fellows/rfCs/pull/105) +//! and the [specification](https://github.com/polkadot-fellows/xcm-format) for more information. + +use codec::Encode; +use xcm::prelude::*; + +use super::mock::*; +use crate::XcmExecutor; + +#[test] +fn set_asset_claimer() { + let sender = Location::new(0, [AccountId32 { id: [0; 32], network: None }]); + let bob = Location::new(0, [AccountId32 { id: [2; 32], network: None }]); + + // Make sure the user has enough funds to withdraw. + add_asset(sender.clone(), (Here, 100u128)); + + // Build xcm. + let xcm = Xcm::::builder_unsafe() + // if withdrawing fails we're not missing any corner case. + .withdraw_asset((Here, 100u128)) + .clear_origin() + .set_asset_claimer(bob.clone()) + .pay_fees((Here, 10u128)) // 10% destined for fees, not more. + .build(); + + // We create an XCVM instance instead of calling `XcmExecutor::<_>::prepare_and_execute` so we + // can inspect its fields. + let mut vm = + XcmExecutor::::new(sender, xcm.using_encoded(sp_io::hashing::blake2_256)); + vm.message_weight = XcmExecutor::::prepare(xcm.clone()).unwrap().weight_of(); + + let result = vm.bench_process(xcm); + assert!(result.is_ok()); + assert_eq!(vm.asset_claimer(), Some(bob)); +} + +#[test] +fn do_not_set_asset_claimer_none() { + let sender = Location::new(0, [AccountId32 { id: [0; 32], network: None }]); + + // Make sure the user has enough funds to withdraw. + add_asset(sender.clone(), (Here, 100u128)); + + // Build xcm. + let xcm = Xcm::::builder_unsafe() + // if withdrawing fails we're not missing any corner case. + .withdraw_asset((Here, 100u128)) + .clear_origin() + .pay_fees((Here, 10u128)) // 10% destined for fees, not more. + .build(); + + // We create an XCVM instance instead of calling `XcmExecutor::<_>::prepare_and_execute` so we + // can inspect its fields. + let mut vm = + XcmExecutor::::new(sender, xcm.using_encoded(sp_io::hashing::blake2_256)); + vm.message_weight = XcmExecutor::::prepare(xcm.clone()).unwrap().weight_of(); + + let result = vm.bench_process(xcm); + assert!(result.is_ok()); + assert_eq!(vm.asset_claimer(), None); +} + +#[test] +fn trap_then_set_asset_claimer() { + let sender = Location::new(0, [AccountId32 { id: [0; 32], network: None }]); + let bob = Location::new(0, [AccountId32 { id: [2; 32], network: None }]); + + // Make sure the user has enough funds to withdraw. + add_asset(sender.clone(), (Here, 100u128)); + + // Build xcm. + let xcm = Xcm::::builder_unsafe() + // if withdrawing fails we're not missing any corner case. + .withdraw_asset((Here, 100u128)) + .clear_origin() + .trap(0u64) + .set_asset_claimer(bob) + .pay_fees((Here, 10u128)) // 10% destined for fees, not more. + .build(); + + // We create an XCVM instance instead of calling `XcmExecutor::<_>::prepare_and_execute` so we + // can inspect its fields. + let mut vm = + XcmExecutor::::new(sender, xcm.using_encoded(sp_io::hashing::blake2_256)); + vm.message_weight = XcmExecutor::::prepare(xcm.clone()).unwrap().weight_of(); + + let result = vm.bench_process(xcm); + assert!(result.is_err()); + assert_eq!(vm.asset_claimer(), None); +} + +#[test] +fn set_asset_claimer_then_trap() { + let sender = Location::new(0, [AccountId32 { id: [0; 32], network: None }]); + let bob = Location::new(0, [AccountId32 { id: [2; 32], network: None }]); + + // Make sure the user has enough funds to withdraw. + add_asset(sender.clone(), (Here, 100u128)); + + // Build xcm. + let xcm = Xcm::::builder_unsafe() + // if withdrawing fails we're not missing any corner case. + .withdraw_asset((Here, 100u128)) + .clear_origin() + .set_asset_claimer(bob.clone()) + .trap(0u64) + .pay_fees((Here, 10u128)) // 10% destined for fees, not more. + .build(); + + // We create an XCVM instance instead of calling `XcmExecutor::<_>::prepare_and_execute` so we + // can inspect its fields. + let mut vm = + XcmExecutor::::new(sender, xcm.using_encoded(sp_io::hashing::blake2_256)); + vm.message_weight = XcmExecutor::::prepare(xcm.clone()).unwrap().weight_of(); + + let result = vm.bench_process(xcm); + assert!(result.is_err()); + assert_eq!(vm.asset_claimer(), Some(bob)); +} diff --git a/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs b/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs index b6e303daaad891fd98918fee4729941458236dba..256f47fec4f050caaf445754654a851f98803621 100644 --- a/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs +++ b/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs @@ -39,6 +39,8 @@ pub enum FeeReason { InitiateReserveWithdraw, /// When the `InitiateTeleport` instruction is called. InitiateTeleport, + /// When the `InitiateTransfer` instruction is called. + InitiateTransfer, /// When the `QueryPallet` instruction is called. QueryPallet, /// When the `ExportMessage` instruction is called (and includes the network ID). diff --git a/polkadot/xcm/xcm-executor/src/traits/weight.rs b/polkadot/xcm/xcm-executor/src/traits/weight.rs index 61545c330621326e85884014d87cc3bfec674bd1..4e41aa5b4753039652f85eb0b665d7631fdcbdf1 100644 --- a/polkadot/xcm/xcm-executor/src/traits/weight.rs +++ b/polkadot/xcm/xcm-executor/src/traits/weight.rs @@ -26,7 +26,7 @@ pub trait WeightBounds { /// Return the maximum amount of weight that an attempted execution of this instruction could /// consume. - fn instr_weight(instruction: &Instruction) -> Result; + fn instr_weight(instruction: &mut Instruction) -> Result; } /// Charge for weight in order to execute XCM. diff --git a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs index 4a5de887500789944a7b06b227540947fbed1648..f0a5be908f693b0cfdc5a41d71559728a06d73fc 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs @@ -144,7 +144,7 @@ parameter_types! { pub const BaseXcmWeight: Weight = Weight::from_parts(100, 10); // Random value. pub const MaxInstructions: u32 = 100; pub const NativeTokenPerSecondPerByte: (AssetId, u128, u128) = (AssetId(HereLocation::get()), 1, 1); - pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(2000)].into(); + pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::ByGenesis([0; 32])), Parachain(2000)].into(); pub const HereLocation: Location = Location::here(); pub const RelayLocation: Location = Location::parent(); pub const MaxAssetsIntoHolding: u32 = 64; diff --git a/polkadot/xcm/xcm-simulator/example/src/tests.rs b/polkadot/xcm/xcm-simulator/example/src/tests.rs index 34c1feb6e946876bb62a5624915e414df65ed35e..bbac44ed8a1f1f3883b1546c7611198d400b1c21 100644 --- a/polkadot/xcm/xcm-simulator/example/src/tests.rs +++ b/polkadot/xcm/xcm-simulator/example/src/tests.rs @@ -46,7 +46,6 @@ fn dmp() { Parachain(1), Xcm(vec![Transact { origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), call: remark.encode().into(), }]), )); @@ -74,7 +73,6 @@ fn ump() { Parent, Xcm(vec![Transact { origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), call: remark.encode().into(), }]), )); @@ -102,7 +100,6 @@ fn xcmp() { (Parent, Parachain(2)), Xcm(vec![Transact { origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), call: remark.encode().into(), }]), )); @@ -383,7 +380,6 @@ fn reserve_asset_class_create_and_reserve_transfer() { let message = Xcm(vec![Transact { origin_kind: OriginKind::Xcm, - require_weight_at_most: Weight::from_parts(1_000_000_000, 1024 * 1024), call: parachain::RuntimeCall::from( pallet_uniques::Call::::create { collection: (Parent, 2u64).into(), diff --git a/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.zndsl b/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.zndsl index 80ecf6ae1b9be635d904733ec07a2b71f652c0cd..dce52505444e94ba2ea10fea4bc965609cb71fb2 100644 --- a/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.zndsl +++ b/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.zndsl @@ -8,4 +8,4 @@ validator-0: js-script ./force-register-paras.js with "2000" return is 0 within # assign core 0 to be shared by two paras, but only one exists validator-0: js-script ./assign-core.js with "0,2000,28800,2001,28800" return is 0 within 600 seconds -collator-2000: reports block height is at least 10 within 180 seconds +collator-2000: reports block height is at least 10 within 210 seconds diff --git a/prdoc/pr_4012.prdoc b/prdoc/pr_4012.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3a53e31a7fc6e7e32c371d2b734ad1c68cff6468 --- /dev/null +++ b/prdoc/pr_4012.prdoc @@ -0,0 +1,37 @@ +# 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: "`impl_runtime_apis!`: replace the use of `Self` with `Runtime`" + +doc: + - audience: Runtime Dev + description: | + Currently, if there is a type alias similar to `type HeaderFor` in the scope, it makes sense to expect that + `HeaderFor` and `HeaderFor` are equivalent. However, this is not the case. It currently leads to + a compilation error that `Self is not in scope`, which is confusing. This PR introduces a visitor, similar to + `CheckTraitDecl` in `decl_runtime_apis!`, `ReplaceSelfImpl`. It identifies usage of `Self` as a type argument in + `impl_runtime_apis!` and replaces `Self` with an explicit `Runtime` type. + + For example, the following example code will be transformed before expansion: + ```rust + impl apis::Core for Runtime { + fn initialize_block(header: &HeaderFor) -> ExtrinsicInclusionMode { + let _: HeaderFor = header.clone(); + RuntimeExecutive::initialize_block(header) + } + } + ``` + Instead, it will be passed to macro as: + ```rust + impl apis::Core for Runtime { + fn initialize_block(header: &HeaderFor) -> ExtrinsicInclusionMode { + let _: HeaderFor = header.clone(); + RuntimeExecutive::initialize_block(header) + } + } + ``` +crates: + - name: sp-api + bump: none + - name: sp-api-proc-macro + bump: none \ No newline at end of file diff --git a/prdoc/pr_4826.prdoc b/prdoc/pr_4826.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..daa4a77e3e8fac408184a898609f2c66ceaf90e9 --- /dev/null +++ b/prdoc/pr_4826.prdoc @@ -0,0 +1,69 @@ +# 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: XCMv5 + +doc: + - audience: [Runtime User, Runtime Dev] + description: | + Added XCMv5. + + This PR brings a new XCM version. + It's an amalgamation of multiple individual PRs: + - https://github.com/paritytech/polkadot-sdk/pull/6228 + - https://github.com/paritytech/polkadot-sdk/pull/6148 + - https://github.com/paritytech/polkadot-sdk/pull/5971 + - https://github.com/paritytech/polkadot-sdk/pull/5876 + - https://github.com/paritytech/polkadot-sdk/pull/5420 + - https://github.com/paritytech/polkadot-sdk/pull/5585 + + XCMv5 reduces the potential for bugs by: + - Removing the need to specify weight in Transact. + - Handling fees in a better way with `PayFees` instead of `BuyExecution`. + - Improves asset claiming with `SetAssetClaimer`. + + It also allows some new use-cases like: + - Sending both teleported and reserve asset transferred assets in the same cross-chain + transfer. + - Preserving the origin when doing cross-chain transfers. Allowing the use of Transact + in the same message as a cross-chain transfer. + + In version 5, it's expected to change usage of `BuyExecution` to `PayFees`. + While `BuyExecution` returns all leftover assets to holding, `PayFees` doesn't. + The only way to get funds back from those sent to `PayFees` is by using `RefundSurplus`. + Because of this, it's meant to be used alongside the new DryRunApi and XcmPaymentApi. + You first dry-run the XCM, get the fees needed, and put them in `PayFees`. + +crates: + - name: staging-xcm + bump: major + - name: staging-xcm-builder + bump: major + - name: staging-xcm-executor + bump: major + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: bridge-hub-rococo-runtime + bump: minor + - name: bridge-hub-westend-runtime + bump: minor + - name: coretime-rococo-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor + - name: people-rococo-runtime + bump: minor + - name: people-westend-runtime + bump: minor + - name: penpal-runtime + bump: minor + - name: rococo-runtime + bump: minor + - name: westend-runtime + bump: minor + - name: pallet-xcm-benchmarks + bump: minor + - name: pallet-multisig + bump: minor diff --git a/prdoc/pr_5390.prdoc b/prdoc/pr_5390.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..cfe6894324aa30904b537e44540827b5db80febf --- /dev/null +++ b/prdoc/pr_5390.prdoc @@ -0,0 +1,55 @@ +# 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: Remove NetworkIds for testnets Rococo and Westend + +doc: + - audience: [Runtime Dev, Runtime User] + description: | + Implemetation of https://github.com/polkadot-fellows/RFCs/pull/108, in the version 5 of XCM, + Remove `Westend` and `Rococo` from the included `NetworkId`s to improve the stability of the language. + + `NetworkId::Rococo` and `NetworkId::Westend` can just use `NetworkId::ByGenesis` by importing their genesis + block hash + +crates: + - name: staging-xcm + bump: major + - name: pallet-xcm-bridge-hub + bump: patch + - name: snowbridge-pallet-system + bump: patch + - name: asset-hub-rococo-runtime + bump: patch + - name: asset-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-runtime + bump: patch + - name: collectives-westend-runtime + bump: patch + - name: contracts-rococo-runtime + bump: patch + - name: coretime-rococo-runtime + bump: patch + - name: coretime-westend-runtime + bump: patch + - name: glutton-westend-runtime + bump: patch + - name: people-rococo-runtime + bump: patch + - name: people-westend-runtime + bump: patch + - name: penpal-runtime + bump: patch + - name: rococo-parachain-runtime + bump: patch + - name: xcm-runtime-apis + bump: patch + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: patch + - name: assets-common + bump: patch diff --git a/prdoc/pr_5420.prdoc b/prdoc/pr_5420.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..bf8a34569077ae2cac5a9eb2df8de20578639168 --- /dev/null +++ b/prdoc/pr_5420.prdoc @@ -0,0 +1,62 @@ +# 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: XCMv5 - Better fee mechanism + +doc: + - audience: + - Runtime User + - Runtime Dev + description: | + In XCMv5, there's a new instruction, `PayFees`, which is meant to be a replacement for `BuyExecution`. + This instruction takes only one parameter, the `asset` that you are willing to use for fee payment. + There's no parameter for limiting the weight, the amount of the `asset` you put in is the limit of + how much you're willing to pay. + This instruction works much better with delivery fees. + `BuyExecution` will still be around to ensure backwards-compatibility, however, the benefits of the new + instruction are a good incentive to switch. + The proposed workflow is to estimate fees using the `XcmPaymentApi` and `DryRunApi`, then to put those + values in `PayFees` and watch your message go knowing you covered all the necessary fees. + You can add a little bit more just in case if you want. + `RefundSurplus` now gets back all of the assets that were destined for fee payment so you can deposit + them somewhere. + BEWARE, make sure you're not sending any other message after you call `RefundSurplus`, if not, it will + error. + +crates: + - name: staging-xcm-executor + bump: minor + - name: staging-xcm-builder + bump: minor + - name: staging-xcm + bump: major + - name: rococo-runtime + bump: minor + - name: westend-runtime + bump: minor + - name: xcm-emulator + bump: major + - name: people-westend-runtime + bump: minor + - name: people-rococo-runtime + bump: minor + - name: coretime-rococo-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor + - name: bridge-hub-westend-runtime + bump: minor + - name: bridge-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: emulated-integration-tests-common + bump: minor + - name: xcm-procedural + bump: minor + - name: pallet-xcm-benchmarks + bump: minor + - name: snowbridge-pallet-system + bump: patch diff --git a/prdoc/pr_5585.prdoc b/prdoc/pr_5585.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d4b115413d4d180925d6ade1edc775501071b502 --- /dev/null +++ b/prdoc/pr_5585.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: Added SetAssetClaimer instruction to XCM v5. + +doc: + - audience: Runtime Dev + description: | + Added SetAssetClaimer implementation to XCM v5. With asset_claimer set users can retrieve their trapped assets + at any point in time without the need to go through OpenGov reclaim process. + +crates: +- name: bridge-hub-westend-emulated-chain + bump: minor +- name: asset-hub-westend-integration-tests + bump: minor +- name: asset-hub-rococo-runtime + bump: minor +- name: asset-hub-westend-runtime + bump: minor +- name: bridge-hub-rococo-runtime + bump: minor +- name: bridge-hub-westend-runtime + bump: minor +- name: coretime-rococo-runtime + bump: minor +- name: coretime-westend-runtime + bump: minor +- name: people-rococo-runtime + bump: minor +- name: people-westend-runtime + bump: minor +- name: penpal-runtime + bump: minor +- name: rococo-runtime + bump: minor +- name: westend-runtime + bump: minor +- name: staging-xcm + bump: minor +- name: staging-xcm-executor + bump: minor +- name: pallet-xcm-benchmarks + bump: minor +- name: pallet-multisig + bump: minor + diff --git a/prdoc/pr_5616.prdoc b/prdoc/pr_5616.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..16d81c291c30f72a24707ee52c01e29023dc0504 --- /dev/null +++ b/prdoc/pr_5616.prdoc @@ -0,0 +1,25 @@ +# 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: "PVF: drop backing jobs if it is too late" + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + Introduces the removal of backing jobs that have been back pressured for longer than `allowedAncestryLen`, as these candidates are no longer viable. + +crates: + - name: polkadot-overseer + bump: major + - name: polkadot-node-core-pvf + bump: major + - name: polkadot-node-subsystem-types + bump: major + - name: polkadot-node-core-approval-voting + bump: patch + - name: polkadot-node-core-backing + bump: patch + - name: polkadot-node-core-candidate-validation + bump: patch + - name: polkadot-node-core-dispute-coordinator + bump: patch diff --git a/prdoc/pr_5693.prdoc b/prdoc/pr_5693.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..d8afae7ba0bca14906968b4d7cc6ff9586870b03 --- /dev/null +++ b/prdoc/pr_5693.prdoc @@ -0,0 +1,84 @@ +title: Remove `sp_runtime::RuntimeString` and replace with `Cow<'static, str>` or + `String` depending on use case +doc: + - audience: Runtime Dev + description: | + Deprecate `RuntimeString`, replace with `String` or `Cow<'static, str>` where appropriate. + + For downstream projects the upgrade will primarily consist of following two changes: + ```diff + #[sp_version::runtime_version] + pub const VERSION: RuntimeVersion = RuntimeVersion { + - spec_name: create_runtime_str!("statemine"), + - impl_name: create_runtime_str!("statemine"), + + spec_name: alloc::borrow::Cow::Borrowed("statemine"), + + impl_name: alloc::borrow::Cow::Borrowed("statemine"), + ``` + ```diff + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + - ) -> Result, sp_runtime::RuntimeString> { + + ) -> Result, alloc::string::String> { + ``` + SCALE encoding/decoding remains the same as before, but serde encoding in runtime has changed from bytes to string (it was like this in `std` environment already). +crates: +- name: cumulus-client-network + bump: major +- name: cumulus-client-pov-recovery + bump: major +- name: cumulus-pallet-parachain-system + bump: major +- name: asset-hub-rococo-runtime + bump: major +- name: asset-hub-westend-runtime + bump: major +- name: bridge-hub-rococo-runtime + bump: major +- name: bridge-hub-westend-runtime + bump: major +- name: collectives-westend-runtime + bump: major +- name: contracts-rococo-runtime + bump: major +- name: coretime-rococo-runtime + bump: major +- name: coretime-westend-runtime + bump: major +- name: glutton-westend-runtime + bump: major +- name: people-rococo-runtime + bump: major +- name: people-westend-runtime + bump: major +- name: penpal-runtime + bump: major +- name: rococo-parachain-runtime + bump: major +- name: rococo-runtime + bump: major +- name: westend-runtime + bump: major +- name: staging-chain-spec-builder + bump: major +- name: sc-consensus-pow + bump: major +- name: sc-executor + bump: major +- name: frame-benchmarking + bump: major +- name: polkadot-sdk-frame + bump: major +- name: frame-support + bump: major +- name: frame-system + bump: major +- name: sp-api + bump: major +- name: sp-genesis-builder + bump: major +- name: sp-runtime + bump: major +- name: sp-version-proc-macro + bump: major +- name: sp-version + bump: major diff --git a/prdoc/pr_5737.prdoc b/prdoc/pr_5737.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a122e4574a9cecba6160ede60055a4443182591b --- /dev/null +++ b/prdoc/pr_5737.prdoc @@ -0,0 +1,25 @@ +title: Make syncing service an argument of `build_network` + +doc: + - audience: Node Dev + description: | + `build_network` is accompanied with lower-level `build_network_advanced` with simpler API that does not create + syncing engine internally, but instead takes a handle to syncing service as an argument. In most cases typical + syncing engine with polkadot syncing strategy and default block downloader can be created with newly introduced + `sc_service::build_default_syncing_engine()` function, but lower-level `build_default_block_downloader` also + exists for those needing more customization. + + These changes allow developers higher than ever control over syncing implementation, but `build_network` is still + available for easier high-level usage. + +crates: + - name: cumulus-client-service + bump: patch + - name: polkadot-service + bump: patch + - name: sc-consensus + bump: major + - name: sc-service + bump: major + - name: sc-network-sync + bump: major diff --git a/prdoc/pr_5876.prdoc b/prdoc/pr_5876.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4e2b8a5c8aadd5b79b2053e34b14d360ce692481 --- /dev/null +++ b/prdoc/pr_5876.prdoc @@ -0,0 +1,99 @@ +# 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: (XCMv5) implement RFC#100, add new InitiateTransfer instruction + +doc: + - audience: Runtime Dev + description: | + There's a new XCM instruction in v5: InitiateTransfer. + It's meant as a general instruction that will do everything (and more) currently + done by InitiateTeleport, InitiateReserveWithdraw and DepositReserveAsset. + Its main feature is the ability to do cross-chains transfers mixing teleported and + reserve transferred assets. + ```rust + /// Specify which type of asset transfer is required for a particular `(asset, dest)` combination. + pub enum AssetTransferFilter { + /// teleport assets matching `AssetFilter` to `dest` + Teleport(AssetFilter), + /// reserve-transfer assets matching `AssetFilter` to `dest`, using the local chain as reserve + ReserveDeposit(AssetFilter), + /// reserve-transfer assets matching `AssetFilter` to `dest`, using `dest` as reserve + ReserveWithdraw(AssetFilter), + } + /// Cross-chain transfer matching `assets` in the holding register as follows: + /// + /// Assets in the holding register are matched using the given list of `AssetTransferFilter`s, + /// they are then transferred based on their specified transfer type: + /// + /// - teleport: burn local assets and append a `ReceiveTeleportedAsset` XCM instruction to + /// the XCM program to be sent onward to the `dest` location, + /// + /// - reserve deposit: place assets under the ownership of `dest` within this consensus system + /// (i.e. its sovereign account), and append a `ReserveAssetDeposited` XCM instruction + /// to the XCM program to be sent onward to the `dest` location, + /// + /// - reserve withdraw: burn local assets and append a `WithdrawAsset` XCM instruction + /// to the XCM program to be sent onward to the `dest` location, + /// + /// The onward XCM is then appended a `ClearOrigin` to allow safe execution of any following + /// custom XCM instructions provided in `remote_xcm`. + /// + /// The onward XCM also potentially contains a `BuyExecution` instruction based on the presence + /// of the `remote_fees` parameter (see below). + /// + /// If a transfer requires going through multiple hops, an XCM program can compose this instruction + /// to be used at every chain along the path, describing that specific leg of the transfer. + /// + /// Parameters: + /// - `dest`: The location of the transfer next hop. + /// - `remote_fees`: If set to `Some(asset_xfer_filter)`, the single asset matching + /// `asset_xfer_filter` in the holding register will be transferred first in the remote XCM + /// program, followed by a `BuyExecution(fee)`, then rest of transfers follow. + /// This guarantees `remote_xcm` will successfully pass a `AllowTopLevelPaidExecutionFrom` barrier. + /// - `remote_xcm`: Custom instructions that will be executed on the `dest` chain. Note that + /// these instructions will be executed after a `ClearOrigin` so their origin will be `None`. + /// + /// Safety: No concerns. + /// + /// Kind: *Command*. + /// + InitiateTransfer { + destination: Location, + remote_fees: Option, + assets: Vec, + remote_xcm: Xcm<()>, + } + ``` + +crates: + - name: emulated-integration-tests-common + bump: major + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: bridge-hub-rococo-runtime + bump: minor + - name: bridge-hub-westend-runtime + bump: minor + - name: coretime-rococo-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor + - name: people-rococo-runtime + bump: minor + - name: people-westend-runtime + bump: minor + - name: rococo-runtime + bump: minor + - name: westend-runtime + bump: minor + - name: pallet-xcm-benchmarks + bump: minor + - name: staging-xcm + bump: major + - name: staging-xcm-executor + bump: major diff --git a/prdoc/pr_5883.prdoc b/prdoc/pr_5883.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..96225a89bc99ad4a96eff864b1784b8f3d61f791 --- /dev/null +++ b/prdoc/pr_5883.prdoc @@ -0,0 +1,15 @@ +title: 'statement-distribution RFC103 implementation' + +doc: + - audience: Node Dev + description: | + Introduces checks for the new candidate descriptor fields: `core_index` and `session_index`. + +crates: + - name: polkadot-statement-distribution + bump: minor + - name: polkadot-primitives + bump: major + - name: polkadot-primitives-test-helpers + bump: major + diff --git a/prdoc/pr_5908.prdoc b/prdoc/pr_5908.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..8f05819451a0651688f382e903c34032fdb45919 --- /dev/null +++ b/prdoc/pr_5908.prdoc @@ -0,0 +1,14 @@ +title: "collation-generation: use v2 receipts" + +doc: + - audience: Node Dev + description: | + Implementation of [RFC 103](https://github.com/polkadot-fellows/RFCs/pull/103) for the collation-generation subsystem. + Also removes the usage of AsyncBackingParams. + +crates: + - name: polkadot-node-collation-generation + bump: major + validate: false + - name: polkadot-primitives + bump: minor diff --git a/prdoc/pr_5954.prdoc b/prdoc/pr_5954.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2c9efcce7a6a1e7432e18e01c034479a5080b18a --- /dev/null +++ b/prdoc/pr_5954.prdoc @@ -0,0 +1,19 @@ +# 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: "templates: make node compilation optional" + +doc: + - audience: [Node Dev, Runtime Dev] + description: | + Node compilation for minimal and parachain templates is made optional, not part of the + templates `default-members` list. At the same time, we introduce OmniNode as alternative + to run the templates. + +crates: + - name: polkadot-omni-node + bump: patch + - name: polkadot-omni-node-lib + bump: patch + - name: staging-chain-spec-builder + bump: patch diff --git a/prdoc/pr_5961.prdoc b/prdoc/pr_5961.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..46a5be8e49d510cc37e7e45e89ce3645bd4621c9 --- /dev/null +++ b/prdoc/pr_5961.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: "Bounties Pallet: add `approve_bounty_with_curator` call" + +doc: + - audience: [Runtime Dev, Runtime User] + description: | + Adds `approve_bounty_with_curator` call to the bounties pallet to combine `approve_bounty` and `propose_curator` into one call. If `unassign_curator` is called after `approve_bounty_with_curator` the process falls back to the previous flow of calling `propose_curator` separately. Introduces a new `ApprovedWithCurator` bounty status when bounty is approved with curator. + +crates: + - name: pallet-bounties + bump: major + - name: rococo-runtime + bump: minor diff --git a/prdoc/pr_5971.prdoc b/prdoc/pr_5971.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4b1afc4c268a593345d568dad4818d8778440572 --- /dev/null +++ b/prdoc/pr_5971.prdoc @@ -0,0 +1,66 @@ +# 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: XCMv5 InitiateTransfer can preserve original origin across chains. + +doc: + - audience: Runtime User + description: | + The new InitiateTransfer instruction can preserve the original origin across chains by + setting `preserve_origin: true` in the instruction itself. + When it's set to true, it will append after the inner XCM, an `AliasOrigin` instruction + instead of the usual `ClearOrigin`. + This instruction will try to alias to the original origin, thus preserving it. + This only works if the chain receiving the transfer supports the aliasing operation. + If not, `preserve_origin: false` works as before and will never fail because of this. + - audience: Runtime Dev + description: | + The new InitiateTransfer instruction can preserve the original origin across chains by + setting `preserve_origin: true` in the instruction itself. + When it's set to true, it will append after the inner XCM, an `AliasOrigin` instruction + instead of the usual `ClearOrigin`. + This instruction will try to alias to the original origin, thus preserving it. + + Beware: This only works if the following two rules are followed by the chain receiving such + a message. + - Alias to interior locations is valid (the exact same behaviour as DescendOrigin) + - AssetHub can alias everything (most importantly sibling accounts and ethereum). + These can be set with the `Aliasers` configuration item, with the following adapters: + - AliasChildLocation + - AliasOriginRootUsingFilter with AssetHub and Everything + An example of the first one can be seen in `asset-hub-westend` and of the second one in + `penpal-runtime`. + +crates: + - name: staging-xcm + bump: minor + - name: staging-xcm-builder + bump: minor + - name: staging-xcm-executor + bump: minor + - name: pallet-xcm-benchmarks + bump: minor + - name: snowbridge-router-primitives + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: bridge-hub-rococo-runtime + bump: minor + - name: bridge-hub-westend-runtime + bump: minor + - name: coretime-rococo-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor + - name: people-rococo-runtime + bump: minor + - name: people-westend-runtime + bump: minor + - name: penpal-runtime + bump: minor + - name: rococo-runtime + bump: minor + - name: westend-runtime + bump: minor diff --git a/prdoc/pr_5984.prdoc b/prdoc/pr_5984.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3b6651bac6b93e5aeb0cca06316de7d281f01e33 --- /dev/null +++ b/prdoc/pr_5984.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: Add page information to staking::PayoutStarted event + +doc: + - audience: Runtime User + description: | + Adds page index that is claimed, and optional next page that can be claimed. If next is none, then the page is the + last one. + +crates: + - name: pallet-staking + bump: major diff --git a/prdoc/pr_6011.prdoc b/prdoc/pr_6011.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e053b607085f77a4c4c1685870c2eb633aa4ec20 --- /dev/null +++ b/prdoc/pr_6011.prdoc @@ -0,0 +1,12 @@ +title: "collator protocol: validate descriptor version on the validator side" + +doc: + - audience: Node Dev + description: | + Implement checks needed for RFC 103 https://github.com/polkadot-fellows/rfcs/pull/103 in the validator + side of the collator protocol. + + +crates: + - name: polkadot-collator-protocol + bump: major diff --git a/prdoc/pr_6016.prdoc b/prdoc/pr_6016.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..967c3a766068516bba4bbcbd9c1c5ad0cbe0f6ed --- /dev/null +++ b/prdoc/pr_6016.prdoc @@ -0,0 +1,15 @@ +title: Litep2p network backend do not disconnect all peers on SetReservedPeers command + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + Previously, when the `SetReservedPeers` was received, all peers except the new + reserved peers were disconnected. + This PR ensures that previously reserved nodes are kept connected as regular nodes if + enough slots are available. + While at it, this PR excludes reserved peers from the candidates of peers obtained from + the peerstore. + +crates: + - name: sc-network + bump: patch diff --git a/prdoc/pr_6077.prdoc b/prdoc/pr_6077.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f222fb27ce07be2ec2838e98a6ffeeccf018d485 --- /dev/null +++ b/prdoc/pr_6077.prdoc @@ -0,0 +1,9 @@ +title: Add networking benchmarks for libp2p +doc: +- audience: node_dev + description: |- + Adds benchmarks for Notifications and RequestResponse protocols with libp2p implementation + +crates: +- name: sc-network + validate: false diff --git a/prdoc/pr_6163.prdoc b/prdoc/pr_6163.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c8571f80ed52f34ed55aca38b9d043560f58db9e --- /dev/null +++ b/prdoc/pr_6163.prdoc @@ -0,0 +1,13 @@ +# 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: Expose more syncing types to enable custom syncing strategy + +doc: + - audience: Node Dev + description: | + Exposes additional syncing types to facilitate the development of a custom syncing strategy. + +crates: + - name: sc-network-sync + bump: patch diff --git a/prdoc/pr_6169.prdoc b/prdoc/pr_6169.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0416fe008051fc6045dba2e58cfbcf2bed377c66 --- /dev/null +++ b/prdoc/pr_6169.prdoc @@ -0,0 +1,63 @@ +title: "[Deprecation] deprecate treasury `spend_local` call and related items" + +doc: + - audience: Runtime Dev + description: | + Deprecates `spend_local` from the treasury pallet and items associated with it. + + ### Migration + + #### For users who were using only `spend_local` before + + To replace `spend_local` functionality configure `Paymaster` pallet configuration to be `PayFromAccount` and configure `AssetKind` to be `()` and use `spend` call instead. + This way `spend` call will function as deprecated `spend_local`. + + Example: + ``` + impl pallet_treasury::Config for Runtime { + .. + type AssetKind = (); + type Paymaster = PayFromAccount; + // convert balance 1:1 ratio with native currency + type BalanceConverter = UnityAssetBalanceConversion; + .. + } + ``` + + #### For users who were already using `spend` with all other assets, except the native asset + + Use `NativeOrWithId` type for `AssetKind` and have a `UnionOf` for native and non-native assets, then use that with `PayAssetFromAccount`. + + Example from `kitchensink-runtime`: + ``` + // Union of native currency and assets + pub type NativeAndAssets = + UnionOf, AccountId>; + + impl pallet_treasury::Config for Runtime { + .. + type AssetKind = NativeOrWithId; + type Paymaster = PayAssetFromAccount; + type BalanceConverter = AssetRate; + .. + } + + // AssetRate pallet configuration + impl pallet_asset_rate::Config for Runtime { + .. + type Currency = Balances; + type AssetKind = NativeOrWithId; + .. + } + ``` + + +crates: +- name: pallet-treasury + bump: patch +- name: pallet-bounties + bump: patch +- name: pallet-child-bounties + bump: patch +- name: pallet-tips + bump: patch diff --git a/prdoc/pr_6187.prdoc b/prdoc/pr_6187.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..92d801987969ee77363d842cf8ba63827bbca510 --- /dev/null +++ b/prdoc/pr_6187.prdoc @@ -0,0 +1,16 @@ +title: '[pallet-revive] rework balance transfers' +doc: +- audience: Runtime Dev + description: |- + This PR removes the `transfer` syscall and changes balance transfers to make the existential deposit (ED) fully transparent for contracts. + + The `transfer` API is removed since there is no corresponding EVM opcode and transferring via a call introduces barely any overhead. + + We make the ED transparent to contracts by transferring the ED from the call origin to nonexistent accounts. Without this change, transfers to nonexistant accounts will transfer the supplied value minus the ED from the contracts viewpoint, and consequentially fail if the supplied value lies below the ED. Changing this behavior removes the need for contract code to handle this rather annoying corner case and aligns better with the EVM. The EVM charges a similar deposit from the gas meter, so transferring the ED from the call origin is practically the same as the call origin pays for gas. +crates: +- name: pallet-revive + bump: major +- name: pallet-revive-fixtures + bump: patch +- name: pallet-revive-uapi + bump: major diff --git a/prdoc/pr_6217.prdoc b/prdoc/pr_6217.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2fa800b58d2cfea4d9834636467b5f83c90f87d8 --- /dev/null +++ b/prdoc/pr_6217.prdoc @@ -0,0 +1,24 @@ +title: 'Unify and harden UMP signal checks in check_core_index' +doc: +- audience: [Runtime Dev, Node Dev] + description: | + Refactors and hardens the core index checks on the candidate commitments. + Also adds a utility for skipping the ump signals + +crates: +- name: cumulus-client-consensus-aura + bump: patch +- name: cumulus-pallet-parachain-system + bump: patch +- name: cumulus-primitives-core + bump: major +- name: polkadot-node-collation-generation + bump: major +- name: polkadot-node-core-candidate-validation + bump: patch +- name: polkadot-node-subsystem-util + bump: patch +- name: polkadot-primitives + bump: major +- name: polkadot-runtime-parachains + bump: patch diff --git a/prdoc/pr_6221.prdoc b/prdoc/pr_6221.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..57c81b322f9294a331fd3a0233c63a237fdb7d10 --- /dev/null +++ b/prdoc/pr_6221.prdoc @@ -0,0 +1,10 @@ +title: "snowbridge: allow account conversion for Ethereum accounts" + +doc: + - audience: Runtime Dev + description: | + Replaced `GlobalConsensusEthereumConvertsFor` with `EthereumLocationsConverterFor` that allows `Location` + to `AccountId` conversion for the Ethereum network root as before, but also for Ethereum contracts and accounts. +crates: + - name: snowbridge-router-primitives + bump: major diff --git a/prdoc/pr_6228.prdoc b/prdoc/pr_6228.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4512adf01bbb5c12678ce4b6a9bb9dbe2d789889 --- /dev/null +++ b/prdoc/pr_6228.prdoc @@ -0,0 +1,50 @@ +# 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: Transact without having to specify weight + +doc: + - audience: [Runtime User, Runtime Dev] + description: | + In XCMv5, it's no longer required to pass in the expected weight when using + `Transact`. + This was made to remove a whole class of bugs where the weight specified + was not enough. + +crates: + - name: staging-xcm + bump: major + - name: staging-xcm-executor + bump: major + - name: snowbridge-router-primitives + bump: minor + - name: emulated-integration-tests-common + bump: minor + - name: cumulus-ping + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: parachains-runtimes-test-utils + bump: minor + - name: bridge-hub-westend-runtime + bump: minor + - name: bridge-hub-rococo-runtime + bump: minor + - name: coretime-rococo-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor + - name: people-westend-runtime + bump: minor + - name: people-rococo-runtime + bump: minor + - name: rococo-runtime + bump: minor + - name: westend-runtime + bump: minor + - name: pallet-xcm-benchmarks + bump: minor + - name: xcm-simulator-example + bump: minor diff --git a/prdoc/pr_6255.prdoc b/prdoc/pr_6255.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7b69717b5c2d597fe17286e8932565149bf8c8a7 --- /dev/null +++ b/prdoc/pr_6255.prdoc @@ -0,0 +1,34 @@ +title: '[pallet-child-bounties] Index child bounties by parent bounty' +doc: +- audience: Runtime Dev + description: | + Index child bounties by their parent bounty, ensuring that their indexes are independent of + child bounties from other parent bounties. This will allow for predictable indexes and the + ability to batch creation and approval calls together. + + ### Migration for Runtime Pallet Instance + Use `migration::v1::MigrateToV1Impl` storage migration type to translate ids for the active + child bounties and migrate the state to the new schema. + + ### Migration for Clients + - Use new `ParentTotalChildBounties` storage item to iterate over child bounties for a certain + parent bounty; + - Use new `ChildBountyDescriptionsV1` storage item to get the bounty description instead of + removed `ChildBountyDescriptions`; + - Use `V0ToV1ChildBountyIds` storage item to look up the new child bounty id for a given + old child bounty id; + - Update the child bounty account id derivation from `PalletId + "cb" + child_id` to + `PalletId + "cb" + bounty_id + child_id`. + + ### Additional Notes + - The `ChildBountyCount` storage item is deprecated and will be remove in May 2025. + +crates: +- name: pallet-child-bounties + bump: major +- name: pallet-bounties + bump: major +- name: rococo-runtime + bump: major +- name: sp-core + bump: minor diff --git a/prdoc/pr_6257.prdoc b/prdoc/pr_6257.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..45f9810108ef70f97da7b703d9180dbaa229ab94 --- /dev/null +++ b/prdoc/pr_6257.prdoc @@ -0,0 +1,10 @@ +title: 'fix claim queue size when validator groups count is smaller' +doc: +- audience: Runtime Dev + description: 'Fixes a bug introduced in https://github.com/paritytech/polkadot-sdk/pull/5461, where the claim queue + would contain entries even if the validator groups storage is empty (which happens during the first session). + This PR sets the claim queue core count to be the minimum between the num_cores param and the number of validator groups.' + +crates: +- name: polkadot-runtime-parachains + bump: patch diff --git a/prdoc/pr_6261.prdoc b/prdoc/pr_6261.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..20ee5563bcfd50b910c86a8b9561da8a41544704 --- /dev/null +++ b/prdoc/pr_6261.prdoc @@ -0,0 +1,13 @@ +# 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: Add missing events to identity pallet + +doc: + - audience: Runtime Dev + description: | + Extrinsics from `pallet_identity` that were missing an event emission on success now emit one. + +crates: + - name: pallet-identity + bump: major diff --git a/prdoc/pr_6290.prdoc b/prdoc/pr_6290.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a05d0cd15acf710a21d33865244c1826cd6355b9 --- /dev/null +++ b/prdoc/pr_6290.prdoc @@ -0,0 +1,11 @@ +title: Migrate pallet-transaction-storage and pallet-indices to benchmark v2 +doc: +- audience: Runtime Dev + description: |- + Part of: + #6202 +crates: +- name: pallet-indices + bump: patch +- name: pallet-transaction-storage + bump: patch diff --git a/prdoc/pr_6291.prdoc b/prdoc/pr_6291.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..73053c9d47bd000ba75952f3e1fffa78d0b89981 --- /dev/null +++ b/prdoc/pr_6291.prdoc @@ -0,0 +1,9 @@ +title: migrate pallet-remarks to v2 bench syntax +doc: +- audience: Runtime Dev + description: |- + Part of: + * #6202 +crates: +- name: pallet-remark + bump: patch diff --git a/prdoc/pr_6295.prdoc b/prdoc/pr_6295.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c7e4282208ee28d57d6696293d0214b3b7b66f1a --- /dev/null +++ b/prdoc/pr_6295.prdoc @@ -0,0 +1,10 @@ +title: Migrate pallet-im-online benchmark to v2 +doc: +- audience: Runtime Dev + description: |- + Part of: + + - #6202. +crates: +- name: pallet-im-online + bump: patch diff --git a/prdoc/pr_6296.prdoc b/prdoc/pr_6296.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dcc4ad9095f6c668f012e450b390533af11089da --- /dev/null +++ b/prdoc/pr_6296.prdoc @@ -0,0 +1,8 @@ +title: Migrate pallet-glutton benchmark to v2 +doc: +- audience: Runtime Dev + description: |- + Update `pallet-glutton` to benchmarks v2. +crates: +- name: pallet-glutton + bump: patch diff --git a/prdoc/pr_6298.prdoc b/prdoc/pr_6298.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..fa8d73b11943272fbb962985b813e4a01f94fa43 --- /dev/null +++ b/prdoc/pr_6298.prdoc @@ -0,0 +1,25 @@ +title: Populate authority DHT records with public listen addresses + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + This PR populates the authority DHT records with public listen addresses if any. + The change effectively ensures that addresses are added to the DHT record in the + following order: + 1. Public addresses provided by CLI `--public-addresses` + 2. Maximum of 4 public (global) listen addresses (if any) + 3. Any external addresses discovered from the network (ie from `/identify` protocol) + + While at it, this PR adds the following constraints on the number of addresses: + - Total number of addresses cached is bounded at 16 (increased from 10). + - A maximum number of 32 addresses are published to DHT records (previously unbounded). + - A maximum of 4 global listen addresses are utilized. + + This PR replaces the following warning: + `WARNING: No public address specified, validator node may not be reachable.` + with a more descriptive one originated from the authority-discovery + mechanism itself: `No public addresses configured and no global listen addresses found`. + +crates: + - name: sc-authority-discovery + bump: patch diff --git a/prdoc/pr_6299.prdoc b/prdoc/pr_6299.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..fe8906f6e1533d9b6a0393c9180f3302b3e322fb --- /dev/null +++ b/prdoc/pr_6299.prdoc @@ -0,0 +1,8 @@ +title: migrate pallet-recovery to benchmark V2 syntax +doc: +- audience: Runtime Dev + description: |- + migrate pallet-recovery to benchmark V2 syntax +crates: +- name: pallet-recovery + bump: patch diff --git a/prdoc/pr_6314.prdoc b/prdoc/pr_6314.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2ebbc68158d590d6b8d33abe77752139f1e2db06 --- /dev/null +++ b/prdoc/pr_6314.prdoc @@ -0,0 +1,10 @@ +title: Migrate pallet-elections-phragmen benchmark to v2 and improve doc +doc: +- audience: Runtime Dev + description: |- + Part of: + + - #6202. +crates: +- name: pallet-elections-phragmen + bump: patch diff --git a/prdoc/pr_6315.prdoc b/prdoc/pr_6315.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..6fc070b43b353e2ca5165c62edc94254775dbd98 --- /dev/null +++ b/prdoc/pr_6315.prdoc @@ -0,0 +1,8 @@ +title: Migrate pallet-election-provider-support-benchmarking benchmark to v2 +doc: +- audience: Runtime Dev + description: |- + Migrate pallet-election-provider-support-benchmarking benchmark to v2 +crates: +- name: pallet-election-provider-support-benchmarking + bump: patch diff --git a/prdoc/pr_6316.prdoc b/prdoc/pr_6316.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..00ad8699ff8511e54e7b877b205b3de966d841b4 --- /dev/null +++ b/prdoc/pr_6316.prdoc @@ -0,0 +1,8 @@ +title: Migrate pallet-election-provider-multi-phase benchmark to v2 and improve doc +doc: +- audience: Runtime Dev + description: |- + Migrate pallet-election-provider-multi-phase benchmark to v2 and improve doc +crates: +- name: pallet-election-provider-multi-phase + bump: patch diff --git a/prdoc/pr_6317.prdoc b/prdoc/pr_6317.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4034ab3f3012b752fcb8cc6cac1ad554b93582cf --- /dev/null +++ b/prdoc/pr_6317.prdoc @@ -0,0 +1,12 @@ +title: eth-rpc fixes +doc: +- audience: Runtime Dev + description: | + Various fixes for the release of eth-rpc & ah-westend-runtime: + - Bump asset-hub westend spec version + - Fix the status of the Receipt to properly report failed transactions + - Fix value conversion between native and eth decimal representation + +crates: +- name: asset-hub-westend-runtime + bump: patch diff --git a/prdoc/pr_6318.prdoc b/prdoc/pr_6318.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b44a982f59920bfa70e9aefaf991843969253f7b --- /dev/null +++ b/prdoc/pr_6318.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: Refactor pallet claims + +doc: + - audience: Runtime Dev + description: | + Adds bounds on stored types for pallet claims. + Migrates benchmarking from v1 to v2 for pallet claims. + +crates: + - name: polkadot-runtime-common + bump: patch diff --git a/prdoc/pr_6337.prdoc b/prdoc/pr_6337.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..aeab61cdf933e3aa9c652501e4f31107cba5980c --- /dev/null +++ b/prdoc/pr_6337.prdoc @@ -0,0 +1,17 @@ +# 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: Don't expose metadata for Runtime APIs that haven't been implemented + +doc: + - audience: Runtime User + description: | + Prior to this PR, the metadata for runtime APIs would contain all methods for the + latest version of each API, regardless of which version a runtime implements. This + PR fixes that, so that the runtime API metadata reflects what is actually implemented. + +crates: + - name: sp-api-proc-macro + bump: major + - name: sp-consensus-babe + bump: patch \ No newline at end of file diff --git a/prdoc/pr_6349.prdoc b/prdoc/pr_6349.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..40f02712c99aec8e40845e1bf099bd56896a5786 --- /dev/null +++ b/prdoc/pr_6349.prdoc @@ -0,0 +1,44 @@ +title: "runtimes: presets are provided as config patches" + +doc: + - audience: Runtime Dev + description: | + This PR introduces usage of build_struct_json_patch macro in all + runtimes (also guides) within the code base. It also fixes macro to support + field init shorthand, and Struct Update syntax which were missing in original + implementation. + +crates: + - name: frame-support + bump: major + + - name: westend-runtime + bump: patch + + - name: rococo-runtime + bump: patch + + - name: asset-hub-westend-runtime + bump: patch + + - name: bridge-hub-rococo-runtime + bump: patch + + - name: bridge-hub-westend-runtime + bump: patch + + - name: collectives-westend-runtime + bump: patch + + - name: minimal-template-runtime + bump: patch + + - name: solochain-template-runtime + bump: patch + + - name: parachain-template-runtime + bump: patch + + - name: polkadot-sdk-docs-first-runtime + bump: patch + diff --git a/prdoc/pr_6353.prdoc b/prdoc/pr_6353.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..8a5a152628a0bd3e0db0bc25fbe6f0bba2076eaa --- /dev/null +++ b/prdoc/pr_6353.prdoc @@ -0,0 +1,10 @@ +title: Update litep2p network backend to version 0.8.0 + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + Release 0.8.0 of litep2p includes several improvements and memory leak fixes enhancing the stability and performance of the litep2p network backend. + +crates: + - name: sc-network + bump: patch diff --git a/prdoc/pr_6357.prdoc b/prdoc/pr_6357.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b3155b1a605055f6a461a975f777f0d2ed0a0610 --- /dev/null +++ b/prdoc/pr_6357.prdoc @@ -0,0 +1,20 @@ +# 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: New runtime api that returns the associated pool accounts with a nomination pool. + +doc: + - audience: Runtime User + description: | + Each nomination pool has two associated pot accounts: the bonded account, where funds are pooled for staking, and + the reward account. This update introduces a runtime api that clients can query to retrieve these accounts. + +crates: + - name: westend-runtime + bump: minor + - name: kitchensink-runtime + bump: minor + - name: pallet-nomination-pools + bump: minor + - name: pallet-nomination-pools-runtime-api + bump: minor diff --git a/prdoc/pr_6360.prdoc b/prdoc/pr_6360.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..270af29e37af18b63dfa02031eb782658aaa939a --- /dev/null +++ b/prdoc/pr_6360.prdoc @@ -0,0 +1,9 @@ +title: '[eth-rpc] proxy /health' +doc: +- audience: Runtime Dev + description: |- + make the eth-rpc proxy /health and /health/readiness from the proxied substrate chain + see #4802 +crates: +- name: pallet-revive-eth-rpc + bump: minor diff --git a/prdoc/pr_6365.prdoc b/prdoc/pr_6365.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b99a7ae4035edafc4b49d45285f92333604f16ec --- /dev/null +++ b/prdoc/pr_6365.prdoc @@ -0,0 +1,10 @@ +title: 'pallet-revive: Use `RUSTUP_TOOLCHAIN` if set' +doc: +- audience: Runtime Dev + description: We were not passing through the `RUSTUP_TOOLCHAIN` variable to the + `build.rs` script of our fixtures. This means that setting the toolchain like + `cargo +1.81 build` had no effect on the fixture build. It would always fall back + to the default toolchain. +crates: +- name: pallet-revive-fixtures + bump: major diff --git a/prdoc/pr_6373.prdoc b/prdoc/pr_6373.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..04758d9dd41fda19976f3e2a6d5850835f0a699f --- /dev/null +++ b/prdoc/pr_6373.prdoc @@ -0,0 +1,8 @@ +title: '`chain-spec-builder`: info about patch/full files added' +doc: +- audience: Runtime User + description: There was no good example of what is patch and full genesis config + file. Some explanation and example were added to the `chain-spec-builder` doc. +crates: +- name: staging-chain-spec-builder + bump: patch diff --git a/prdoc/pr_6380.prdoc b/prdoc/pr_6380.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..72853bcf230c8ccdcbfe6df72889588d414c9190 --- /dev/null +++ b/prdoc/pr_6380.prdoc @@ -0,0 +1,11 @@ +title: Do not propagate external addr with different peerIDs + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + External addresses that belong to a different peerID are no longer + propagated to the higher layers of the networking backends. + +crates: + - name: sc-network + bump: patch diff --git a/prdoc/pr_6382.prdoc b/prdoc/pr_6382.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ac6821c1100a1900fbdbdf5d5ae9fb4ae2a4afd6 --- /dev/null +++ b/prdoc/pr_6382.prdoc @@ -0,0 +1,12 @@ +title: 'gensis-config: patching default `RuntimeGenesisConfig` fixed' +doc: +- audience: Node Dev + description: |- + This PR fixes issue reported in #6306. + It changes the behavior of `sc_chain_spec::json_patch::merge` function which no longer removes any keys from the base JSON object. + +crates: +- name: staging-chain-spec-builder + bump: major +- name: sc-chain-spec + bump: major diff --git a/prdoc/pr_6384.prdoc b/prdoc/pr_6384.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2ea0bc1043c33264fa429c58929e657293c32868 --- /dev/null +++ b/prdoc/pr_6384.prdoc @@ -0,0 +1,12 @@ +title: Relax requirements on `assign_core`. +doc: +- audience: Runtime Dev + description: |- + Relax requirements for `assign_core` so that it accepts updates for the last scheduled entry. + This will allow the coretime chain to split up assignments into multiple + messages, which allows for interlacing down to single block granularity. + + Fixes: https://github.com/paritytech/polkadot-sdk/issues/6102 +crates: +- name: polkadot-runtime-parachains + bump: major diff --git a/prdoc/pr_6406.prdoc b/prdoc/pr_6406.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9da4462263b9e4ecfed6895953fd204fb3ba53c8 --- /dev/null +++ b/prdoc/pr_6406.prdoc @@ -0,0 +1,9 @@ +title: 'make prospective-parachains debug logs less spammy' +doc: +- audience: [Node Dev, Node Operator] + description: | + Demote some of the frequent prospective-parachains debug logs to trace level and prefer printing aggregate debug logs. + +crates: +- name: polkadot-node-core-prospective-parachains + bump: patch diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 057e0bbdcefc751c2d970633fcbc017d15e0c8ca..008cac4ef8a88b3b147abf98047e6d5ecbe0b0ba 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -32,7 +32,6 @@ use frame_system_rpc_runtime_api::AccountNonceApi; use futures::prelude::*; use kitchensink_runtime::RuntimeApi; use node_primitives::Block; -use polkadot_sdk::sc_service::build_polkadot_syncing_strategy; use sc_client_api::{Backend, BlockBackend}; use sc_consensus_babe::{self, SlotProportion}; use sc_network::{ @@ -514,16 +513,6 @@ pub fn new_full_base::Hash>>( Vec::default(), )); - let syncing_strategy = build_polkadot_syncing_strategy( - config.protocol_id(), - config.chain_spec.fork_id(), - &mut net_config, - Some(WarpSyncConfig::WithProvider(warp_sync)), - client.clone(), - &task_manager.spawn_handle(), - config.prometheus_config.as_ref().map(|config| &config.registry), - )?; - let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -533,7 +522,7 @@ pub fn new_full_base::Hash>>( spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - syncing_strategy, + warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), block_relay: None, metrics, })?; diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 49fc360e399fa9018fe451e277b0a5b1b7d33956..68fb58106e03ac9d8877fa20dac62a9e78501b31 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -24,6 +24,13 @@ extern crate alloc; +#[cfg(feature = "runtime-benchmarks")] +use pallet_asset_rate::AssetKindFactory; +#[cfg(feature = "runtime-benchmarks")] +use pallet_treasury::ArgumentsFactory; +#[cfg(feature = "runtime-benchmarks")] +use polkadot_sdk::sp_core::crypto::FromEntropy; + use polkadot_sdk::*; use alloc::{vec, vec::Vec}; @@ -47,7 +54,7 @@ use frame_support::{ }, tokens::{ imbalance::ResolveAssetTo, nonfungibles_v2::Inspect, pay::PayAssetFromAccount, - GetSalary, PayFromAccount, + Fortitude::Polite, GetSalary, PayFromAccount, Preservation::Preserve, }, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, Contains, Currency, EitherOfDiverse, EnsureOriginWithArg, EqualPrivilegeOnly, Imbalance, InsideBoth, @@ -76,6 +83,7 @@ use pallet_identity::legacy::IdentityInfo; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_nfts::PalletFeatures; use pallet_nis::WithMaximumOf; +use pallet_nomination_pools::PoolId; use pallet_revive::{evm::runtime::EthExtra, AddressMapper}; use pallet_session::historical as pallet_session_historical; // Can't use `FungibleAdapter` here until Treasury pallet migrates to fungibles @@ -95,7 +103,6 @@ use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ - create_runtime_str, curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ @@ -161,8 +168,8 @@ pub fn wasm_binary_unwrap() -> &'static [u8] { /// Runtime version. #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("node"), - impl_name: create_runtime_str!("substrate-node"), + spec_name: alloc::borrow::Cow::Borrowed("node"), + impl_name: alloc::borrow::Cow::Borrowed("substrate-node"), authoring_version: 10, // Per convention: if the runtime behavior changes, increment spec_version // and set impl_version to 0. If only runtime @@ -267,6 +274,36 @@ impl Contains> for TxPauseWhitelistedCalls { } } +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetRateArguments; +#[cfg(feature = "runtime-benchmarks")] +impl AssetKindFactory> for AssetRateArguments { + fn create_asset_kind(seed: u32) -> NativeOrWithId { + if seed % 2 > 0 { + NativeOrWithId::Native + } else { + NativeOrWithId::WithId(seed / 2) + } + } +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct PalletTreasuryArguments; +#[cfg(feature = "runtime-benchmarks")] +impl ArgumentsFactory, AccountId> for PalletTreasuryArguments { + fn create_asset_kind(seed: u32) -> NativeOrWithId { + if seed % 2 > 0 { + NativeOrWithId::Native + } else { + NativeOrWithId::WithId(seed / 2) + } + } + + fn create_beneficiary(seed: [u8; 32]) -> AccountId { + AccountId::from_entropy(&mut seed.as_slice()).unwrap() + } +} + impl pallet_tx_pause::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -1262,15 +1299,15 @@ impl pallet_treasury::Config for Runtime { type WeightInfo = pallet_treasury::weights::SubstrateWeight; type MaxApprovals = MaxApprovals; type SpendOrigin = EnsureWithSuccess, AccountId, MaxBalance>; - type AssetKind = u32; + type AssetKind = NativeOrWithId; type Beneficiary = AccountId; type BeneficiaryLookup = Indices; - type Paymaster = PayAssetFromAccount; + type Paymaster = PayAssetFromAccount; type BalanceConverter = AssetRate; type PayoutPeriod = SpendPayoutPeriod; type BlockNumberProvider = System; #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); + type BenchmarkHelper = PalletTreasuryArguments; } impl pallet_asset_rate::Config for Runtime { @@ -1278,11 +1315,11 @@ impl pallet_asset_rate::Config for Runtime { type RemoveOrigin = EnsureRoot; type UpdateOrigin = EnsureRoot; type Currency = Balances; - type AssetKind = u32; + type AssetKind = NativeOrWithId; type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_asset_rate::weights::SubstrateWeight; #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); + type BenchmarkHelper = AssetRateArguments; } parameter_types! { @@ -1429,6 +1466,7 @@ impl pallet_revive::Config for Runtime { 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. } impl pallet_sudo::Config for Runtime { @@ -2969,15 +3007,15 @@ impl_runtime_apis! { NominationPools::api_pending_rewards(who).unwrap_or_default() } - fn points_to_balance(pool_id: pallet_nomination_pools::PoolId, points: Balance) -> Balance { + fn points_to_balance(pool_id: PoolId, points: Balance) -> Balance { NominationPools::api_points_to_balance(pool_id, points) } - fn balance_to_points(pool_id: pallet_nomination_pools::PoolId, new_funds: Balance) -> Balance { + fn balance_to_points(pool_id: PoolId, new_funds: Balance) -> Balance { NominationPools::api_balance_to_points(pool_id, new_funds) } - fn pool_pending_slash(pool_id: pallet_nomination_pools::PoolId) -> Balance { + fn pool_pending_slash(pool_id: PoolId) -> Balance { NominationPools::api_pool_pending_slash(pool_id) } @@ -2985,7 +3023,7 @@ impl_runtime_apis! { NominationPools::api_member_pending_slash(member) } - fn pool_needs_delegate_migration(pool_id: pallet_nomination_pools::PoolId) -> bool { + fn pool_needs_delegate_migration(pool_id: PoolId) -> bool { NominationPools::api_pool_needs_delegate_migration(pool_id) } @@ -2997,9 +3035,13 @@ impl_runtime_apis! { NominationPools::api_member_total_balance(member) } - fn pool_balance(pool_id: pallet_nomination_pools::PoolId) -> Balance { + fn pool_balance(pool_id: PoolId) -> Balance { NominationPools::api_pool_balance(pool_id) } + + fn pool_accounts(pool_id: PoolId) -> (AccountId, AccountId) { + NominationPools::api_pool_accounts(pool_id) + } } impl pallet_staking_runtime_api::StakingApi for Runtime { @@ -3166,8 +3208,9 @@ impl_runtime_apis! { impl pallet_revive::ReviveApi for Runtime { fn balance(address: H160) -> Balance { + use frame_support::traits::fungible::Inspect; let account = ::AddressMapper::to_account_id(&address); - Balances::usable_balance(account) + Balances::reducible_balance(&account, Preserve, Polite) } fn nonce(address: H160) -> Nonce { @@ -3576,7 +3619,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{baseline, Benchmarking, BenchmarkBatch}; use sp_storage::TrackedStorageKey; diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml index f2fe8cb7e166c7b5f8a70e73582be0c9c173e2a1..b71e935a918f2e52a5a8324b011ec6891c68a510 100644 --- a/substrate/bin/utils/chain-spec-builder/Cargo.toml +++ b/substrate/bin/utils/chain-spec-builder/Cargo.toml @@ -21,15 +21,28 @@ path = "bin/main.rs" name = "chain-spec-builder" [lib] -crate-type = ["rlib"] +# Docs tests are not needed since the code samples that would be executed +# are exercised already in the context of unit/integration tests, by virtue +# of using a combination of encapsulation in functions + `docify::export`. +# This is a practice we should use for new code samples if any. +doctest = false [dependencies] clap = { features = ["derive"], workspace = true } +docify = { workspace = true } log = { workspace = true, default-features = true } -sc-chain-spec = { features = ["clap"], workspace = true, default-features = true } +sc-chain-spec = { features = [ + "clap", +], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } serde = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } [dev-dependencies] substrate-test-runtime = { workspace = true } +cmd_lib = { workspace = true } +docify = { workspace = true } + +[features] +# `cargo build --feature=generate-readme` updates the `README.md` file. +generate-readme = [] diff --git a/substrate/bin/utils/chain-spec-builder/README.docify.md b/substrate/bin/utils/chain-spec-builder/README.docify.md new file mode 100644 index 0000000000000000000000000000000000000000..75d05bccfe0d2387c9cb6e21e7ad134fdfcbb229 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/README.docify.md @@ -0,0 +1,146 @@ +# Chain Spec Builder + +Substrate's chain spec builder utility. + +A chain-spec is short for `chain-specification`. See the [`sc-chain-spec`](https://crates.io/docs.rs/sc-chain-spec/latest/sc_chain_spec) +for more information. + +_Note:_ this binary is a more flexible alternative to the `build-spec` subcommand, contained in typical Substrate-based nodes. +This particular binary is capable of interacting with [`sp-genesis-builder`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/index.html) +implementation of any provided runtime allowing to build chain-spec JSON files. + +See [`ChainSpecBuilderCmd`](https://docs.rs/staging-chain-spec-builder/6.0.0/staging_chain_spec_builder/enum.ChainSpecBuilderCmd.html) +for a list of available commands. + +## Installation + +```bash +cargo install staging-chain-spec-builder +``` + +_Note:_ `chain-spec-builder` binary is published on [crates.io](https://crates.io) under +[`staging-chain-spec-builder`](https://crates.io/crates/staging-chain-spec-builder) due to a name conflict. + +## Usage + +Please note that below usage is backed by integration tests. The commands' examples are wrapped +around by the `bash!(...)` macro calls. + +### Generate chains-spec using default config from runtime + +Query the default genesis config from the provided runtime WASM blob and use it in the chain spec. + + + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +runtime function is called. + +### Display the runtime's default `GenesisConfig` + + + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +runtime function is called. + +### Display the `GenesisConfig` preset with given name + + + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +runtime function is called. + +### List the names of `GenesisConfig` presets provided by runtime + + + +_Note:_ [`GenesisBuilder::preset_names`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.preset_names) +runtime function is called. + +### Generate chain spec using runtime provided genesis config preset + +Patch the runtime's default genesis config with the named preset provided by the runtime and generate the plain +version of chain spec: + + + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +runtime functions are called. + +### Generate raw storage chain spec using genesis config patch + +Patch the runtime's default genesis config with provided `patch.json` and generate raw +storage (`-s`) version of chain spec: + + + +Refer to [*patch file*](#patch-file) for some details on the patch file format. + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +and +[`GenesisBuilder::build_state`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.build_state) +runtime functions are called. + +### Generate raw storage chain spec using full genesis config + +Build the chain spec using provided full genesis config json file. No defaults will be used: + + + +Refer to [*full config file*](#full-genesis-config-file) for some details on the full file format. + +_Note_: [`GenesisBuilder::build_state`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.build_state) +runtime function is called. + +### Generate human readable chain spec using provided genesis config patch + + + +Refer to [*patch file*](#patch-file) for some details on the patch file format. + +### Generate human readable chain spec using provided full genesis config + + + +Refer to [*full config file*](#full-genesis-config-file) for some details on the full file format. + + +## Patch and full genesis config files +This section provides details on the files that can be used with `create patch` or `create full` subcommands. + +### Patch file +The patch file for genesis config contains the key-value pairs valid for given runtime, that needs to be customized, + e.g: +```ignore +{ + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "sudo": { + "key": "5Ff3iXP75ruzroPWRP2FYBHWnmGGBSb63857BgnzCoXNxfPo" + } +} +``` +The rest of genesis config keys will be initialized with default values. + +### Full genesis config file +The full genesis config file must contain values for *all* the keys present in the genesis config for given runtime. The +format of the file is similar to patch format. Example is not provided here as it heavily depends on the runtime. + +### Extra tools + +The `chain-spec-builder` provides also some extra utilities: [`VerifyCmd`](https://docs.rs/staging-chain-spec-builder/latest/staging_chain_spec_builder/struct.VerifyCmd.html), +[`ConvertToRawCmd`](https://docs.rs/staging-chain-spec-builder/latest/staging_chain_spec_builder/struct.ConvertToRawCmd.html), +[`UpdateCodeCmd`](https://docs.rs/staging-chain-spec-builder/latest/staging_chain_spec_builder/struct.UpdateCodeCmd.html). diff --git a/substrate/bin/utils/chain-spec-builder/README.md b/substrate/bin/utils/chain-spec-builder/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a85b37826139bc791000c3332ebd9201d588a967 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/README.md @@ -0,0 +1,184 @@ +# Chain Spec Builder + +Substrate's chain spec builder utility. + +A chain-spec is short for `chain-specification`. See the [`sc-chain-spec`](https://crates.io/docs.rs/sc-chain-spec/latest/sc_chain_spec) +for more information. + +_Note:_ this binary is a more flexible alternative to the `build-spec` subcommand, contained in typical Substrate-based nodes. +This particular binary is capable of interacting with [`sp-genesis-builder`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/index.html) +implementation of any provided runtime allowing to build chain-spec JSON files. + +See [`ChainSpecBuilderCmd`](https://docs.rs/staging-chain-spec-builder/6.0.0/staging_chain_spec_builder/enum.ChainSpecBuilderCmd.html) +for a list of available commands. + +## Installation + +```bash +cargo install staging-chain-spec-builder +``` + +_Note:_ `chain-spec-builder` binary is published on [crates.io](https://crates.io) under +[`staging-chain-spec-builder`](https://crates.io/crates/staging-chain-spec-builder) due to a name conflict. + +## Usage + +Please note that below usage is backed by integration tests. The commands' examples are wrapped +around by the `bash!(...)` macro calls. + +### Generate chains-spec using default config from runtime + +Query the default genesis config from the provided runtime WASM blob and use it in the chain spec. + +```rust,ignore +bash!( + chain-spec-builder -c "/dev/stdout" create -r $runtime_path default +) +``` + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +runtime function is called. + +### Display the runtime's default `GenesisConfig` + +```rust,ignore +bash!( + chain-spec-builder display-preset -r $runtime_path +) +``` + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +runtime function is called. + +### Display the `GenesisConfig` preset with given name + +```rust,ignore +fn cmd_display_preset(runtime_path: &str) -> String { + bash!( + chain-spec-builder display-preset -r $runtime_path -p "staging" + ) +} +``` + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +runtime function is called. + +### List the names of `GenesisConfig` presets provided by runtime + +```rust,ignore +bash!( + chain-spec-builder list-presets -r $runtime_path +) +``` + +_Note:_ [`GenesisBuilder::preset_names`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.preset_names) +runtime function is called. + +### Generate chain spec using runtime provided genesis config preset + +Patch the runtime's default genesis config with the named preset provided by the runtime and generate the plain +version of chain spec: + +```rust,ignore +bash!( + chain-spec-builder -c "/dev/stdout" create --relay-chain "dev" --para-id 1000 -r $runtime_path named-preset "staging" +) +``` + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +runtime functions are called. + +### Generate raw storage chain spec using genesis config patch + +Patch the runtime's default genesis config with provided `patch.json` and generate raw +storage (`-s`) version of chain spec: + +```rust,ignore +bash!( + chain-spec-builder -c "/dev/stdout" create -s -r $runtime_path patch "tests/input/patch.json" +) +``` + +Refer to [*patch file*](#patch-file) for some details on the patch file format. + +_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset) +and +[`GenesisBuilder::build_state`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.build_state) +runtime functions are called. + +### Generate raw storage chain spec using full genesis config + +Build the chain spec using provided full genesis config json file. No defaults will be used: + +```rust,ignore +bash!( + chain-spec-builder -c "/dev/stdout" create -s -r $runtime_path full "tests/input/full.json" +) +``` + +Refer to [*full config file*](#full-genesis-config-file) for some details on the full file format. + +_Note_: [`GenesisBuilder::build_state`](https://docs.rs/sp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.build_state) +runtime function is called. + +### Generate human readable chain spec using provided genesis config patch + +```rust,ignore +bash!( + chain-spec-builder -c "/dev/stdout" create -r $runtime_path patch "tests/input/patch.json" +) +``` + +Refer to [*patch file*](#patch-file) for some details on the patch file format. + +### Generate human readable chain spec using provided full genesis config + +```rust,ignore +bash!( + chain-spec-builder -c "/dev/stdout" create -r $runtime_path full "tests/input/full.json" +) +``` + +Refer to [*full config file*](#full-genesis-config-file) for some details on the full file format. + + +## Patch and full genesis config files +This section provides details on the files that can be used with `create patch` or `create full` subcommands. + +### Patch file +The patch file for genesis config contains the key-value pairs valid for given runtime, that needs to be customized, + e.g: +```ignore +{ + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "sudo": { + "key": "5Ff3iXP75ruzroPWRP2FYBHWnmGGBSb63857BgnzCoXNxfPo" + } +} +``` +The rest of genesis config keys will be initialized with default values. + +### Full genesis config file +The full genesis config file must contain values for *all* the keys present in the genesis config for given runtime. The +format of the file is similar to patch format. Example is not provided here as it heavily depends on the runtime. + +### Extra tools + +The `chain-spec-builder` provides also some extra utilities: [`VerifyCmd`](https://docs.rs/staging-chain-spec-builder/latest/staging_chain_spec_builder/struct.VerifyCmd.html), +[`ConvertToRawCmd`](https://docs.rs/staging-chain-spec-builder/latest/staging_chain_spec_builder/struct.ConvertToRawCmd.html), +[`UpdateCodeCmd`](https://docs.rs/staging-chain-spec-builder/latest/staging_chain_spec_builder/struct.UpdateCodeCmd.html). diff --git a/substrate/bin/utils/chain-spec-builder/src/lib.rs b/substrate/bin/utils/chain-spec-builder/src/lib.rs index 629edcf685685d73312fefe1a967a8585465cfaf..73c2868b3312e9c1f66540830000d14b36e4bdb1 100644 --- a/substrate/bin/utils/chain-spec-builder/src/lib.rs +++ b/substrate/bin/utils/chain-spec-builder/src/lib.rs @@ -15,107 +15,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . - -//! Substrate's chain spec builder utility. -//! -//! A chain-spec is short for `chain-configuration`. See the [`sc-chain-spec`] for more information. -//! -//! Note that this binary is analogous to the `build-spec` subcommand, contained in typical -//! substrate-based nodes. This particular binary is capable of interacting with -//! [`sp-genesis-builder`] implementation of any provided runtime allowing to build chain-spec JSON -//! files. -//! -//! See [`ChainSpecBuilderCmd`] for a list of available commands. -//! -//! ## Typical use-cases. -//! ##### Generate chains-spec using default config from runtime. -//! -//! Query the default genesis config from the provided `runtime.wasm` and use it in the chain -//! spec. -//! ```bash -//! chain-spec-builder create -r runtime.wasm default -//! ``` -//! -//! _Note:_ [`GenesisBuilder::get_preset`][sp-genesis-builder-get-preset] runtime function is -//! called. -//! -//! -//! ##### Display the runtime's default `GenesisConfig` -//! -//! Displays the content of the runtime's default `GenesisConfig` -//! ```bash -//! chain-spec-builder display-preset -r runtime.wasm -//! ``` -//! -//! _Note:_ [`GenesisBuilder::get_preset`][sp-genesis-builder-get-preset] runtime function is called. -//! -//! ##### Display the `GenesisConfig` preset with given name -//! -//! Displays the content of the `GenesisConfig` preset for given name -//! ```bash -//! chain-spec-builder display-preset -r runtime.wasm -p "staging" -//! ``` -//! -//! _Note:_ [`GenesisBuilder::get_preset`][sp-genesis-builder-get-preset] runtime function is called. -//! -//! ##### List the names of `GenesisConfig` presets provided by runtime. -//! -//! Displays the names of the presets of `GenesisConfigs` provided by runtime. -//! ```bash -//! chain-spec-builder list-presets -r runtime.wasm -//! ``` -//! -//! _Note:_ [`GenesisBuilder::preset_names`][sp-genesis-builder-list] runtime function is called. -//! -//! ##### Generate chain spec using runtime provided genesis config preset. -//! -//! Patch the runtime's default genesis config with the named preset provided by the runtime and generate the plain -//! version of chain spec: -//! ```bash -//! chain-spec-builder create -r runtime.wasm named-preset "staging" -//! ``` -//! -//! _Note:_ [`GenesisBuilder::get_preset`][sp-genesis-builder-get-preset] and [`GenesisBuilder::build_state`][sp-genesis-builder-build] runtime functions are called. -//! -//! ##### Generate raw storage chain spec using genesis config patch. -//! -//! Patch the runtime's default genesis config with provided `patch.json` and generate raw -//! storage (`-s`) version of chain spec: -//! ```bash -//! chain-spec-builder create -s -r runtime.wasm patch patch.json -//! ``` -//! -//! _Note:_ [`GenesisBuilder::build_state`][sp-genesis-builder-build] runtime function is called. -//! -//! ##### Generate raw storage chain spec using full genesis config. -//! -//! Build the chain spec using provided full genesis config json file. No defaults will be used: -//! ```bash -//! chain-spec-builder create -s -r runtime.wasm full full-genesis-config.json -//! ``` -//! -//! _Note_: [`GenesisBuilder::build_state`][sp-genesis-builder-build] runtime function is called. -//! -//! ##### Generate human readable chain spec using provided genesis config patch. -//! ```bash -//! chain-spec-builder create -r runtime.wasm patch patch.json -//! ``` -//! -//! ##### Generate human readable chain spec using provided full genesis config. -//! ```bash -//! chain-spec-builder create -r runtime.wasm full full-genesis-config.json -//! ``` -//! -//! ##### Extra tools. -//! The `chain-spec-builder` provides also some extra utilities: [`VerifyCmd`], [`ConvertToRawCmd`], -//! [`UpdateCodeCmd`]. -//! -//! [`sc-chain-spec`]: ../sc_chain_spec/index.html -//! [`node-cli`]: ../node_cli/index.html -//! [`sp-genesis-builder`]: ../sp_genesis_builder/index.html -//! [sp-genesis-builder-build]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.build_state -//! [sp-genesis-builder-list]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.preset_names -//! [sp-genesis-builder-get-preset]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset +#![doc = include_str!("../README.md")] +#[cfg(feature = "generate-readme")] +docify::compile_markdown!("README.docify.md", "README.md"); use clap::{Parser, Subcommand}; use sc_chain_spec::{ @@ -359,19 +261,19 @@ impl ChainSpecBuilder { .map_err(|e| format!("Conversion to json failed: {e}"))?; // We want to extract only raw genesis ("genesis::raw" key), and apply it as a patch - // for the original json file. However, the file also contains original plain - // genesis ("genesis::runtimeGenesis") so set it to null so the patch will erase it. + // for the original json file. genesis_json.as_object_mut().map(|map| { map.retain(|key, _| key == "genesis"); - map.get_mut("genesis").map(|genesis| { - genesis.as_object_mut().map(|genesis_map| { - genesis_map - .insert("runtimeGenesis".to_string(), serde_json::Value::Null); - }); - }); }); let mut org_chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?; + + // The original plain genesis ("genesis::runtimeGenesis") is no longer needed, so + // just remove it: + org_chain_spec_json + .get_mut("genesis") + .and_then(|genesis| genesis.as_object_mut()) + .and_then(|genesis| genesis.remove("runtimeGenesis")); json_patch::merge(&mut org_chain_spec_json, genesis_json); let chain_spec_json = serde_json::to_string_pretty(&org_chain_spec_json) @@ -391,16 +293,6 @@ impl ChainSpecBuilder { let presets = caller .preset_names() .map_err(|e| format!("getting default config from runtime should work: {e}"))?; - let presets: Vec = presets - .into_iter() - .map(|preset| { - String::from( - TryInto::<&str>::try_into(&preset) - .unwrap_or_else(|_| "cannot display preset id") - .to_string(), - ) - }) - .collect(); println!("{}", serde_json::json!({"presets":presets}).to_string()); }, ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime, preset_name }) => { diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json index 6d127b6c0aca09fce0924291b5a5615b302b443f..10071670179abe09e4ee431d2b5538f2eacc860d 100644 --- a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json @@ -16,9 +16,18 @@ "config": { "babe": { "authorities": [ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", - "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 1 + ], + [ + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + 1 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 1 + ] ], "epochConfig": { "allowed_slots": "PrimaryAndSecondaryVRFSlots", diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_default.json b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_default.json new file mode 100644 index 0000000000000000000000000000000000000000..203b6716cb26883a31ff3c90500f0d0d810dc78f --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_default.json @@ -0,0 +1,36 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} \ No newline at end of file diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_full_plain.json b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_full_plain.json new file mode 100644 index 0000000000000000000000000000000000000000..26868c3241a192c7b9b2b257ef5b6ae962839e70 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_full_plain.json @@ -0,0 +1,66 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "config": { + "babe": { + "authorities": [ + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 1 + ], + [ + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + 1 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 1 + ] + ], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 2, + 4 + ] + } + }, + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 2000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 2000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + }, + "system": {} + } + } + } +} \ No newline at end of file diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_full_raw.json b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_full_raw.json new file mode 100644 index 0000000000000000000000000000000000000000..523a266fc439269936a2cbfe2abbfab28095440f --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_full_raw.json @@ -0,0 +1,39 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000001cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c0100000000000000186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e0100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000001cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c0100000000000000186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e0100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0200000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x000000000000000000000000010000000000000000008d49fd1a07000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x000000000000000000000000010000000000000000008d49fd1a07000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0080faca73f91f00" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_with_named_preset_staging.json b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_with_named_preset_staging.json new file mode 100644 index 0000000000000000000000000000000000000000..5cf51554b2cba8d9456f31a20159addc9f9983e5 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_with_named_preset_staging.json @@ -0,0 +1,39 @@ +{ + "bootNodes": [], + "chainType": "Live", + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "patch": { + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" + ] + } + } + } + }, + "id": "custom", + "name": "Custom", + "para_id": 1000, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "protocolId": null, + "relay_chain": "dev", + "telemetryEndpoints": null +} \ No newline at end of file diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_with_patch_plain.json b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_with_patch_plain.json new file mode 100644 index 0000000000000000000000000000000000000000..b243534c0d61f5f63959065b450cc1870631272a --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_with_patch_plain.json @@ -0,0 +1,42 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "patch": { + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + } + } + } + } +} \ No newline at end of file diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_with_patch_raw.json b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_with_patch_raw.json new file mode 100644 index 0000000000000000000000000000000000000000..c4ac1cbe8ea197c125a88ccbfefa8b3fda06644e --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/create_with_patch_raw.json @@ -0,0 +1,37 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/doc/display_preset.json b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/display_preset.json new file mode 100644 index 0000000000000000000000000000000000000000..6aa6799af771d197636dea3b3add454150cc2c4c --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/display_preset.json @@ -0,0 +1 @@ +{"babe":{"authorities":[],"epochConfig":{"allowed_slots":"PrimaryAndSecondaryVRFSlots","c":[1,4]}},"balances":{"balances":[]},"substrateTest":{"authorities":[]},"system":{}} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/doc/display_preset_staging.json b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/display_preset_staging.json new file mode 100644 index 0000000000000000000000000000000000000000..b0c8e40c23a9796a1206206da456e2c660154697 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/display_preset_staging.json @@ -0,0 +1 @@ +{"balances":{"balances":[["5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",1000000000000000],["5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",1000000000000000]]},"substrateTest":{"authorities":["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY","5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"]}} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/doc/list_presets.json b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/list_presets.json new file mode 100644 index 0000000000000000000000000000000000000000..882462391888962cea1bf2cb527b2e45a62101a9 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/doc/list_presets.json @@ -0,0 +1 @@ +{"presets":["foobar","staging"]} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/full.json b/substrate/bin/utils/chain-spec-builder/tests/input/full.json index f05e3505a2bb662042483c32aa6210fc74ba3124..e34aede52cbe5dc8a2e1d9b474354532519e6ecf 100644 --- a/substrate/bin/utils/chain-spec-builder/tests/input/full.json +++ b/substrate/bin/utils/chain-spec-builder/tests/input/full.json @@ -1,9 +1,9 @@ { "babe": { "authorities": [ - "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", - "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", - "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1], + ["5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", 1], + ["5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", 1] ], "epochConfig": { "allowed_slots": "PrimaryAndSecondaryVRFSlots", diff --git a/substrate/bin/utils/chain-spec-builder/tests/test.rs b/substrate/bin/utils/chain-spec-builder/tests/test.rs index f553f05f20a049aecaab7e06d86e01d3685a326e..5ac687d75fd4454e1e2452e0b001bc435279efd9 100644 --- a/substrate/bin/utils/chain-spec-builder/tests/test.rs +++ b/substrate/bin/utils/chain-spec-builder/tests/test.rs @@ -19,7 +19,10 @@ use std::fs::File; use clap::Parser; + +use cmd_lib::spawn_with_output; use sc_chain_spec::update_code_in_json_chain_spec; +use serde_json::{from_reader, from_str, Value}; use staging_chain_spec_builder::ChainSpecBuilder; // note: the runtime path will not be read, runtime code will be set directly, to avoid hassle with @@ -28,6 +31,44 @@ const DUMMY_PATH: &str = "fake-runtime-path"; const OUTPUT_FILE: &str = "/tmp/chain_spec_builder.test_output_file.json"; +// Used for running commands visually pleasing in doc tests. +macro_rules! bash( + ( chain-spec-builder $($a:tt)* ) => {{ + let bin_path = env!("CARGO_BIN_EXE_chain-spec-builder"); + spawn_with_output!( + $bin_path $($a)* + ) + .expect("a process running. qed") + .wait_with_output() + .expect("to get output. qed.") + }} +); + +// Used specifically in docs tests. +fn doc_assert(output: String, expected_output_path: &str, remove_code: bool) { + let expected: Value = + from_reader(File::open(expected_output_path).unwrap()).expect("a valid JSON. qed."); + let output = if remove_code { + let mut output: Value = from_str(output.as_str()).expect("a valid JSON. qed."); + // Remove code sections gracefully for both `plain` & `raw`. + output + .get_mut("genesis") + .and_then(|inner| inner.get_mut("runtimeGenesis")) + .and_then(|inner| inner.as_object_mut()) + .and_then(|inner| inner.remove("code")); + output + .get_mut("genesis") + .and_then(|inner| inner.get_mut("raw")) + .and_then(|inner| inner.get_mut("top")) + .and_then(|inner| inner.as_object_mut()) + .and_then(|inner| inner.remove("0x3a636f6465")); + output + } else { + from_str::(output.as_str()).expect("a valid JSON. qed.") + }; + assert_eq!(output, expected); +} + /// Asserts that the JSON in output file matches the JSON in expected file. /// /// This helper function reads the JSON content from the file at `OUTPUT_FILE + suffix` path. If the @@ -192,3 +233,165 @@ fn test_add_code_substitute() { builder.run().unwrap(); assert_output_eq_expected(true, SUFFIX, "tests/expected/add_code_substitute.json"); } + +#[docify::export_content] +fn cmd_create_default(runtime_path: &str) -> String { + bash!( + chain-spec-builder -c "/dev/stdout" create -r $runtime_path default + ) +} + +#[test] +fn create_default() { + doc_assert( + cmd_create_default( + substrate_test_runtime::WASM_BINARY_PATH.expect("to be a valid path. qed"), + ), + "tests/expected/doc/create_default.json", + true, + ); +} + +#[docify::export_content] +fn cmd_display_default_preset(runtime_path: &str) -> String { + bash!( + chain-spec-builder display-preset -r $runtime_path + ) +} + +#[test] +fn display_default_preset() { + doc_assert( + cmd_display_default_preset( + substrate_test_runtime::WASM_BINARY_PATH.expect("to be a valid path. qed."), + ), + "tests/expected/doc/display_preset.json", + false, + ); +} + +#[docify::export] +fn cmd_display_preset(runtime_path: &str) -> String { + bash!( + chain-spec-builder display-preset -r $runtime_path -p "staging" + ) +} + +#[test] +fn display_preset() { + doc_assert( + cmd_display_preset( + substrate_test_runtime::WASM_BINARY_PATH.expect("to be a valid path. qed"), + ), + "tests/expected/doc/display_preset_staging.json", + false, + ); +} + +#[docify::export_content] +fn cmd_list_presets(runtime_path: &str) -> String { + bash!( + chain-spec-builder list-presets -r $runtime_path + ) +} + +#[test] +fn list_presets() { + doc_assert( + cmd_list_presets( + substrate_test_runtime::WASM_BINARY_PATH.expect("to be a valid path. qed"), + ), + "tests/expected/doc/list_presets.json", + false, + ); +} + +#[docify::export_content] +fn cmd_create_with_named_preset(runtime_path: &str) -> String { + bash!( + chain-spec-builder -c "/dev/stdout" create --relay-chain "dev" --para-id 1000 -r $runtime_path named-preset "staging" + ) +} + +#[test] +fn create_with_named_preset() { + doc_assert( + cmd_create_with_named_preset( + substrate_test_runtime::WASM_BINARY_PATH.expect("to be a valid path. qed"), + ), + "tests/expected/doc/create_with_named_preset_staging.json", + true, + ) +} + +#[docify::export_content] +fn cmd_create_with_patch_raw(runtime_path: &str) -> String { + bash!( + chain-spec-builder -c "/dev/stdout" create -s -r $runtime_path patch "tests/input/patch.json" + ) +} + +#[test] +fn create_with_patch_raw() { + doc_assert( + cmd_create_with_patch_raw( + substrate_test_runtime::WASM_BINARY_PATH.expect("to be a valid path. qed"), + ), + "tests/expected/doc/create_with_patch_raw.json", + true, + ); +} + +#[docify::export_content] +fn cmd_create_with_patch_plain(runtime_path: &str) -> String { + bash!( + chain-spec-builder -c "/dev/stdout" create -r $runtime_path patch "tests/input/patch.json" + ) +} + +#[test] +fn create_with_patch_plain() { + doc_assert( + cmd_create_with_patch_plain( + substrate_test_runtime::WASM_BINARY_PATH.expect("to be a valid path. qed"), + ), + "tests/expected/doc/create_with_patch_plain.json", + true, + ); +} + +#[docify::export_content] +fn cmd_create_full_plain(runtime_path: &str) -> String { + bash!( + chain-spec-builder -c "/dev/stdout" create -r $runtime_path full "tests/input/full.json" + ) +} + +#[test] +fn create_full_plain() { + doc_assert( + cmd_create_full_plain( + substrate_test_runtime::WASM_BINARY_PATH.expect("to be a valid path. qed"), + ), + "tests/expected/doc/create_full_plain.json", + true, + ); +} + +#[docify::export_content] +fn cmd_create_full_raw(runtime_path: &str) -> String { + bash!( + chain-spec-builder -c "/dev/stdout" create -s -r $runtime_path full "tests/input/full.json" + ) +} + +#[test] +fn create_full_raw() { + doc_assert( + cmd_create_full_raw( + substrate_test_runtime::WASM_BINARY_PATH.expect("to be a valid path. qed"), + ), + "tests/expected/doc/create_full_raw.json", + true, + ); +} diff --git a/substrate/client/authority-discovery/src/worker.rs b/substrate/client/authority-discovery/src/worker.rs index 6f4fbac77e05982042ad7bad7ed337f8f1170cac..9319fbe6321e7d5049795a86b72618e508bc7d9a 100644 --- a/substrate/client/authority-discovery/src/worker.rs +++ b/substrate/client/authority-discovery/src/worker.rs @@ -71,7 +71,13 @@ pub mod tests; const LOG_TARGET: &str = "sub-authority-discovery"; /// Maximum number of addresses cached per authority. Additional addresses are discarded. -const MAX_ADDRESSES_PER_AUTHORITY: usize = 10; +const MAX_ADDRESSES_PER_AUTHORITY: usize = 16; + +/// Maximum number of global listen addresses published by the node. +const MAX_GLOBAL_LISTEN_ADDRESSES: usize = 4; + +/// Maximum number of addresses to publish in a single record. +const MAX_ADDRESSES_TO_PUBLISH: usize = 32; /// Maximum number of in-flight DHT lookups at any given point in time. const MAX_IN_FLIGHT_LOOKUPS: usize = 8; @@ -174,6 +180,9 @@ pub struct Worker { metrics: Option, + /// Flag to ensure the warning about missing public addresses is only printed once. + warn_public_addresses: bool, + role: Role, phantom: PhantomData, @@ -271,20 +280,7 @@ where config .public_addresses .into_iter() - .map(|mut address| { - if let Some(multiaddr::Protocol::P2p(peer_id)) = address.iter().last() { - if peer_id != *local_peer_id.as_ref() { - error!( - target: LOG_TARGET, - "Discarding invalid local peer ID in public address {address}.", - ); - } - // Always discard `/p2p/...` protocol for proper address comparison (local - // peer id will be added before publishing). - address.pop(); - } - address - }) + .map(|address| AddressType::PublicAddress(address).without_p2p(local_peer_id)) .collect() }; @@ -309,6 +305,7 @@ where addr_cache, role, metrics, + warn_public_addresses: false, phantom: PhantomData, last_known_records: HashMap::new(), } @@ -373,47 +370,70 @@ where } } - fn addresses_to_publish(&self) -> impl Iterator { + fn addresses_to_publish(&mut self) -> impl Iterator { let local_peer_id = self.network.local_peer_id(); let publish_non_global_ips = self.publish_non_global_ips; + + // Checks that the address is global. + let address_is_global = |address: &Multiaddr| { + address.iter().all(|protocol| match protocol { + // The `ip_network` library is used because its `is_global()` method is stable, + // while `is_global()` in the standard library currently isn't. + multiaddr::Protocol::Ip4(ip) => IpNetwork::from(ip).is_global(), + multiaddr::Protocol::Ip6(ip) => IpNetwork::from(ip).is_global(), + _ => true, + }) + }; + + // These are the addresses the node is listening for incoming connections, + // as reported by installed protocols (tcp / websocket etc). + // + // We double check the address is global. In other words, we double check the node + // is not running behind a NAT. + // Note: we do this regardless of the `publish_non_global_ips` setting, since the + // node discovers many external addresses via the identify protocol. + let mut global_listen_addresses = self + .network + .listen_addresses() + .into_iter() + .filter_map(|address| { + address_is_global(&address) + .then(|| AddressType::GlobalListenAddress(address).without_p2p(local_peer_id)) + }) + .take(MAX_GLOBAL_LISTEN_ADDRESSES) + .peekable(); + + // Similar to listen addresses that takes into consideration `publish_non_global_ips`. + let mut external_addresses = self + .network + .external_addresses() + .into_iter() + .filter_map(|address| { + (publish_non_global_ips || address_is_global(&address)) + .then(|| AddressType::ExternalAddress(address).without_p2p(local_peer_id)) + }) + .peekable(); + + let has_global_listen_addresses = global_listen_addresses.peek().is_some(); + trace!( + target: LOG_TARGET, + "Node has public addresses: {}, global listen addresses: {}, external addresses: {}", + !self.public_addresses.is_empty(), + has_global_listen_addresses, + external_addresses.peek().is_some(), + ); + + let mut seen_addresses = HashSet::new(); + let addresses = self .public_addresses .clone() .into_iter() - .chain(self.network.external_addresses().into_iter().filter_map(|mut address| { - // Make sure the reported external address does not contain `/p2p/...` protocol. - if let Some(multiaddr::Protocol::P2p(peer_id)) = address.iter().last() { - if peer_id != *local_peer_id.as_ref() { - error!( - target: LOG_TARGET, - "Network returned external address '{address}' with peer id \ - not matching the local peer id '{local_peer_id}'.", - ); - debug_assert!(false); - } - address.pop(); - } - - if self.public_addresses.contains(&address) { - // Already added above. - None - } else { - Some(address) - } - })) - .filter(move |address| { - if publish_non_global_ips { - return true - } - - address.iter().all(|protocol| match protocol { - // The `ip_network` library is used because its `is_global()` method is stable, - // while `is_global()` in the standard library currently isn't. - multiaddr::Protocol::Ip4(ip) if !IpNetwork::from(ip).is_global() => false, - multiaddr::Protocol::Ip6(ip) if !IpNetwork::from(ip).is_global() => false, - _ => true, - }) - }) + .chain(global_listen_addresses) + .chain(external_addresses) + // Deduplicate addresses. + .filter(|address| seen_addresses.insert(address.clone())) + .take(MAX_ADDRESSES_TO_PUBLISH) .collect::>(); if !addresses.is_empty() { @@ -421,6 +441,21 @@ where target: LOG_TARGET, "Publishing authority DHT record peer_id='{local_peer_id}' with addresses='{addresses:?}'", ); + + if !self.warn_public_addresses && + self.public_addresses.is_empty() && + !has_global_listen_addresses + { + self.warn_public_addresses = true; + + error!( + target: LOG_TARGET, + "No public addresses configured and no global listen addresses found. \ + Authority DHT record may contain unreachable addresses. \ + Consider setting `--public-addr` to the public IP address of this node. \ + This will become a hard requirement in future versions for authorities." + ); + } } // The address must include the local peer id. @@ -437,7 +472,8 @@ where let key_store = match &self.role { Role::PublishAndDiscover(key_store) => key_store, Role::Discover => return Ok(()), - }; + } + .clone(); let addresses = serialize_addresses(self.addresses_to_publish()); if addresses.is_empty() { @@ -946,6 +982,44 @@ where } } +/// Removes the `/p2p/..` from the address if it is present. +#[derive(Debug, Clone, PartialEq, Eq)] +enum AddressType { + /// The address is specified as a public address via the CLI. + PublicAddress(Multiaddr), + /// The address is a global listen address. + GlobalListenAddress(Multiaddr), + /// The address is discovered via the network (ie /identify protocol). + ExternalAddress(Multiaddr), +} + +impl AddressType { + /// Removes the `/p2p/..` from the address if it is present. + /// + /// In case the peer id in the address does not match the local peer id, an error is logged for + /// `ExternalAddress` and `GlobalListenAddress`. + fn without_p2p(self, local_peer_id: PeerId) -> Multiaddr { + // Get the address and the source str for logging. + let (mut address, source) = match self { + AddressType::PublicAddress(address) => (address, "public address"), + AddressType::GlobalListenAddress(address) => (address, "global listen address"), + AddressType::ExternalAddress(address) => (address, "external address"), + }; + + if let Some(multiaddr::Protocol::P2p(peer_id)) = address.iter().last() { + if peer_id != *local_peer_id.as_ref() { + error!( + target: LOG_TARGET, + "Network returned '{source}' '{address}' with peer id \ + not matching the local peer id '{local_peer_id}'.", + ); + } + address.pop(); + } + address + } +} + /// NetworkProvider provides [`Worker`] with all necessary hooks into the /// underlying Substrate networking. Using this trait abstraction instead of /// `sc_network::NetworkService` directly is necessary to unit test [`Worker`]. diff --git a/substrate/client/authority-discovery/src/worker/tests.rs b/substrate/client/authority-discovery/src/worker/tests.rs index d71a85db8b813004ba74773a1d818c8b70dd1cff..8018b5ea492dc31950f81dae813bfb3bfc9fd58d 100644 --- a/substrate/client/authority-discovery/src/worker/tests.rs +++ b/substrate/client/authority-discovery/src/worker/tests.rs @@ -1018,7 +1018,7 @@ fn addresses_to_publish_adds_p2p() { )); let (_to_worker, from_service) = mpsc::channel(0); - let worker = Worker::new( + let mut worker = Worker::new( from_service, Arc::new(TestApi { authorities: vec![] }), network.clone(), @@ -1056,7 +1056,7 @@ fn addresses_to_publish_respects_existing_p2p_protocol() { }); let (_to_worker, from_service) = mpsc::channel(0); - let worker = Worker::new( + let mut worker = Worker::new( from_service, Arc::new(TestApi { authorities: vec![] }), network.clone(), diff --git a/substrate/client/chain-spec/src/genesis_config_builder.rs b/substrate/client/chain-spec/src/genesis_config_builder.rs index 66989495d4231bc933efc34bdd2a2d726f9f5df3..5fe8f9dc053c136d038953e77f452c5a46ae671e 100644 --- a/substrate/client/chain-spec/src/genesis_config_builder.rs +++ b/substrate/client/chain-spec/src/genesis_config_builder.rs @@ -142,11 +142,9 @@ where /// The patching process modifies the default `RuntimeGenesisConfig` according to the following /// rules: /// 1. Existing keys in the default configuration will be overridden by the corresponding values - /// in the patch. + /// in the patch (also applies to `null` values). /// 2. If a key exists in the patch but not in the default configuration, it will be added to /// the resulting `RuntimeGenesisConfig`. - /// 3. Keys in the default configuration that have null values in the patch will be removed from - /// the resulting `RuntimeGenesisConfig`. This is helpful for changing enum variant value. /// /// Please note that the patch may contain full `RuntimeGenesisConfig`. pub fn get_storage_for_patch(&self, patch: Value) -> core::result::Result { diff --git a/substrate/client/chain-spec/src/json_patch.rs b/substrate/client/chain-spec/src/json_patch.rs index c3930069a60d029114c42fedaaeb1cffa7aa3319..a223792374e0cf43317deee5ab3f35bc14515f2f 100644 --- a/substrate/client/chain-spec/src/json_patch.rs +++ b/substrate/client/chain-spec/src/json_patch.rs @@ -22,9 +22,10 @@ use serde_json::Value; /// Recursively merges two JSON objects, `a` and `b`, into a single object. /// -/// If a key exists in both objects, the value from `b` will override the value from `a`. -/// If a key exists in `b` with a `null` value, it will be removed from `a`. +/// If a key exists in both objects, the value from `b` will override the value from `a` (also if +/// value in `b` is `null`). /// If a key exists only in `b` and not in `a`, it will be added to `a`. +/// No keys will be removed from `a`. /// /// # Arguments /// @@ -34,11 +35,7 @@ pub fn merge(a: &mut Value, b: Value) { match (a, b) { (Value::Object(a), Value::Object(b)) => for (k, v) in b { - if v.is_null() { - a.remove(&k); - } else { - merge(a.entry(k).or_insert(Value::Null), v); - } + merge(a.entry(k).or_insert(Value::Null), v); }, (a, b) => *a = b, }; @@ -166,7 +163,7 @@ mod tests { } #[test] - fn test6_patch_removes_keys_if_null() { + fn test6_patch_does_not_remove_keys_if_null() { let mut j1 = json!({ "a": { "name": "xxx", @@ -186,6 +183,16 @@ mod tests { }); merge(&mut j1, j2); - assert_eq!(j1, json!({ "a": {"name":"xxx", "value":456, "enum_variant_2": 32 }})); + assert_eq!( + j1, + json!({ + "a": { + "name":"xxx", + "value":456, + "enum_variant_1": null, + "enum_variant_2": 32 + } + }) + ); } } diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs index f91d18aca749730cd110853e2c83ed4cb4a41128..f79e5b558e37ee270fa20518ce98f21afc8b8658 100644 --- a/substrate/client/cli/src/commands/run_cmd.rs +++ b/substrate/client/cli/src/commands/run_cmd.rs @@ -201,17 +201,7 @@ impl CliConfiguration for RunCmd { } fn network_params(&self) -> Option<&NetworkParams> { - let network_params = &self.network_params; - let is_authority = self.role(self.is_dev().ok()?).ok()?.is_authority(); - if is_authority && network_params.public_addr.is_empty() { - eprintln!( - "WARNING: No public address specified, validator node may not be reachable. - Consider setting `--public-addr` to the public IP address of this node. - This will become a hard requirement in future versions." - ); - } - - Some(network_params) + Some(&self.network_params) } fn keystore_params(&self) -> Option<&KeystoreParams> { diff --git a/substrate/client/consensus/common/src/import_queue.rs b/substrate/client/consensus/common/src/import_queue.rs index 1baa67398a49c3461a2d4f85d9c23de3191e9c22..602683907d4824900f5131c99168a43c833aee3a 100644 --- a/substrate/client/consensus/common/src/import_queue.rs +++ b/substrate/client/consensus/common/src/import_queue.rs @@ -107,7 +107,7 @@ pub trait Verifier: Send + Sync { /// /// The `import_*` methods can be called in order to send elements for the import queue to verify. pub trait ImportQueueService: Send { - /// Import bunch of blocks, every next block must be an ancestor of the previous block in the + /// Import a bunch of blocks, every next block must be an ancestor of the previous block in the /// list. fn import_blocks(&mut self, origin: BlockOrigin, blocks: Vec>); @@ -132,21 +132,21 @@ pub trait ImportQueue: Send { /// This method should behave in a way similar to `Future::poll`. It can register the current /// task and notify later when more actions are ready to be polled. To continue the comparison, /// it is as if this method always returned `Poll::Pending`. - fn poll_actions(&mut self, cx: &mut futures::task::Context, link: &mut dyn Link); + fn poll_actions(&mut self, cx: &mut futures::task::Context, link: &dyn Link); /// Start asynchronous runner for import queue. /// /// Takes an object implementing [`Link`] which allows the import queue to /// influence the synchronization process. - async fn run(self, link: Box>); + async fn run(self, link: &dyn Link); } /// Hooks that the verification queue can use to influence the synchronization /// algorithm. -pub trait Link: Send { +pub trait Link: Send + Sync { /// Batch of blocks imported, with or without error. fn blocks_processed( - &mut self, + &self, _imported: usize, _count: usize, _results: Vec<(BlockImportResult, B::Hash)>, @@ -155,7 +155,7 @@ pub trait Link: Send { /// Justification import result. fn justification_imported( - &mut self, + &self, _who: RuntimeOrigin, _hash: &B::Hash, _number: NumberFor, @@ -164,7 +164,7 @@ pub trait Link: Send { } /// Request a justification for the given block. - fn request_justification(&mut self, _hash: &B::Hash, _number: NumberFor) {} + fn request_justification(&self, _hash: &B::Hash, _number: NumberFor) {} } /// Block import successful result. diff --git a/substrate/client/consensus/common/src/import_queue/basic_queue.rs b/substrate/client/consensus/common/src/import_queue/basic_queue.rs index 7b371145e2e7df871d8c959bdc04fd405451ff39..21270859dd75ccaf04abad6a5d2cf7c4ed6fe003 100644 --- a/substrate/client/consensus/common/src/import_queue/basic_queue.rs +++ b/substrate/client/consensus/common/src/import_queue/basic_queue.rs @@ -177,7 +177,7 @@ impl ImportQueue for BasicQueue { } /// Poll actions from network. - fn poll_actions(&mut self, cx: &mut Context, link: &mut dyn Link) { + fn poll_actions(&mut self, cx: &mut Context, link: &dyn Link) { if self.result_port.poll_actions(cx, link).is_err() { log::error!( target: LOG_TARGET, @@ -190,9 +190,9 @@ impl ImportQueue for BasicQueue { /// /// Takes an object implementing [`Link`] which allows the import queue to /// influence the synchronization process. - async fn run(mut self, mut link: Box>) { + async fn run(mut self, link: &dyn Link) { loop { - if let Err(_) = self.result_port.next_action(&mut *link).await { + if let Err(_) = self.result_port.next_action(link).await { log::error!(target: "sync", "poll_actions: Background import task is no longer alive"); return } @@ -223,7 +223,7 @@ mod worker_messages { async fn block_import_process( mut block_import: BoxBlockImport, verifier: impl Verifier, - mut result_sender: BufferedLinkSender, + result_sender: BufferedLinkSender, mut block_import_receiver: TracingUnboundedReceiver>, metrics: Option, ) { @@ -501,6 +501,7 @@ mod tests { import_queue::Verifier, }; use futures::{executor::block_on, Future}; + use parking_lot::Mutex; use sp_test_primitives::{Block, BlockNumber, Hash, Header}; #[async_trait::async_trait] @@ -558,29 +559,29 @@ mod tests { #[derive(Default)] struct TestLink { - events: Vec, + events: Mutex>, } impl Link for TestLink { fn blocks_processed( - &mut self, + &self, _imported: usize, _count: usize, results: Vec<(Result, BlockImportError>, Hash)>, ) { if let Some(hash) = results.into_iter().find_map(|(r, h)| r.ok().map(|_| h)) { - self.events.push(Event::BlockImported(hash)); + self.events.lock().push(Event::BlockImported(hash)); } } fn justification_imported( - &mut self, + &self, _who: RuntimeOrigin, hash: &Hash, _number: BlockNumber, _success: bool, ) { - self.events.push(Event::JustificationImported(*hash)) + self.events.lock().push(Event::JustificationImported(*hash)) } } @@ -638,7 +639,7 @@ mod tests { hash }; - let mut link = TestLink::default(); + let link = TestLink::default(); // we send a bunch of tasks to the worker let block1 = import_block(1); @@ -653,13 +654,13 @@ mod tests { // we poll the worker until we have processed 9 events block_on(futures::future::poll_fn(|cx| { - while link.events.len() < 9 { + while link.events.lock().len() < 9 { match Future::poll(Pin::new(&mut worker), cx) { Poll::Pending => {}, Poll::Ready(()) => panic!("import queue worker should not conclude."), } - result_port.poll_actions(cx, &mut link).unwrap(); + result_port.poll_actions(cx, &link).unwrap(); } Poll::Ready(()) @@ -667,8 +668,8 @@ mod tests { // all justification tasks must be done before any block import work assert_eq!( - link.events, - vec![ + &*link.events.lock(), + &[ Event::JustificationImported(justification1), Event::JustificationImported(justification2), Event::JustificationImported(justification3), diff --git a/substrate/client/consensus/common/src/import_queue/buffered_link.rs b/substrate/client/consensus/common/src/import_queue/buffered_link.rs index c23a4b0d5d0abdcb2ec9291f44a8709c349fc048..67131b06a32e5e674f552c07245211fed53c3154 100644 --- a/substrate/client/consensus/common/src/import_queue/buffered_link.rs +++ b/substrate/client/consensus/common/src/import_queue/buffered_link.rs @@ -27,13 +27,13 @@ //! # use sc_consensus::import_queue::buffered_link::buffered_link; //! # use sp_test_primitives::Block; //! # struct DummyLink; impl Link for DummyLink {} -//! # let mut my_link = DummyLink; +//! # let my_link = DummyLink; //! let (mut tx, mut rx) = buffered_link::(100_000); //! tx.blocks_processed(0, 0, vec![]); //! //! // Calls `my_link.blocks_processed(0, 0, vec![])` when polled. //! let _fut = futures::future::poll_fn(move |cx| { -//! rx.poll_actions(cx, &mut my_link); +//! rx.poll_actions(cx, &my_link).unwrap(); //! std::task::Poll::Pending::<()> //! }); //! ``` @@ -90,7 +90,7 @@ pub enum BlockImportWorkerMsg { impl Link for BufferedLinkSender { fn blocks_processed( - &mut self, + &self, imported: usize, count: usize, results: Vec<(BlockImportResult, B::Hash)>, @@ -101,7 +101,7 @@ impl Link for BufferedLinkSender { } fn justification_imported( - &mut self, + &self, who: RuntimeOrigin, hash: &B::Hash, number: NumberFor, @@ -111,7 +111,7 @@ impl Link for BufferedLinkSender { let _ = self.tx.unbounded_send(msg); } - fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { + fn request_justification(&self, hash: &B::Hash, number: NumberFor) { let _ = self .tx .unbounded_send(BlockImportWorkerMsg::RequestJustification(*hash, number)); @@ -125,7 +125,7 @@ pub struct BufferedLinkReceiver { impl BufferedLinkReceiver { /// Send action for the synchronization to perform. - pub fn send_actions(&mut self, msg: BlockImportWorkerMsg, link: &mut dyn Link) { + pub fn send_actions(&mut self, msg: BlockImportWorkerMsg, link: &dyn Link) { match msg { BlockImportWorkerMsg::BlocksProcessed(imported, count, results) => link.blocks_processed(imported, count, results), @@ -144,7 +144,7 @@ impl BufferedLinkReceiver { /// it is as if this method always returned `Poll::Pending`. /// /// Returns an error if the corresponding [`BufferedLinkSender`] has been closed. - pub fn poll_actions(&mut self, cx: &mut Context, link: &mut dyn Link) -> Result<(), ()> { + pub fn poll_actions(&mut self, cx: &mut Context, link: &dyn Link) -> Result<(), ()> { loop { let msg = match Stream::poll_next(Pin::new(&mut self.rx), cx) { Poll::Ready(Some(msg)) => msg, @@ -152,12 +152,12 @@ impl BufferedLinkReceiver { Poll::Pending => break Ok(()), }; - self.send_actions(msg, &mut *link); + self.send_actions(msg, link); } } /// Poll next element from import queue and send the corresponding action command over the link. - pub async fn next_action(&mut self, link: &mut dyn Link) -> Result<(), ()> { + pub async fn next_action(&mut self, link: &dyn Link) -> Result<(), ()> { if let Some(msg) = self.rx.next().await { self.send_actions(msg, link); return Ok(()) diff --git a/substrate/client/consensus/common/src/import_queue/mock.rs b/substrate/client/consensus/common/src/import_queue/mock.rs index 64ac532ded854194121815878b935aff0291e5cd..a238f72568ca655cf51b9c01e2f2a0d34ba1125c 100644 --- a/substrate/client/consensus/common/src/import_queue/mock.rs +++ b/substrate/client/consensus/common/src/import_queue/mock.rs @@ -40,7 +40,7 @@ mockall::mock! { impl ImportQueue for ImportQueue { fn service(&self) -> Box>; fn service_ref(&mut self) -> &mut dyn ImportQueueService; - fn poll_actions<'a>(&mut self, cx: &mut futures::task::Context<'a>, link: &mut dyn Link); - async fn run(self, link: Box>); + fn poll_actions<'a>(&mut self, cx: &mut futures::task::Context<'a>, link: &dyn Link); + async fn run(self, link: &'__mockall_link dyn Link); } } diff --git a/substrate/client/consensus/pow/src/lib.rs b/substrate/client/consensus/pow/src/lib.rs index cd7da128549f0d8ded3b073568e735fae970028f..882f3440e164e1761b0a9bfdc80d30af8b4004dc 100644 --- a/substrate/client/consensus/pow/src/lib.rs +++ b/substrate/client/consensus/pow/src/lib.rs @@ -62,7 +62,6 @@ use sp_inherents::{CreateInherentDataProviders, InherentDataProvider}; use sp_runtime::{ generic::{BlockId, Digest, DigestItem}, traits::{Block as BlockT, Header as HeaderT}, - RuntimeString, }; use std::{cmp::Ordering, marker::PhantomData, sync::Arc, time::Duration}; @@ -110,7 +109,7 @@ pub enum Error { #[error("{0}")] Environment(String), #[error("{0}")] - Runtime(RuntimeString), + Runtime(String), #[error("{0}")] Other(String), } diff --git a/substrate/client/executor/src/wasm_runtime.rs b/substrate/client/executor/src/wasm_runtime.rs index 77dfc09c8807e7bb6fa9289bf3a58cc909b9f4f8..8f189ca92388aeae8663f2165f68caad48d1b979 100644 --- a/substrate/client/executor/src/wasm_runtime.rs +++ b/substrate/client/executor/src/wasm_runtime.rs @@ -441,18 +441,20 @@ where #[cfg(test)] mod tests { + extern crate alloc; + use super::*; + use alloc::borrow::Cow; use codec::Encode; use sp_api::{Core, RuntimeApiInfo}; - use sp_runtime::RuntimeString; use sp_version::{create_apis_vec, RuntimeVersion}; use sp_wasm_interface::HostFunctions; use substrate_test_runtime::Block; #[derive(Encode)] pub struct OldRuntimeVersion { - pub spec_name: RuntimeString, - pub impl_name: RuntimeString, + pub spec_name: Cow<'static, str>, + pub impl_name: Cow<'static, str>, pub authoring_version: u32, pub spec_version: u32, pub impl_version: u32, diff --git a/substrate/client/network/Cargo.toml b/substrate/client/network/Cargo.toml index 8ae3de72f79646b9d65beb975f7e5e602dc95679..c8fd28e0810943a898c14ba25cad618f372769c2 100644 --- a/substrate/client/network/Cargo.toml +++ b/substrate/client/network/Cargo.toml @@ -70,7 +70,7 @@ mockall = { workspace = true } multistream-select = { workspace = true } rand = { workspace = true, default-features = true } tempfile = { workspace = true } -tokio = { features = ["macros"], workspace = true, default-features = true } +tokio = { features = ["macros", "rt-multi-thread"], workspace = true, default-features = true } tokio-util = { features = ["compat"], workspace = true } tokio-test = { workspace = true } sc-block-builder = { workspace = true, default-features = true } @@ -83,5 +83,17 @@ sp-tracing = { workspace = true, default-features = true } substrate-test-runtime = { workspace = true } substrate-test-runtime-client = { workspace = true } +criterion = { workspace = true, default-features = true, features = ["async_tokio"] } +sc-consensus = { workspace = true, default-features = true } + [features] default = [] + + +[[bench]] +name = "notifications_protocol" +harness = false + +[[bench]] +name = "request_response_protocol" +harness = false diff --git a/substrate/client/network/benches/notifications_protocol.rs b/substrate/client/network/benches/notifications_protocol.rs new file mode 100644 index 0000000000000000000000000000000000000000..7d32c9faeba108ede0af5632e6de3c015bf0d277 --- /dev/null +++ b/substrate/client/network/benches/notifications_protocol.rs @@ -0,0 +1,290 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use criterion::{ + criterion_group, criterion_main, AxisScale, BenchmarkId, Criterion, PlotConfiguration, + Throughput, +}; +use sc_network::{ + config::{ + FullNetworkConfiguration, MultiaddrWithPeerId, NetworkConfiguration, NonDefaultSetConfig, + NonReservedPeerMode, NotificationHandshake, Params, ProtocolId, Role, SetConfig, + }, + service::traits::NotificationEvent, + NetworkWorker, NotificationMetrics, NotificationService, Roles, +}; +use sc_network_common::sync::message::BlockAnnouncesHandshake; +use sc_network_types::build_multiaddr; +use sp_runtime::traits::Zero; +use std::{ + net::{IpAddr, Ipv4Addr, TcpListener}, + str::FromStr, +}; +use substrate_test_runtime_client::runtime; + +const MAX_SIZE: u64 = 2u64.pow(30); +const SAMPLE_SIZE: usize = 50; +const NOTIFICATIONS: usize = 50; +const EXPONENTS: &[(u32, &'static str)] = &[ + (6, "64B"), + (9, "512B"), + (12, "4KB"), + (15, "64KB"), + (18, "256KB"), + (21, "2MB"), + (24, "16MB"), + (27, "128MB"), +]; + +// TODO: It's be better to bind system-provided port when initializing the worker +fn get_listen_address() -> sc_network::Multiaddr { + let ip = Ipv4Addr::from_str("127.0.0.1").unwrap(); + let listener = TcpListener::bind((IpAddr::V4(ip), 0)).unwrap(); // Bind to a random port + let local_addr = listener.local_addr().unwrap(); + let port = local_addr.port(); + + build_multiaddr!(Ip4(ip), Tcp(port)) +} + +pub fn create_network_worker( + listen_addr: sc_network::Multiaddr, +) -> (NetworkWorker, Box) { + let role = Role::Full; + let genesis_hash = runtime::Hash::zero(); + let (block_announce_config, notification_service) = NonDefaultSetConfig::new( + "/block-announces/1".into(), + vec!["/bench-notifications-protocol/block-announces/1".into()], + MAX_SIZE, + Some(NotificationHandshake::new(BlockAnnouncesHandshake::::build( + Roles::from(&role), + Zero::zero(), + genesis_hash, + genesis_hash, + ))), + SetConfig { + in_peers: 1, + out_peers: 1, + reserved_nodes: vec![], + non_reserved_mode: NonReservedPeerMode::Accept, + }, + ); + let mut net_conf = NetworkConfiguration::new_local(); + net_conf.listen_addresses = vec![listen_addr]; + let worker = NetworkWorker::::new(Params::< + runtime::Block, + runtime::Hash, + NetworkWorker<_, _>, + > { + block_announce_config, + role, + executor: Box::new(|f| { + tokio::spawn(f); + }), + genesis_hash, + network_config: FullNetworkConfiguration::new(&net_conf, None), + protocol_id: ProtocolId::from("bench-protocol-name"), + fork_id: None, + metrics_registry: None, + bitswap_config: None, + notification_metrics: NotificationMetrics::new(None), + }) + .unwrap(); + + (worker, notification_service) +} + +async fn run_serially(size: usize, limit: usize) { + let listen_address1 = get_listen_address(); + let listen_address2 = get_listen_address(); + let (worker1, mut notification_service1) = create_network_worker(listen_address1); + let (worker2, mut notification_service2) = create_network_worker(listen_address2.clone()); + let peer_id2: sc_network::PeerId = (*worker2.local_peer_id()).into(); + + worker1 + .add_reserved_peer(MultiaddrWithPeerId { multiaddr: listen_address2, peer_id: peer_id2 }) + .unwrap(); + + let network1_run = worker1.run(); + let network2_run = worker2.run(); + let (tx, rx) = async_channel::bounded(10); + + let network1 = tokio::spawn(async move { + tokio::pin!(network1_run); + loop { + tokio::select! { + _ = &mut network1_run => {}, + event = notification_service1.next_event() => { + match event { + Some(NotificationEvent::NotificationStreamOpened { .. }) => { + notification_service1 + .send_async_notification(&peer_id2, vec![0; size]) + .await + .unwrap(); + }, + event => panic!("Unexpected event {:?}", event), + }; + }, + message = rx.recv() => { + match message { + Ok(Some(_)) => { + notification_service1 + .send_async_notification(&peer_id2, vec![0; size]) + .await + .unwrap(); + }, + Ok(None) => break, + Err(err) => panic!("Unexpected error {:?}", err), + + } + } + } + } + }); + let network2 = tokio::spawn(async move { + let mut received_counter = 0; + tokio::pin!(network2_run); + loop { + tokio::select! { + _ = &mut network2_run => {}, + event = notification_service2.next_event() => { + match event { + Some(NotificationEvent::ValidateInboundSubstream { result_tx, .. }) => { + result_tx.send(sc_network::service::traits::ValidationResult::Accept).unwrap(); + }, + Some(NotificationEvent::NotificationStreamOpened { .. }) => {}, + Some(NotificationEvent::NotificationReceived { .. }) => { + received_counter += 1; + if received_counter >= limit { + let _ = tx.send(None).await; + break + } + let _ = tx.send(Some(())).await; + }, + event => panic!("Unexpected event {:?}", event), + }; + }, + } + } + }); + + let _ = tokio::join!(network1, network2); +} + +async fn run_with_backpressure(size: usize, limit: usize) { + let listen_address1 = get_listen_address(); + let listen_address2 = get_listen_address(); + let (worker1, mut notification_service1) = create_network_worker(listen_address1); + let (worker2, mut notification_service2) = create_network_worker(listen_address2.clone()); + let peer_id2: sc_network::PeerId = (*worker2.local_peer_id()).into(); + + worker1 + .add_reserved_peer(MultiaddrWithPeerId { multiaddr: listen_address2, peer_id: peer_id2 }) + .unwrap(); + + let network1_run = worker1.run(); + let network2_run = worker2.run(); + + let network1 = tokio::spawn(async move { + let mut sent_counter = 0; + tokio::pin!(network1_run); + loop { + tokio::select! { + _ = &mut network1_run => {}, + event = notification_service1.next_event() => { + match event { + Some(NotificationEvent::NotificationStreamOpened { .. }) => { + while sent_counter < limit { + sent_counter += 1; + notification_service1 + .send_async_notification(&peer_id2, vec![0; size]) + .await + .unwrap(); + } + }, + Some(NotificationEvent::NotificationStreamClosed { .. }) => { + if sent_counter != limit { panic!("Stream closed unexpectedly") } + break + }, + event => panic!("Unexpected event {:?}", event), + }; + }, + } + } + }); + let network2 = tokio::spawn(async move { + let mut received_counter = 0; + tokio::pin!(network2_run); + loop { + tokio::select! { + _ = &mut network2_run => {}, + event = notification_service2.next_event() => { + match event { + Some(NotificationEvent::ValidateInboundSubstream { result_tx, .. }) => { + result_tx.send(sc_network::service::traits::ValidationResult::Accept).unwrap(); + }, + Some(NotificationEvent::NotificationStreamOpened { .. }) => {}, + Some(NotificationEvent::NotificationStreamClosed { .. }) => { + if received_counter != limit { panic!("Stream closed unexpectedly") } + break + }, + Some(NotificationEvent::NotificationReceived { .. }) => { + received_counter += 1; + if received_counter >= limit { break } + }, + event => panic!("Unexpected event {:?}", event), + }; + }, + } + } + }); + + let _ = tokio::join!(network1, network2); +} + +fn run_benchmark(c: &mut Criterion) { + let rt = tokio::runtime::Runtime::new().unwrap(); + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + let mut group = c.benchmark_group("notifications_benchmark"); + group.plot_config(plot_config); + + for &(exponent, label) in EXPONENTS.iter() { + let size = 2usize.pow(exponent); + group.throughput(Throughput::Bytes(NOTIFICATIONS as u64 * size as u64)); + group.bench_with_input( + BenchmarkId::new("consistently", label), + &(size, NOTIFICATIONS), + |b, &(size, limit)| { + b.to_async(&rt).iter(|| run_serially(size, limit)); + }, + ); + group.bench_with_input( + BenchmarkId::new("with_backpressure", label), + &(size, NOTIFICATIONS), + |b, &(size, limit)| { + b.to_async(&rt).iter(|| run_with_backpressure(size, limit)); + }, + ); + } +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(SAMPLE_SIZE); + targets = run_benchmark +} +criterion_main!(benches); diff --git a/substrate/client/network/benches/request_response_protocol.rs b/substrate/client/network/benches/request_response_protocol.rs new file mode 100644 index 0000000000000000000000000000000000000000..09bf829f5a7e9cf93b389ed587ecea853ddf5642 --- /dev/null +++ b/substrate/client/network/benches/request_response_protocol.rs @@ -0,0 +1,278 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use criterion::{ + criterion_group, criterion_main, AxisScale, BenchmarkId, Criterion, PlotConfiguration, + Throughput, +}; +use sc_network::{ + config::{ + FullNetworkConfiguration, IncomingRequest, NetworkConfiguration, NonDefaultSetConfig, + NonReservedPeerMode, NotificationHandshake, OutgoingResponse, Params, ProtocolId, Role, + SetConfig, + }, + IfDisconnected, NetworkBackend, NetworkRequest, NetworkWorker, NotificationMetrics, + NotificationService, Roles, +}; +use sc_network_common::sync::message::BlockAnnouncesHandshake; +use sc_network_types::build_multiaddr; +use sp_runtime::traits::Zero; +use std::{ + net::{IpAddr, Ipv4Addr, TcpListener}, + str::FromStr, + time::Duration, +}; +use substrate_test_runtime_client::runtime; + +const MAX_SIZE: u64 = 2u64.pow(30); +const SAMPLE_SIZE: usize = 50; +const REQUESTS: usize = 50; +const EXPONENTS: &[(u32, &'static str)] = &[ + (6, "64B"), + (9, "512B"), + (12, "4KB"), + (15, "64KB"), + (18, "256KB"), + (21, "2MB"), + (24, "16MB"), + (27, "128MB"), +]; + +fn get_listen_address() -> sc_network::Multiaddr { + let ip = Ipv4Addr::from_str("127.0.0.1").unwrap(); + let listener = TcpListener::bind((IpAddr::V4(ip), 0)).unwrap(); // Bind to a random port + let local_addr = listener.local_addr().unwrap(); + let port = local_addr.port(); + + build_multiaddr!(Ip4(ip), Tcp(port)) +} + +pub fn create_network_worker( + listen_addr: sc_network::Multiaddr, +) -> ( + NetworkWorker, + async_channel::Receiver, + Box, +) { + let (tx, rx) = async_channel::bounded(10); + let request_response_config = + NetworkWorker::::request_response_config( + "/request-response/1".into(), + vec![], + MAX_SIZE, + MAX_SIZE, + Duration::from_secs(2), + Some(tx), + ); + let mut net_conf = NetworkConfiguration::new_local(); + net_conf.listen_addresses = vec![listen_addr]; + let mut network_config = FullNetworkConfiguration::new(&net_conf, None); + network_config.add_request_response_protocol(request_response_config); + let (block_announce_config, notification_service) = NonDefaultSetConfig::new( + "/block-announces/1".into(), + vec![], + 1024, + Some(NotificationHandshake::new(BlockAnnouncesHandshake::::build( + Roles::from(&Role::Full), + Zero::zero(), + runtime::Hash::zero(), + runtime::Hash::zero(), + ))), + SetConfig { + in_peers: 1, + out_peers: 1, + reserved_nodes: vec![], + non_reserved_mode: NonReservedPeerMode::Accept, + }, + ); + let worker = NetworkWorker::::new(Params::< + runtime::Block, + runtime::Hash, + NetworkWorker<_, _>, + > { + block_announce_config, + role: Role::Full, + executor: Box::new(|f| { + tokio::spawn(f); + }), + genesis_hash: runtime::Hash::zero(), + network_config, + protocol_id: ProtocolId::from("bench-request-response-protocol"), + fork_id: None, + metrics_registry: None, + bitswap_config: None, + notification_metrics: NotificationMetrics::new(None), + }) + .unwrap(); + + (worker, rx, notification_service) +} + +async fn run_serially(size: usize, limit: usize) { + let listen_address1 = get_listen_address(); + let listen_address2 = get_listen_address(); + let (mut worker1, _rx1, _notification_service1) = create_network_worker(listen_address1); + let service1 = worker1.service().clone(); + let (worker2, rx2, _notification_service2) = create_network_worker(listen_address2.clone()); + let peer_id2 = *worker2.local_peer_id(); + + worker1.add_known_address(peer_id2, listen_address2.into()); + + let network1_run = worker1.run(); + let network2_run = worker2.run(); + let (break_tx, break_rx) = async_channel::bounded(10); + let requests = async move { + let mut sent_counter = 0; + while sent_counter < limit { + let _ = service1 + .request( + peer_id2.into(), + "/request-response/1".into(), + vec![0; 2], + None, + IfDisconnected::TryConnect, + ) + .await + .unwrap(); + sent_counter += 1; + } + let _ = break_tx.send(()).await; + }; + + let network1 = tokio::spawn(async move { + tokio::pin!(requests); + tokio::pin!(network1_run); + loop { + tokio::select! { + _ = &mut network1_run => {}, + _ = &mut requests => break, + } + } + }); + let network2 = tokio::spawn(async move { + tokio::pin!(network2_run); + loop { + tokio::select! { + _ = &mut network2_run => {}, + res = rx2.recv() => { + let IncomingRequest { pending_response, .. } = res.unwrap(); + pending_response.send(OutgoingResponse { + result: Ok(vec![0; size]), + reputation_changes: vec![], + sent_feedback: None, + }).unwrap(); + }, + _ = break_rx.recv() => break, + } + } + }); + + let _ = tokio::join!(network1, network2); +} + +// The libp2p request-response implementation does not provide any backpressure feedback. +// So this benchmark is useless until we implement it for litep2p. +#[allow(dead_code)] +async fn run_with_backpressure(size: usize, limit: usize) { + let listen_address1 = get_listen_address(); + let listen_address2 = get_listen_address(); + let (mut worker1, _rx1, _notification_service1) = create_network_worker(listen_address1); + let service1 = worker1.service().clone(); + let (worker2, rx2, _notification_service2) = create_network_worker(listen_address2.clone()); + let peer_id2 = *worker2.local_peer_id(); + + worker1.add_known_address(peer_id2, listen_address2.into()); + + let network1_run = worker1.run(); + let network2_run = worker2.run(); + let (break_tx, break_rx) = async_channel::bounded(10); + let requests = futures::future::join_all((0..limit).into_iter().map(|_| { + let (tx, rx) = futures::channel::oneshot::channel(); + service1.start_request( + peer_id2.into(), + "/request-response/1".into(), + vec![0; 8], + None, + tx, + IfDisconnected::TryConnect, + ); + rx + })); + + let network1 = tokio::spawn(async move { + tokio::pin!(requests); + tokio::pin!(network1_run); + loop { + tokio::select! { + _ = &mut network1_run => {}, + responses = &mut requests => { + for res in responses { + res.unwrap().unwrap(); + } + let _ = break_tx.send(()).await; + break; + }, + } + } + }); + let network2 = tokio::spawn(async move { + tokio::pin!(network2_run); + loop { + tokio::select! { + _ = &mut network2_run => {}, + res = rx2.recv() => { + let IncomingRequest { pending_response, .. } = res.unwrap(); + pending_response.send(OutgoingResponse { + result: Ok(vec![0; size]), + reputation_changes: vec![], + sent_feedback: None, + }).unwrap(); + }, + _ = break_rx.recv() => break, + } + } + }); + + let _ = tokio::join!(network1, network2); +} + +fn run_benchmark(c: &mut Criterion) { + let rt = tokio::runtime::Runtime::new().unwrap(); + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + let mut group = c.benchmark_group("request_response_benchmark"); + group.plot_config(plot_config); + + for &(exponent, label) in EXPONENTS.iter() { + let size = 2usize.pow(exponent); + group.throughput(Throughput::Bytes(REQUESTS as u64 * size as u64)); + group.bench_with_input( + BenchmarkId::new("consistently", label), + &(size, REQUESTS), + |b, &(size, limit)| { + b.to_async(&rt).iter(|| run_serially(size, limit)); + }, + ); + } +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(SAMPLE_SIZE); + targets = run_benchmark +} +criterion_main!(benches); diff --git a/substrate/client/network/src/discovery.rs b/substrate/client/network/src/discovery.rs index 49e0797c126c5a2adfce1e5400105a806f9cd063..8080bda9a5749ec5f644137eee5378273951d98e 100644 --- a/substrate/client/network/src/discovery.rs +++ b/substrate/client/network/src/discovery.rs @@ -749,16 +749,28 @@ impl NetworkBehaviour for DiscoveryBehaviour { self.mdns.on_swarm_event(FromSwarm::NewListenAddr(e)); }, FromSwarm::ExternalAddrConfirmed(e @ ExternalAddrConfirmed { addr }) => { - let new_addr = addr.clone().with(Protocol::P2p(self.local_peer_id)); + let mut address = addr.clone(); - if Self::can_add_to_dht(addr) { + if let Some(Protocol::P2p(peer_id)) = addr.iter().last() { + if peer_id != self.local_peer_id { + warn!( + target: "sub-libp2p", + "๐Ÿ” Discovered external address for a peer that is not us: {addr}", + ); + // Ensure this address is not propagated to kademlia. + return + } + } else { + address.push(Protocol::P2p(self.local_peer_id)); + } + + if Self::can_add_to_dht(&address) { // NOTE: we might re-discover the same address multiple times // in which case we just want to refrain from logging. - if self.known_external_addresses.insert(new_addr.clone()) { + if self.known_external_addresses.insert(address.clone()) { info!( target: "sub-libp2p", - "๐Ÿ” Discovered new external address for our node: {}", - new_addr, + "๐Ÿ” Discovered new external address for our node: {address}", ); } } diff --git a/substrate/client/network/src/litep2p/discovery.rs b/substrate/client/network/src/litep2p/discovery.rs index 13cf8a4c6ee0c30ba14dbd5165f9d7e73a0dba75..9043f9420e8dc2106ea6f8948f3a1981a270ff70 100644 --- a/substrate/client/network/src/litep2p/discovery.rs +++ b/substrate/client/network/src/litep2p/discovery.rs @@ -162,6 +162,9 @@ pub enum DiscoveryEvent { /// Discovery. pub struct Discovery { + /// Local peer ID. + local_peer_id: litep2p::PeerId, + /// Ping event stream. ping_event_stream: Box + Send + Unpin>, @@ -233,6 +236,7 @@ impl Discovery { /// Enables `/ipfs/ping/1.0.0` and `/ipfs/identify/1.0.0` by default and starts /// the mDNS peer discovery if it was enabled. pub fn new + Clone>( + local_peer_id: litep2p::PeerId, config: &NetworkConfiguration, genesis_hash: Hash, fork_id: Option<&str>, @@ -273,6 +277,7 @@ impl Discovery { ( Self { + local_peer_id, ping_event_stream, identify_event_stream, mdns_event_stream, @@ -553,7 +558,7 @@ impl Stream for Discovery { return Poll::Ready(Some(DiscoveryEvent::GetRecordSuccess { query_id, records })); }, - Poll::Ready(Some(KademliaEvent::PutRecordSucess { query_id, key: _ })) => + Poll::Ready(Some(KademliaEvent::PutRecordSuccess { query_id, key: _ })) => return Poll::Ready(Some(DiscoveryEvent::PutRecordSuccess { query_id })), Poll::Ready(Some(KademliaEvent::QueryFailed { query_id })) => { match this.find_node_query_id == Some(query_id) { @@ -576,6 +581,9 @@ impl Stream for Discovery { return Poll::Ready(Some(DiscoveryEvent::IncomingRecord { record })) }, + // Content provider events are ignored for now. + Poll::Ready(Some(KademliaEvent::GetProvidersSuccess { .. })) | + Poll::Ready(Some(KademliaEvent::IncomingProvider { .. })) => {}, } match Pin::new(&mut this.identify_event_stream).poll_next(cx) { @@ -588,24 +596,43 @@ impl Stream for Discovery { observed_address, .. })) => { - let (is_new, expired_address) = - this.is_new_external_address(&observed_address, peer); - - if let Some(expired_address) = expired_address { - log::trace!( - target: LOG_TARGET, - "Removing expired external address expired={expired_address} is_new={is_new} observed={observed_address}", - ); - - this.pending_events.push_back(DiscoveryEvent::ExternalAddressExpired { - address: expired_address, - }); - } + let observed_address = + if let Some(Protocol::P2p(peer_id)) = observed_address.iter().last() { + if peer_id != *this.local_peer_id.as_ref() { + log::warn!( + target: LOG_TARGET, + "Discovered external address for a peer that is not us: {observed_address}", + ); + None + } else { + Some(observed_address) + } + } else { + Some(observed_address.with(Protocol::P2p(this.local_peer_id.into()))) + }; + + // Ensure that an external address with a different peer ID does not have + // side effects of evicting other external addresses via `ExternalAddressExpired`. + if let Some(observed_address) = observed_address { + let (is_new, expired_address) = + this.is_new_external_address(&observed_address, peer); + + if let Some(expired_address) = expired_address { + log::trace!( + target: LOG_TARGET, + "Removing expired external address expired={expired_address} is_new={is_new} observed={observed_address}", + ); + + this.pending_events.push_back(DiscoveryEvent::ExternalAddressExpired { + address: expired_address, + }); + } - if is_new { - this.pending_events.push_back(DiscoveryEvent::ExternalAddressDiscovered { - address: observed_address.clone(), - }); + if is_new { + this.pending_events.push_back(DiscoveryEvent::ExternalAddressDiscovered { + address: observed_address.clone(), + }); + } } return Poll::Ready(Some(DiscoveryEvent::Identified { diff --git a/substrate/client/network/src/litep2p/mod.rs b/substrate/client/network/src/litep2p/mod.rs index df4244890f96714a86c5d9235ffb7ae87a8192f6..87b992423674412c5283772ace367f46af8db5c4 100644 --- a/substrate/client/network/src/litep2p/mod.rs +++ b/substrate/client/network/src/litep2p/mod.rs @@ -540,6 +540,7 @@ impl NetworkBackend for Litep2pNetworkBac let listen_addresses = Arc::new(Default::default()); let (discovery, ping_config, identify_config, kademlia_config, maybe_mdns_config) = Discovery::new( + local_peer_id, &network_config, params.genesis_hash, params.fork_id.as_deref(), diff --git a/substrate/client/network/src/litep2p/shim/notification/peerset.rs b/substrate/client/network/src/litep2p/shim/notification/peerset.rs index 2fd7920909e334c2f4756982bbd6937781dea4a7..fb822794ccf0a08bb89addb5bc6dce01600e6fcb 100644 --- a/substrate/client/network/src/litep2p/shim/notification/peerset.rs +++ b/substrate/client/network/src/litep2p/shim/notification/peerset.rs @@ -88,6 +88,8 @@ const DISCONNECT_ADJUSTMENT: Reputation = Reputation::new(-256, "Peer disconnect const OPEN_FAILURE_ADJUSTMENT: Reputation = Reputation::new(-1024, "Open failure"); /// Is the peer reserved? +/// +/// Regular peers count towards slot allocation. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Reserved { Yes, @@ -118,6 +120,15 @@ pub enum Direction { Outbound(Reserved), } +impl Direction { + fn set_reserved(&mut self, new_reserved: Reserved) { + match self { + Direction::Inbound(ref mut reserved) | Direction::Outbound(ref mut reserved) => + *reserved = new_reserved, + } + } +} + impl From for traits::Direction { fn from(direction: Direction) -> traits::Direction { match direction { @@ -784,7 +795,9 @@ impl Peerset { } /// Calculate how many of the connected peers were counted as normal inbound/outbound peers - /// which is needed to adjust slot counts when new reserved peers are added + /// which is needed to adjust slot counts when new reserved peers are added. + /// + /// If the peer is not already in the [`Peerset`], it is added as a disconnected peer. fn calculate_slot_adjustment<'a>( &'a mut self, peers: impl Iterator, @@ -819,6 +832,26 @@ impl Peerset { }) } + /// Checks if the peer should be disconnected based on the current state of the [`Peerset`] + /// and the provided direction. + /// + /// Note: The role of the peer is not checked. + fn should_disconnect(&self, direction: Direction) -> bool { + match direction { + Direction::Inbound(_) => self.num_in >= self.max_in, + Direction::Outbound(_) => self.num_out >= self.max_out, + } + } + + /// Increment the slot count for given peer. + fn increment_slot(&mut self, direction: Direction) { + match direction { + Direction::Inbound(Reserved::No) => self.num_in += 1, + Direction::Outbound(Reserved::No) => self.num_out += 1, + _ => {}, + } + } + /// Get the number of inbound peers. #[cfg(test)] pub fn num_in(&self) -> usize { @@ -949,8 +982,9 @@ impl Stream for Peerset { }, // set new reserved peers for the protocol // - // current reserved peers not in the new set are disconnected and the new reserved - // peers are scheduled for outbound substreams + // Current reserved peers not in the new set are moved to the regular set of peers + // or disconnected (if there are no slots available). The new reserved peers are + // scheduled for outbound substreams PeersetCommand::SetReservedPeers { peers } => { log::debug!(target: LOG_TARGET, "{}: set reserved peers {peers:?}", self.protocol); @@ -960,39 +994,58 @@ impl Stream for Peerset { // // calculate how many of the previously connected peers were counted as regular // peers and substract these counts from `num_out`/`num_in` + // + // If a reserved peer is not already tracked, it is added as disconnected by + // `calculate_slot_adjustment`. This ensures at the next slot allocation (1sec) + // that we'll try to establish a connection with the reserved peer. let (in_peers, out_peers) = self.calculate_slot_adjustment(peers.iter()); self.num_out -= out_peers; self.num_in -= in_peers; - // add all unknown peers to `self.peers` - peers.iter().for_each(|peer| { - if !self.peers.contains_key(peer) { - self.peers.insert(*peer, PeerState::Disconnected); - } - }); - - // collect all peers who are not in the new reserved set - let peers_to_remove = self - .peers - .iter() - .filter_map(|(peer, _)| (!peers.contains(peer)).then_some(*peer)) - .collect::>(); + // collect all *reserved* peers who are not in the new reserved set + let reserved_peers_maybe_remove = + self.reserved_peers.difference(&peers).cloned().collect::>(); self.reserved_peers = peers; - let peers = peers_to_remove + let peers_to_remove = reserved_peers_maybe_remove .into_iter() .filter(|peer| { match self.peers.remove(&peer) { - Some(PeerState::Connected { direction }) => { - log::trace!( - target: LOG_TARGET, - "{}: close connection to {peer:?}, direction {direction:?}", - self.protocol, - ); - - self.peers.insert(*peer, PeerState::Closing { direction }); - true + Some(PeerState::Connected { mut direction }) => { + // The direction contains a `Reserved::Yes` flag, because this + // is a reserve peer that we want to close. + // The `Reserved::Yes` ensures we don't adjust the slot count + // when the substream is closed. + + let disconnect = + self.reserved_only || self.should_disconnect(direction); + + if disconnect { + log::trace!( + target: LOG_TARGET, + "{}: close connection to previously reserved {peer:?}, direction {direction:?}", + self.protocol, + ); + + self.peers.insert(*peer, PeerState::Closing { direction }); + true + } else { + log::trace!( + target: LOG_TARGET, + "{}: {peer:?} is no longer reserved, move to regular peers, direction {direction:?}", + self.protocol, + ); + + // The peer is kept connected as non-reserved. This will + // further count towards the slot count. + direction.set_reserved(Reserved::No); + self.increment_slot(direction); + + self.peers + .insert(*peer, PeerState::Connected { direction }); + false + } }, // substream might have been opening but not yet fully open when // the protocol request the reserved set to be changed @@ -1021,11 +1074,13 @@ impl Stream for Peerset { log::trace!( target: LOG_TARGET, - "{}: close substreams to {peers:?}", + "{}: close substreams to {peers_to_remove:?}", self.protocol, ); - return Poll::Ready(Some(PeersetNotificationCommand::CloseSubstream { peers })) + return Poll::Ready(Some(PeersetNotificationCommand::CloseSubstream { + peers: peers_to_remove, + })) }, PeersetCommand::AddReservedPeers { peers } => { log::debug!(target: LOG_TARGET, "{}: add reserved peers {peers:?}", self.protocol); @@ -1102,6 +1157,7 @@ impl Stream for Peerset { self.peers.insert(*peer, PeerState::Backoff); None }, + // if there is a rapid change in substream state, the peer may // be canceled when the substream is asked to be closed. // @@ -1122,6 +1178,7 @@ impl Stream for Peerset { self.peers.insert(*peer, PeerState::Canceled { direction }); None }, + // substream to the peer might have failed to open which caused // the peer to be backed off // @@ -1138,6 +1195,7 @@ impl Stream for Peerset { self.peers.insert(*peer, PeerState::Disconnected); None }, + // if a node disconnects, it's put into `PeerState::Closing` // which indicates that `Peerset` wants the substream closed and // has asked litep2p to close it but it hasn't yet received a @@ -1167,125 +1225,70 @@ impl Stream for Peerset { // if there are enough slots, the peer is just converted to // a regular peer and the used slot count is increased and if the // peer cannot be accepted, litep2p is asked to close the substream. - PeerState::Connected { direction } => match direction { - Direction::Inbound(_) => match self.num_in < self.max_in { - true => { - log::trace!( - target: LOG_TARGET, - "{}: {peer:?} converted to regular inbound peer (inbound open)", - self.protocol, - ); - - self.num_in += 1; - self.peers.insert( - *peer, - PeerState::Connected { - direction: Direction::Inbound(Reserved::No), - }, - ); - - None - }, - false => { - self.peers.insert( - *peer, - PeerState::Closing { - direction: Direction::Inbound(Reserved::Yes), - }, - ); - - Some(*peer) - }, - }, - Direction::Outbound(_) => match self.num_out < self.max_out { - true => { - log::trace!( - target: LOG_TARGET, - "{}: {peer:?} converted to regular outbound peer (outbound open)", - self.protocol, - ); - - self.num_out += 1; - self.peers.insert( - *peer, - PeerState::Connected { - direction: Direction::Outbound(Reserved::No), - }, - ); - - None - }, - false => { - self.peers.insert( - *peer, - PeerState::Closing { - direction: Direction::Outbound(Reserved::Yes), - }, - ); - - Some(*peer) - }, - }, + PeerState::Connected { mut direction } => { + let disconnect = self.should_disconnect(direction); + + if disconnect { + log::trace!( + target: LOG_TARGET, + "{}: close connection to removed reserved {peer:?}, direction {direction:?}", + self.protocol, + ); + + self.peers.insert(*peer, PeerState::Closing { direction }); + Some(*peer) + } else { + log::trace!( + target: LOG_TARGET, + "{}: {peer:?} converted to regular peer {peer:?} direction {direction:?}", + self.protocol, + ); + + // The peer is kept connected as non-reserved. This will + // further count towards the slot count. + direction.set_reserved(Reserved::No); + self.increment_slot(direction); + + self.peers + .insert(*peer, PeerState::Connected { direction }); + + None + } }, - PeerState::Opening { direction } => match direction { - Direction::Inbound(_) => match self.num_in < self.max_in { - true => { - log::trace!( - target: LOG_TARGET, - "{}: {peer:?} converted to regular inbound peer (inbound opening)", - self.protocol, - ); - - self.num_in += 1; - self.peers.insert( - *peer, - PeerState::Opening { - direction: Direction::Inbound(Reserved::No), - }, - ); - - None - }, - false => { - self.peers.insert( - *peer, - PeerState::Canceled { - direction: Direction::Inbound(Reserved::Yes), - }, - ); - - None - }, - }, - Direction::Outbound(_) => match self.num_out < self.max_out { - true => { - log::trace!( - target: LOG_TARGET, - "{}: {peer:?} converted to regular outbound peer (outbound opening)", - self.protocol, - ); - - self.num_out += 1; - self.peers.insert( - *peer, - PeerState::Opening { - direction: Direction::Outbound(Reserved::No), - }, - ); - - None - }, - false => { - self.peers.insert( - *peer, - PeerState::Canceled { - direction: Direction::Outbound(Reserved::Yes), - }, - ); - - None - }, - }, + + PeerState::Opening { mut direction } => { + let disconnect = self.should_disconnect(direction); + + if disconnect { + log::trace!( + target: LOG_TARGET, + "{}: cancel substream to disconnect removed reserved peer {peer:?}, direction {direction:?}", + self.protocol, + ); + + self.peers.insert( + *peer, + PeerState::Canceled { + direction + }, + ); + } else { + log::trace!( + target: LOG_TARGET, + "{}: {peer:?} converted to regular peer {peer:?} direction {direction:?}", + self.protocol, + ); + + // The peer is kept connected as non-reserved. This will + // further count towards the slot count. + direction.set_reserved(Reserved::No); + self.increment_slot(direction); + + self.peers + .insert(*peer, PeerState::Opening { direction }); + } + + None }, } }) @@ -1373,12 +1376,17 @@ impl Stream for Peerset { // if the number of outbound peers is lower than the desired amount of outbound peers, // query `PeerStore` and try to get a new outbound candidated. if self.num_out < self.max_out && !self.reserved_only { + // From the candidates offered by the peerstore we need to ignore: + // - all peers that are not in the `PeerState::Disconnected` state (ie they are + // connected / closing) + // - reserved peers since we initiated a connection to them in the previous step let ignore: HashSet = self .peers .iter() .filter_map(|(peer, state)| { (!std::matches!(state, PeerState::Disconnected)).then_some(*peer) }) + .chain(self.reserved_peers.iter().cloned()) .collect(); let peers: Vec<_> = diff --git a/substrate/client/network/src/litep2p/shim/notification/tests/peerset.rs b/substrate/client/network/src/litep2p/shim/notification/tests/peerset.rs index 4f7bfffaa1fc6cdf677188449dd67e97695ff614..295a5b441b3ea1cd5ca5327c16fae3c1254193d8 100644 --- a/substrate/client/network/src/litep2p/shim/notification/tests/peerset.rs +++ b/substrate/client/network/src/litep2p/shim/notification/tests/peerset.rs @@ -794,8 +794,6 @@ async fn set_reserved_peers_but_available_slots() { // when `Peerset` is polled (along with two random peers) and later on `SetReservedPeers` // is called with the common peer and with two new random peers let common_peer = *known_peers.iter().next().unwrap(); - let disconnected_peers = known_peers.iter().skip(1).copied().collect::>(); - assert_eq!(disconnected_peers.len(), 2); let (mut peerset, to_peerset) = Peerset::new( ProtocolName::from("/notif/1"), @@ -809,6 +807,8 @@ async fn set_reserved_peers_but_available_slots() { assert_eq!(peerset.num_in(), 0usize); assert_eq!(peerset.num_out(), 0usize); + // We have less than 25 outbound peers connected. At the next slot allocation we + // query the `peerstore_handle` for more peers to connect to. match peerset.next().await { Some(PeersetNotificationCommand::OpenSubstream { peers: out_peers }) => { assert_eq!(out_peers.len(), 3); @@ -845,29 +845,167 @@ async fn set_reserved_peers_but_available_slots() { .unbounded_send(PeersetCommand::SetReservedPeers { peers: reserved_peers.clone() }) .unwrap(); + // The command `SetReservedPeers` might evict currently reserved peers if + // we don't have enough slot capacity to move them to regular nodes. + // In this case, we did not have previously any reserved peers. match peerset.next().await { - Some(PeersetNotificationCommand::CloseSubstream { peers: out_peers }) => { - assert_eq!(out_peers.len(), 2); + Some(PeersetNotificationCommand::CloseSubstream { peers }) => { + // This ensures we don't disconnect peers when receiving `SetReservedPeers`. + assert_eq!(peers.len(), 0); + }, + event => panic!("invalid event: {event:?}"), + } - for peer in &out_peers { - assert!(disconnected_peers.contains(peer)); + // verify that `Peerset` is aware of five peers, with two of them as outbound. + assert_eq!(peerset.peers().len(), 5); + assert_eq!(peerset.num_in(), 0usize); + assert_eq!(peerset.num_out(), 2usize); + assert_eq!(peerset.reserved_peers().len(), 3usize); + + match peerset.next().await { + Some(PeersetNotificationCommand::OpenSubstream { peers }) => { + assert_eq!(peers.len(), 2); + assert!(!peers.contains(&common_peer)); + + for peer in &peers { + assert!(reserved_peers.contains(peer)); + assert!(peerset.reserved_peers().contains(peer)); assert_eq!( peerset.peers().get(peer), - Some(&PeerState::Closing { direction: Direction::Outbound(Reserved::No) }), + Some(&PeerState::Opening { direction: Direction::Outbound(Reserved::Yes) }), + ); + } + }, + event => panic!("invalid event: {event:?}"), + } + + assert_eq!(peerset.peers().len(), 5); + assert_eq!(peerset.num_in(), 0usize); + assert_eq!(peerset.num_out(), 2usize); + assert_eq!(peerset.reserved_peers().len(), 3usize); +} + +#[tokio::test] +async fn set_reserved_peers_move_previously_reserved() { + sp_tracing::try_init_simple(); + + let peerstore_handle = Arc::new(peerstore_handle_test()); + let known_peers = (0..3) + .map(|_| { + let peer = PeerId::random(); + peerstore_handle.add_known_peer(peer); + peer + }) + .collect::>(); + + // We'll keep this peer as reserved and move the the others to regular nodes. + let common_peer = *known_peers.iter().next().unwrap(); + let moved_peers = known_peers.iter().skip(1).copied().collect::>(); + let known_peers = known_peers.into_iter().collect::>(); + assert_eq!(moved_peers.len(), 2); + + let (mut peerset, to_peerset) = Peerset::new( + ProtocolName::from("/notif/1"), + 25, + 25, + false, + known_peers.clone(), + Default::default(), + peerstore_handle, + ); + assert_eq!(peerset.num_in(), 0usize); + assert_eq!(peerset.num_out(), 0usize); + + // We are not connected to the reserved peers. + match peerset.next().await { + Some(PeersetNotificationCommand::OpenSubstream { peers: out_peers }) => { + assert_eq!(out_peers.len(), 3); + + for peer in &out_peers { + assert_eq!( + peerset.peers().get(&peer), + Some(&PeerState::Opening { direction: Direction::Outbound(Reserved::Yes) }) ); } }, event => panic!("invalid event: {event:?}"), } - // verify that `Peerset` is aware of five peers, with two of them as outbound - // (the two disconnected peers) + // verify all three peers are marked as reserved peers and they don't count towards + // slot allocation. + assert_eq!(peerset.num_in(), 0usize); + assert_eq!(peerset.num_out(), 0usize); + assert_eq!(peerset.reserved_peers().len(), 3usize); + + // report that all substreams were opened + for peer in &known_peers { + assert!(std::matches!( + peerset.report_substream_opened(*peer, traits::Direction::Outbound), + OpenResult::Accept { .. } + )); + assert_eq!( + peerset.peers().get(peer), + Some(&PeerState::Connected { direction: Direction::Outbound(Reserved::Yes) }) + ); + } + + // set reserved peers with `common_peer` being one of them + let reserved_peers = HashSet::from_iter([common_peer, PeerId::random(), PeerId::random()]); + to_peerset + .unbounded_send(PeersetCommand::SetReservedPeers { peers: reserved_peers.clone() }) + .unwrap(); + + // The command `SetReservedPeers` might evict currently reserved peers if + // we don't have enough slot capacity to move them to regular nodes. + // In this case, we have enough capacity. + match peerset.next().await { + Some(PeersetNotificationCommand::CloseSubstream { peers }) => { + // This ensures we don't disconnect peers when receiving `SetReservedPeers`. + assert_eq!(peers.len(), 0); + }, + event => panic!("invalid event: {event:?}"), + } + + // verify that `Peerset` is aware of five peers. + // 2 of the previously reserved peers are moved as outbound regular peers and + // count towards slot allocation. assert_eq!(peerset.peers().len(), 5); assert_eq!(peerset.num_in(), 0usize); assert_eq!(peerset.num_out(), 2usize); + assert_eq!(peerset.reserved_peers().len(), 3usize); + + // Ensure the previously reserved are not regular nodes. + for (peer, state) in peerset.peers() { + // This peer was previously reserved and remained reserved after `SetReservedPeers`. + if peer == &common_peer { + assert_eq!( + state, + &PeerState::Connected { direction: Direction::Outbound(Reserved::Yes) } + ); + continue + } + + // Part of the new reserved nodes. + if reserved_peers.contains(peer) { + assert_eq!(state, &PeerState::Disconnected); + continue + } + + // Previously reserved, but remained connected. + if moved_peers.contains(peer) { + // This was previously `Reseved::Yes` but moved to regular nodes. + assert_eq!( + state, + &PeerState::Connected { direction: Direction::Outbound(Reserved::No) } + ); + continue + } + panic!("Invalid state peer={peer:?} state={state:?}"); + } match peerset.next().await { Some(PeersetNotificationCommand::OpenSubstream { peers }) => { + // Open desires with newly reserved. assert_eq!(peers.len(), 2); assert!(!peers.contains(&common_peer)); @@ -885,7 +1023,103 @@ async fn set_reserved_peers_but_available_slots() { assert_eq!(peerset.peers().len(), 5); assert_eq!(peerset.num_in(), 0usize); - - // two substreams are closing still closing assert_eq!(peerset.num_out(), 2usize); + assert_eq!(peerset.reserved_peers().len(), 3usize); +} + +#[tokio::test] +async fn set_reserved_peers_cannot_move_previously_reserved() { + sp_tracing::try_init_simple(); + + let peerstore_handle = Arc::new(peerstore_handle_test()); + let known_peers = (0..3) + .map(|_| { + let peer = PeerId::random(); + peerstore_handle.add_known_peer(peer); + peer + }) + .collect::>(); + + // We'll keep this peer as reserved and move the the others to regular nodes. + let common_peer = *known_peers.iter().next().unwrap(); + let moved_peers = known_peers.iter().skip(1).copied().collect::>(); + let known_peers = known_peers.into_iter().collect::>(); + assert_eq!(moved_peers.len(), 2); + + // We don't have capacity to move peers. + let (mut peerset, to_peerset) = Peerset::new( + ProtocolName::from("/notif/1"), + 0, + 0, + false, + known_peers.clone(), + Default::default(), + peerstore_handle, + ); + assert_eq!(peerset.num_in(), 0usize); + assert_eq!(peerset.num_out(), 0usize); + + // We are not connected to the reserved peers. + match peerset.next().await { + Some(PeersetNotificationCommand::OpenSubstream { peers: out_peers }) => { + assert_eq!(out_peers.len(), 3); + + for peer in &out_peers { + assert_eq!( + peerset.peers().get(&peer), + Some(&PeerState::Opening { direction: Direction::Outbound(Reserved::Yes) }) + ); + } + }, + event => panic!("invalid event: {event:?}"), + } + + // verify all three peers are marked as reserved peers and they don't count towards + // slot allocation. + assert_eq!(peerset.num_in(), 0usize); + assert_eq!(peerset.num_out(), 0usize); + assert_eq!(peerset.reserved_peers().len(), 3usize); + + // report that all substreams were opened + for peer in &known_peers { + assert!(std::matches!( + peerset.report_substream_opened(*peer, traits::Direction::Outbound), + OpenResult::Accept { .. } + )); + assert_eq!( + peerset.peers().get(peer), + Some(&PeerState::Connected { direction: Direction::Outbound(Reserved::Yes) }) + ); + } + + // set reserved peers with `common_peer` being one of them + let reserved_peers = HashSet::from_iter([common_peer, PeerId::random(), PeerId::random()]); + to_peerset + .unbounded_send(PeersetCommand::SetReservedPeers { peers: reserved_peers.clone() }) + .unwrap(); + + // The command `SetReservedPeers` might evict currently reserved peers if + // we don't have enough slot capacity to move them to regular nodes. + // In this case, we don't have enough capacity. + match peerset.next().await { + Some(PeersetNotificationCommand::CloseSubstream { peers }) => { + // This ensures we don't disconnect peers when receiving `SetReservedPeers`. + assert_eq!(peers.len(), 2); + + for peer in peers { + // Ensure common peer is not disconnected. + assert_ne!(common_peer, peer); + + assert_eq!( + peerset.peers().get(&peer), + Some(&PeerState::Closing { direction: Direction::Outbound(Reserved::Yes) }) + ); + } + }, + event => panic!("invalid event: {event:?}"), + } + + assert_eq!(peerset.num_in(), 0usize); + assert_eq!(peerset.num_out(), 0usize); + assert_eq!(peerset.reserved_peers().len(), 3usize); } diff --git a/substrate/client/network/sync/src/block_relay_protocol.rs b/substrate/client/network/sync/src/block_relay_protocol.rs index 3c5b3739e8222395b896003af8bc72e9ea68b9e7..13639d851b27241155701dbfcd5752a7d6b22639 100644 --- a/substrate/client/network/sync/src/block_relay_protocol.rs +++ b/substrate/client/network/sync/src/block_relay_protocol.rs @@ -21,7 +21,7 @@ use sc_network::{request_responses::RequestFailure, NetworkBackend, ProtocolName use sc_network_common::sync::message::{BlockData, BlockRequest}; use sc_network_types::PeerId; use sp_runtime::traits::Block as BlockT; -use std::sync::Arc; +use std::{fmt, sync::Arc}; /// The serving side of the block relay protocol. It runs a single instance /// of the server task that processes the incoming protocol messages. @@ -34,7 +34,10 @@ pub trait BlockServer: Send { /// The client side stub to download blocks from peers. This is a handle /// that can be used to initiate concurrent downloads. #[async_trait::async_trait] -pub trait BlockDownloader: Send + Sync { +pub trait BlockDownloader: fmt::Debug + Send + Sync { + /// Protocol name used by block downloader. + fn protocol_name(&self) -> &ProtocolName; + /// Performs the protocol specific sequence to fetch the blocks from the peer. /// Output: if the download succeeds, the response is a `Vec` which is /// in a format specific to the protocol implementation. The block data diff --git a/substrate/client/network/sync/src/block_request_handler.rs b/substrate/client/network/sync/src/block_request_handler.rs index 6e970b3993106958a2b2bafbd28ae0bc11afad2f..80234170bc203a9d6823dc8a5352e3ac51b6abd8 100644 --- a/substrate/client/network/sync/src/block_request_handler.rs +++ b/substrate/client/network/sync/src/block_request_handler.rs @@ -502,6 +502,7 @@ enum HandleRequestError { } /// The full block downloader implementation of [`BlockDownloader]. +#[derive(Debug)] pub struct FullBlockDownloader { protocol_name: ProtocolName, network: NetworkServiceHandle, @@ -576,6 +577,10 @@ impl FullBlockDownloader { #[async_trait::async_trait] impl BlockDownloader for FullBlockDownloader { + fn protocol_name(&self) -> &ProtocolName { + &self.protocol_name + } + async fn download_blocks( &self, who: PeerId, diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index dceea9954c6e9ff696d9ee01098713857984ed41..cc2089d1974c37f1444aa5c8cdcdc122a259f78f 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -23,30 +23,22 @@ use crate::{ block_announce_validator::{ BlockAnnounceValidationResult, BlockAnnounceValidator as BlockAnnounceValidatorStream, }, - block_relay_protocol::{BlockDownloader, BlockResponseError}, pending_responses::{PendingResponses, ResponseEvent}, - schema::v1::{StateRequest, StateResponse}, service::{ self, syncing_service::{SyncingService, ToServiceCommand}, }, - strategy::{ - warp::{EncodedProof, WarpProofRequest}, - StrategyKey, SyncingAction, SyncingStrategy, - }, - types::{ - BadPeer, ExtendedPeerInfo, OpaqueStateRequest, OpaqueStateResponse, PeerRequest, SyncEvent, - }, + strategy::{SyncingAction, SyncingStrategy}, + types::{BadPeer, ExtendedPeerInfo, SyncEvent}, LOG_TARGET, }; use codec::{Decode, DecodeAll, Encode}; -use futures::{channel::oneshot, FutureExt, StreamExt}; +use futures::{channel::oneshot, StreamExt}; use log::{debug, error, trace, warn}; use prometheus_endpoint::{ register, Counter, Gauge, MetricSource, Opts, PrometheusError, Registry, SourcedGauge, U64, }; -use prost::Message; use schnellru::{ByLength, LruMap}; use tokio::time::{Interval, MissedTickBehavior}; @@ -55,7 +47,7 @@ use sc_consensus::{import_queue::ImportQueueService, IncomingBlock}; use sc_network::{ config::{FullNetworkConfiguration, NotificationHandshake, ProtocolId, SetConfig}, peer_store::PeerStoreProvider, - request_responses::{IfDisconnected, OutboundFailure, RequestFailure}, + request_responses::{OutboundFailure, RequestFailure}, service::{ traits::{Direction, NotificationConfig, NotificationEvent, ValidationResult}, NotificationMetrics, @@ -66,7 +58,7 @@ use sc_network::{ }; use sc_network_common::{ role::Roles, - sync::message::{BlockAnnounce, BlockAnnouncesHandshake, BlockRequest, BlockState}, + sync::message::{BlockAnnounce, BlockAnnouncesHandshake, BlockState}, }; use sc_network_types::PeerId; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; @@ -102,8 +94,6 @@ mod rep { pub const GENESIS_MISMATCH: Rep = Rep::new_fatal("Genesis mismatch"); /// Peer send us a block announcement that failed at validation. pub const BAD_BLOCK_ANNOUNCEMENT: Rep = Rep::new(-(1 << 12), "Bad block announcement"); - /// We received a message that failed to decode. - pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message"); /// Peer is on unsupported protocol version. pub const BAD_PROTOCOL: Rep = Rep::new_fatal("Unsupported protocol"); /// Reputation change when a peer refuses a request. @@ -264,10 +254,7 @@ pub struct SyncingEngine { peer_store_handle: Arc, /// Pending responses - pending_responses: PendingResponses, - - /// Block downloader - block_downloader: Arc>, + pending_responses: PendingResponses, /// Handle to import queue. import_queue: Box>, @@ -291,12 +278,11 @@ where network_metrics: NotificationMetrics, net_config: &FullNetworkConfiguration::Hash, N>, protocol_id: ProtocolId, - fork_id: &Option, + fork_id: Option<&str>, block_announce_validator: Box + Send>, syncing_strategy: Box>, network_service: service::network::NetworkServiceHandle, import_queue: Box>, - block_downloader: Arc>, peer_store_handle: Arc, ) -> Result<(Self, SyncingService, N::NotificationProtocolConfig), ClientError> where @@ -417,7 +403,6 @@ where None }, pending_responses: PendingResponses::new(), - block_downloader, import_queue, }, SyncingService::new(tx, num_connected, is_major_syncing), @@ -583,57 +568,42 @@ where } fn process_strategy_actions(&mut self) -> Result<(), ClientError> { - for action in self.strategy.actions()? { + for action in self.strategy.actions(&self.network_service)? { match action { - SyncingAction::SendBlockRequest { peer_id, key, request } => { - // Sending block request implies dropping obsolete pending response as we are - // not interested in it anymore (see [`SyncingAction::SendBlockRequest`]). - let removed = self.pending_responses.remove(peer_id, key); - self.send_block_request(peer_id, key, request.clone()); - - if removed { - warn!( - target: LOG_TARGET, - "Processed `ChainSyncAction::SendBlockRequest` to {} from {:?} with {:?}. \ - Stale response removed!", - peer_id, - key, - request, - ) - } else { + SyncingAction::StartRequest { peer_id, key, request, remove_obsolete } => { + if !self.peers.contains_key(&peer_id) { trace!( target: LOG_TARGET, - "Processed `ChainSyncAction::SendBlockRequest` to {} from {:?} with {:?}.", - peer_id, - key, - request, - ) + "Cannot start request with strategy key {key:?} to unknown peer \ + {peer_id}", + ); + debug_assert!(false); + continue; } + if remove_obsolete { + if self.pending_responses.remove(peer_id, key) { + warn!( + target: LOG_TARGET, + "Processed `SyncingAction::StartRequest` to {peer_id} with \ + strategy key {key:?}. Stale response removed!", + ) + } else { + trace!( + target: LOG_TARGET, + "Processed `SyncingAction::StartRequest` to {peer_id} with \ + strategy key {key:?}.", + ) + } + } + + self.pending_responses.insert(peer_id, key, request); }, SyncingAction::CancelRequest { peer_id, key } => { let removed = self.pending_responses.remove(peer_id, key); trace!( target: LOG_TARGET, - "Processed {action:?}, response removed: {removed}.", - ); - }, - SyncingAction::SendStateRequest { peer_id, key, protocol_name, request } => { - self.send_state_request(peer_id, key, protocol_name, request); - - trace!( - target: LOG_TARGET, - "Processed `ChainSyncAction::SendStateRequest` to {peer_id}.", - ); - }, - SyncingAction::SendWarpProofRequest { peer_id, key, protocol_name, request } => { - self.send_warp_proof_request(peer_id, key, protocol_name, request.clone()); - - trace!( - target: LOG_TARGET, - "Processed `ChainSyncAction::SendWarpProofRequest` to {}, request: {:?}.", - peer_id, - request, + "Processed `SyncingAction::CancelRequest`, response removed: {removed}.", ); }, SyncingAction::DropPeer(BadPeer(peer_id, rep)) => { @@ -1000,160 +970,12 @@ where Ok(()) } - fn send_block_request(&mut self, peer_id: PeerId, key: StrategyKey, request: BlockRequest) { - if !self.peers.contains_key(&peer_id) { - trace!(target: LOG_TARGET, "Cannot send block request to unknown peer {peer_id}"); - debug_assert!(false); - return; - } - - let downloader = self.block_downloader.clone(); - - self.pending_responses.insert( - peer_id, - key, - PeerRequest::Block(request.clone()), - async move { downloader.download_blocks(peer_id, request).await }.boxed(), - ); - } - - fn send_state_request( - &mut self, - peer_id: PeerId, - key: StrategyKey, - protocol_name: ProtocolName, - request: OpaqueStateRequest, - ) { - if !self.peers.contains_key(&peer_id) { - trace!(target: LOG_TARGET, "Cannot send state request to unknown peer {peer_id}"); - debug_assert!(false); - return; - } - - let (tx, rx) = oneshot::channel(); - - self.pending_responses.insert(peer_id, key, PeerRequest::State, rx.boxed()); - - match Self::encode_state_request(&request) { - Ok(data) => { - self.network_service.start_request( - peer_id, - protocol_name, - data, - tx, - IfDisconnected::ImmediateError, - ); - }, - Err(err) => { - log::warn!( - target: LOG_TARGET, - "Failed to encode state request {request:?}: {err:?}", - ); - }, - } - } - - fn send_warp_proof_request( - &mut self, - peer_id: PeerId, - key: StrategyKey, - protocol_name: ProtocolName, - request: WarpProofRequest, - ) { - if !self.peers.contains_key(&peer_id) { - trace!(target: LOG_TARGET, "Cannot send warp proof request to unknown peer {peer_id}"); - debug_assert!(false); - return; - } - - let (tx, rx) = oneshot::channel(); - - self.pending_responses.insert(peer_id, key, PeerRequest::WarpProof, rx.boxed()); - - self.network_service.start_request( - peer_id, - protocol_name, - request.encode(), - tx, - IfDisconnected::ImmediateError, - ); - } - - fn encode_state_request(request: &OpaqueStateRequest) -> Result, String> { - let request: &StateRequest = request.0.downcast_ref().ok_or_else(|| { - "Failed to downcast opaque state response during encoding, this is an \ - implementation bug." - .to_string() - })?; - - Ok(request.encode_to_vec()) - } - - fn decode_state_response(response: &[u8]) -> Result { - let response = StateResponse::decode(response) - .map_err(|error| format!("Failed to decode state response: {error}"))?; - - Ok(OpaqueStateResponse(Box::new(response))) - } - - fn process_response_event(&mut self, response_event: ResponseEvent) { - let ResponseEvent { peer_id, key, request, response } = response_event; + fn process_response_event(&mut self, response_event: ResponseEvent) { + let ResponseEvent { peer_id, key, response: response_result } = response_event; - match response { - Ok(Ok((resp, _))) => match request { - PeerRequest::Block(req) => { - match self.block_downloader.block_response_into_blocks(&req, resp) { - Ok(blocks) => { - self.strategy.on_block_response(peer_id, key, req, blocks); - }, - Err(BlockResponseError::DecodeFailed(e)) => { - debug!( - target: LOG_TARGET, - "Failed to decode block response from peer {:?}: {:?}.", - peer_id, - e - ); - self.network_service.report_peer(peer_id, rep::BAD_MESSAGE); - self.network_service.disconnect_peer( - peer_id, - self.block_announce_protocol_name.clone(), - ); - return; - }, - Err(BlockResponseError::ExtractionFailed(e)) => { - debug!( - target: LOG_TARGET, - "Failed to extract blocks from peer response {:?}: {:?}.", - peer_id, - e - ); - self.network_service.report_peer(peer_id, rep::BAD_MESSAGE); - return; - }, - } - }, - PeerRequest::State => { - let response = match Self::decode_state_response(&resp[..]) { - Ok(proto) => proto, - Err(e) => { - debug!( - target: LOG_TARGET, - "Failed to decode state response from peer {peer_id:?}: {e:?}.", - ); - self.network_service.report_peer(peer_id, rep::BAD_MESSAGE); - self.network_service.disconnect_peer( - peer_id, - self.block_announce_protocol_name.clone(), - ); - return; - }, - }; - - self.strategy.on_state_response(peer_id, key, response); - }, - PeerRequest::WarpProof => { - self.strategy.on_warp_proof_response(&peer_id, key, EncodedProof(resp)); - }, + match response_result { + Ok(Ok((response, protocol_name))) => { + self.strategy.on_generic_response(&peer_id, key, protocol_name, response); }, Ok(Err(e)) => { debug!(target: LOG_TARGET, "Request to peer {peer_id:?} failed: {e:?}."); @@ -1214,7 +1036,7 @@ where /// Get config for the block announcement protocol fn get_block_announce_proto_config::Hash>>( protocol_id: ProtocolId, - fork_id: &Option, + fork_id: Option<&str>, roles: Roles, best_number: NumberFor, best_hash: B::Hash, @@ -1225,7 +1047,7 @@ where ) -> (N::NotificationProtocolConfig, Box) { let block_announces_protocol = { let genesis_hash = genesis_hash.as_ref(); - if let Some(ref fork_id) = fork_id { + if let Some(fork_id) = fork_id { format!( "/{}/{}/block-announces/1", array_bytes::bytes2hex("", genesis_hash), diff --git a/substrate/client/network/sync/src/lib.rs b/substrate/client/network/sync/src/lib.rs index c458c7a5da498bbec42db83b5df210eda9fe5d70..e503a1cbdb1853c2e8ac46f2c271ac2b10f95930 100644 --- a/substrate/client/network/sync/src/lib.rs +++ b/substrate/client/network/sync/src/lib.rs @@ -18,6 +18,7 @@ //! Blockchain syncing implementation in Substrate. +pub use schema::v1::*; pub use service::syncing_service::SyncingService; pub use strategy::warp::{WarpSyncConfig, WarpSyncPhase, WarpSyncProgress}; pub use types::{SyncEvent, SyncEventStream, SyncState, SyncStatus, SyncStatusProvider}; diff --git a/substrate/client/network/sync/src/mock.rs b/substrate/client/network/sync/src/mock.rs index 741fa7139583f0453d97810cba071fb37ee6e5ee..bf25156f9703dc67d3ff84edf1599c70995c45ae 100644 --- a/substrate/client/network/sync/src/mock.rs +++ b/substrate/client/network/sync/src/mock.rs @@ -27,10 +27,13 @@ use sc_network_types::PeerId; use sp_runtime::traits::Block as BlockT; mockall::mock! { + #[derive(Debug)] pub BlockDownloader {} #[async_trait::async_trait] impl BlockDownloaderT for BlockDownloader { + fn protocol_name(&self) -> &ProtocolName; + async fn download_blocks( &self, who: PeerId, diff --git a/substrate/client/network/sync/src/pending_responses.rs b/substrate/client/network/sync/src/pending_responses.rs index 7d2d598a2e061b5daa252dd12076cc0534bb44c8..46e6ae62632819f176a68a3d0668ff9c8c921fb5 100644 --- a/substrate/client/network/sync/src/pending_responses.rs +++ b/substrate/client/network/sync/src/pending_responses.rs @@ -19,7 +19,7 @@ //! [`PendingResponses`] is responsible for keeping track of pending responses and //! polling them. [`Stream`] implemented by [`PendingResponses`] never terminates. -use crate::{strategy::StrategyKey, types::PeerRequest, LOG_TARGET}; +use crate::{strategy::StrategyKey, LOG_TARGET}; use futures::{ channel::oneshot, future::BoxFuture, @@ -27,61 +27,49 @@ use futures::{ FutureExt, StreamExt, }; use log::error; +use std::any::Any; use sc_network::{request_responses::RequestFailure, types::ProtocolName}; use sc_network_types::PeerId; -use sp_runtime::traits::Block as BlockT; use std::task::{Context, Poll, Waker}; use tokio_stream::StreamMap; /// Response result. -type ResponseResult = Result, ProtocolName), RequestFailure>, oneshot::Canceled>; +type ResponseResult = + Result, ProtocolName), RequestFailure>, oneshot::Canceled>; /// A future yielding [`ResponseResult`]. -type ResponseFuture = BoxFuture<'static, ResponseResult>; +pub(crate) type ResponseFuture = BoxFuture<'static, ResponseResult>; /// An event we receive once a pending response future resolves. -pub(crate) struct ResponseEvent { +pub(crate) struct ResponseEvent { pub peer_id: PeerId, pub key: StrategyKey, - pub request: PeerRequest, pub response: ResponseResult, } /// Stream taking care of polling pending responses. -pub(crate) struct PendingResponses { +pub(crate) struct PendingResponses { /// Pending responses - pending_responses: - StreamMap<(PeerId, StrategyKey), BoxStream<'static, (PeerRequest, ResponseResult)>>, + pending_responses: StreamMap<(PeerId, StrategyKey), BoxStream<'static, ResponseResult>>, /// Waker to implement never terminating stream waker: Option, } -impl PendingResponses { +impl PendingResponses { pub fn new() -> Self { Self { pending_responses: StreamMap::new(), waker: None } } - pub fn insert( - &mut self, - peer_id: PeerId, - key: StrategyKey, - request: PeerRequest, - response_future: ResponseFuture, - ) { - let request_type = request.get_type(); - + pub fn insert(&mut self, peer_id: PeerId, key: StrategyKey, response_future: ResponseFuture) { if self .pending_responses - .insert( - (peer_id, key), - Box::pin(async move { (request, response_future.await) }.into_stream()), - ) + .insert((peer_id, key), Box::pin(response_future.into_stream())) .is_some() { error!( target: LOG_TARGET, - "Discarded pending response from peer {peer_id}, request type: {request_type:?}.", + "Discarded pending response from peer {peer_id}, strategy key: {key:?}.", ); debug_assert!(false); } @@ -112,21 +100,21 @@ impl PendingResponses { } } -impl Stream for PendingResponses { - type Item = ResponseEvent; +impl Stream for PendingResponses { + type Item = ResponseEvent; fn poll_next( mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { match self.pending_responses.poll_next_unpin(cx) { - Poll::Ready(Some(((peer_id, key), (request, response)))) => { + Poll::Ready(Some(((peer_id, key), response))) => { // We need to manually remove the stream, because `StreamMap` doesn't know yet that // it's going to yield `None`, so may not remove it before the next request is made // to the same peer. self.pending_responses.remove(&(peer_id, key)); - Poll::Ready(Some(ResponseEvent { peer_id, key, request, response })) + Poll::Ready(Some(ResponseEvent { peer_id, key, response })) }, Poll::Ready(None) | Poll::Pending => { self.waker = Some(cx.waker().clone()); @@ -138,7 +126,7 @@ impl Stream for PendingResponses { } // As [`PendingResponses`] never terminates, we can easily implement [`FusedStream`] for it. -impl FusedStream for PendingResponses { +impl FusedStream for PendingResponses { fn is_terminated(&self) -> bool { false } diff --git a/substrate/client/network/sync/src/service/mock.rs b/substrate/client/network/sync/src/service/mock.rs index 141edc7c884144455e43f5b7c5b2b62ae246bbab..300aa076515f8095ba19faa8149062cd97c5325a 100644 --- a/substrate/client/network/sync/src/service/mock.rs +++ b/substrate/client/network/sync/src/service/mock.rs @@ -45,19 +45,19 @@ mockall::mock! { impl sc_consensus::Link for ChainSyncInterface { fn blocks_processed( - &mut self, + &self, imported: usize, count: usize, results: Vec<(Result>, BlockImportError>, B::Hash)>, ); fn justification_imported( - &mut self, + &self, who: PeerId, hash: &B::Hash, number: NumberFor, success: bool, ); - fn request_justification(&mut self, hash: &B::Hash, number: NumberFor); + fn request_justification(&self, hash: &B::Hash, number: NumberFor); } } diff --git a/substrate/client/network/sync/src/service/network.rs b/substrate/client/network/sync/src/service/network.rs index e848b5f62c1b8ab9309bf71029862f0548432097..139e1a986a927b757ac4fc6eed74323b5c7573a0 100644 --- a/substrate/client/network/sync/src/service/network.rs +++ b/substrate/client/network/sync/src/service/network.rs @@ -39,9 +39,11 @@ impl Network for T where T: NetworkPeers + NetworkRequest {} /// calls the `NetworkService` on its behalf. pub struct NetworkServiceProvider { rx: TracingUnboundedReceiver, + handle: NetworkServiceHandle, } /// Commands that `ChainSync` wishes to send to `NetworkService` +#[derive(Debug)] pub enum ToServiceCommand { /// Call `NetworkPeers::disconnect_peer()` DisconnectPeer(PeerId, ProtocolName), @@ -61,7 +63,7 @@ pub enum ToServiceCommand { /// Handle that is (temporarily) passed to `ChainSync` so it can /// communicate with `NetworkService` through `SyncingEngine` -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct NetworkServiceHandle { tx: TracingUnboundedSender, } @@ -99,15 +101,23 @@ impl NetworkServiceHandle { impl NetworkServiceProvider { /// Create new `NetworkServiceProvider` - pub fn new() -> (Self, NetworkServiceHandle) { + pub fn new() -> Self { let (tx, rx) = tracing_unbounded("mpsc_network_service_provider", 100_000); - (Self { rx }, NetworkServiceHandle::new(tx)) + Self { rx, handle: NetworkServiceHandle::new(tx) } + } + + /// Get handle to talk to the provider + pub fn handle(&self) -> NetworkServiceHandle { + self.handle.clone() } /// Run the `NetworkServiceProvider` - pub async fn run(mut self, service: Arc) { - while let Some(inner) = self.rx.next().await { + pub async fn run(self, service: Arc) { + let Self { mut rx, handle } = self; + drop(handle); + + while let Some(inner) = rx.next().await { match inner { ToServiceCommand::DisconnectPeer(peer, protocol_name) => service.disconnect_peer(peer, protocol_name), @@ -129,7 +139,8 @@ mod tests { // and then reported #[tokio::test] async fn disconnect_and_report_peer() { - let (provider, handle) = NetworkServiceProvider::new(); + let provider = NetworkServiceProvider::new(); + let handle = provider.handle(); let peer = PeerId::random(); let proto = ProtocolName::from("test-protocol"); diff --git a/substrate/client/network/sync/src/service/syncing_service.rs b/substrate/client/network/sync/src/service/syncing_service.rs index 08a2b36118a9d4d7d1b780c62a848a789562babf..b56af2b9976a176d54ba2dda287dd7539ddaf160 100644 --- a/substrate/client/network/sync/src/service/syncing_service.rs +++ b/substrate/client/network/sync/src/service/syncing_service.rs @@ -177,7 +177,7 @@ impl SyncStatusProvider for SyncingService { impl Link for SyncingService { fn blocks_processed( - &mut self, + &self, imported: usize, count: usize, results: Vec<(Result>, BlockImportError>, B::Hash)>, @@ -188,7 +188,7 @@ impl Link for SyncingService { } fn justification_imported( - &mut self, + &self, who: PeerId, hash: &B::Hash, number: NumberFor, @@ -199,7 +199,7 @@ impl Link for SyncingService { .unbounded_send(ToServiceCommand::JustificationImported(who, *hash, number, success)); } - fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { + fn request_justification(&self, hash: &B::Hash, number: NumberFor) { let _ = self.tx.unbounded_send(ToServiceCommand::RequestJustification(*hash, number)); } } diff --git a/substrate/client/network/sync/src/strategy.rs b/substrate/client/network/sync/src/strategy.rs index 81998b7576bbfb807fbfc8c8e1d79ff087ff8906..2ac6674231e5447e97cbe631fb978845d479f336 100644 --- a/substrate/client/network/sync/src/strategy.rs +++ b/substrate/client/network/sync/src/strategy.rs @@ -16,50 +16,35 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! [`PolkadotSyncingStrategy`] is a proxy between [`crate::engine::SyncingEngine`] -//! and specific syncing algorithms. +//! [`SyncingStrategy`] defines an interface [`crate::engine::SyncingEngine`] uses as a specific +//! syncing algorithm. +//! +//! A few different strategies are provided by Substrate out of the box with custom strategies +//! possible too. pub mod chain_sync; mod disconnected_peers; -mod state; +pub mod polkadot; +pub mod state; pub mod state_sync; pub mod warp; use crate::{ - block_request_handler::MAX_BLOCKS_IN_RESPONSE, - types::{BadPeer, OpaqueStateRequest, OpaqueStateResponse, SyncStatus}, - LOG_TARGET, + pending_responses::ResponseFuture, + service::network::NetworkServiceHandle, + types::{BadPeer, SyncStatus}, }; -use chain_sync::{ChainSync, ChainSyncMode}; -use log::{debug, error, info}; -use prometheus_endpoint::Registry; -use sc_client_api::{BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; use sc_network::ProtocolName; -use sc_network_common::sync::{ - message::{BlockAnnounce, BlockData, BlockRequest}, - SyncMode, -}; +use sc_network_common::sync::message::BlockAnnounce; use sc_network_types::PeerId; -use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata}; +use sp_blockchain::Error as ClientError; use sp_consensus::BlockOrigin; use sp_runtime::{ - traits::{Block as BlockT, Header, NumberFor}, + traits::{Block as BlockT, NumberFor}, Justifications, }; -use state::{StateStrategy, StateStrategyAction}; -use std::{collections::HashMap, sync::Arc}; -use warp::{EncodedProof, WarpProofRequest, WarpSync, WarpSyncAction, WarpSyncConfig}; - -/// Corresponding `ChainSync` mode. -fn chain_sync_mode(sync_mode: SyncMode) -> ChainSyncMode { - match sync_mode { - SyncMode::Full => ChainSyncMode::Full, - SyncMode::LightState { skip_proofs, storage_chain_mode } => - ChainSyncMode::LightState { skip_proofs, storage_chain_mode }, - SyncMode::Warp => ChainSyncMode::Full, - } -} +use std::any::Any; /// Syncing strategy for syncing engine to use pub trait SyncingStrategy: Send @@ -101,29 +86,16 @@ where /// Report a justification import (successful or not). fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool); - /// Process block response. - fn on_block_response( - &mut self, - peer_id: PeerId, - key: StrategyKey, - request: BlockRequest, - blocks: Vec>, - ); - - /// Process state response. - fn on_state_response( - &mut self, - peer_id: PeerId, - key: StrategyKey, - response: OpaqueStateResponse, - ); - - /// Process warp proof response. - fn on_warp_proof_response( + /// Process generic response. + /// + /// Strategy has to create opaque response and should be to downcast it back into concrete type + /// internally. Failure to downcast is an implementation bug. + fn on_generic_response( &mut self, peer_id: &PeerId, key: StrategyKey, - response: EncodedProof, + protocol_name: ProtocolName, + response: Box, ); /// A batch of blocks that have been processed, with or without errors. @@ -160,52 +132,32 @@ where /// Get actions that should be performed by the owner on the strategy's behalf #[must_use] - fn actions(&mut self) -> Result>, ClientError>; -} - -/// Syncing configuration containing data for all strategies. -#[derive(Clone, Debug)] -pub struct SyncingConfig { - /// Syncing mode. - pub mode: SyncMode, - /// The number of parallel downloads to guard against slow peers. - pub max_parallel_downloads: u32, - /// Maximum number of blocks to request. - pub max_blocks_per_request: u32, - /// Prometheus metrics registry. - pub metrics_registry: Option, - /// Protocol name used to send out state requests - pub state_request_protocol_name: ProtocolName, + fn actions( + &mut self, + // TODO: Consider making this internal property of the strategy + network_service: &NetworkServiceHandle, + ) -> Result>, ClientError>; } /// The key identifying a specific strategy for responses routing. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum StrategyKey { - /// Warp sync initiated this request. - Warp, - /// State sync initiated this request. - State, - /// `ChainSync` initiated this request. - ChainSync, +pub struct StrategyKey(&'static str); + +impl StrategyKey { + /// Instantiate opaque strategy key. + pub const fn new(key: &'static str) -> Self { + Self(key) + } } -#[derive(Debug)] pub enum SyncingAction { - /// Send block request to peer. Always implies dropping a stale block request to the same peer. - SendBlockRequest { peer_id: PeerId, key: StrategyKey, request: BlockRequest }, - /// Send state request to peer. - SendStateRequest { - peer_id: PeerId, - key: StrategyKey, - protocol_name: ProtocolName, - request: OpaqueStateRequest, - }, - /// Send warp proof request to peer. - SendWarpProofRequest { + /// Start request to peer. + StartRequest { peer_id: PeerId, key: StrategyKey, - protocol_name: ProtocolName, - request: WarpProofRequest, + request: ResponseFuture, + // Whether to remove obsolete pending responses. + remove_obsolete: bool, }, /// Drop stale request. CancelRequest { peer_id: PeerId, key: StrategyKey }, @@ -225,444 +177,20 @@ pub enum SyncingAction { } impl SyncingAction { - fn is_finished(&self) -> bool { + /// Returns `true` if the syncing action has completed. + pub fn is_finished(&self) -> bool { matches!(self, SyncingAction::Finished) } -} - -impl From> for SyncingAction { - fn from(action: WarpSyncAction) -> Self { - match action { - WarpSyncAction::SendWarpProofRequest { peer_id, protocol_name, request } => - SyncingAction::SendWarpProofRequest { - peer_id, - key: StrategyKey::Warp, - protocol_name, - request, - }, - WarpSyncAction::SendBlockRequest { peer_id, request } => - SyncingAction::SendBlockRequest { peer_id, key: StrategyKey::Warp, request }, - WarpSyncAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), - WarpSyncAction::Finished => SyncingAction::Finished, - } - } -} - -impl From> for SyncingAction { - fn from(action: StateStrategyAction) -> Self { - match action { - StateStrategyAction::SendStateRequest { peer_id, protocol_name, request } => - SyncingAction::SendStateRequest { - peer_id, - key: StrategyKey::State, - protocol_name, - request, - }, - StateStrategyAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), - StateStrategyAction::ImportBlocks { origin, blocks } => - SyncingAction::ImportBlocks { origin, blocks }, - StateStrategyAction::Finished => SyncingAction::Finished, - } - } -} - -/// Proxy to specific syncing strategies used in Polkadot. -pub struct PolkadotSyncingStrategy { - /// Initial syncing configuration. - config: SyncingConfig, - /// Client used by syncing strategies. - client: Arc, - /// Warp strategy. - warp: Option>, - /// State strategy. - state: Option>, - /// `ChainSync` strategy.` - chain_sync: Option>, - /// Connected peers and their best blocks used to seed a new strategy when switching to it in - /// `PolkadotSyncingStrategy::proceed_to_next`. - peer_best_blocks: HashMap)>, -} - -impl SyncingStrategy for PolkadotSyncingStrategy -where - B: BlockT, - Client: HeaderBackend - + BlockBackend - + HeaderMetadata - + ProofProvider - + Send - + Sync - + 'static, -{ - fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor) { - self.peer_best_blocks.insert(peer_id, (best_hash, best_number)); - - self.warp.as_mut().map(|s| s.add_peer(peer_id, best_hash, best_number)); - self.state.as_mut().map(|s| s.add_peer(peer_id, best_hash, best_number)); - self.chain_sync.as_mut().map(|s| s.add_peer(peer_id, best_hash, best_number)); - } - - fn remove_peer(&mut self, peer_id: &PeerId) { - self.warp.as_mut().map(|s| s.remove_peer(peer_id)); - self.state.as_mut().map(|s| s.remove_peer(peer_id)); - self.chain_sync.as_mut().map(|s| s.remove_peer(peer_id)); - - self.peer_best_blocks.remove(peer_id); - } - - fn on_validated_block_announce( - &mut self, - is_best: bool, - peer_id: PeerId, - announce: &BlockAnnounce, - ) -> Option<(B::Hash, NumberFor)> { - let new_best = if let Some(ref mut warp) = self.warp { - warp.on_validated_block_announce(is_best, peer_id, announce) - } else if let Some(ref mut state) = self.state { - state.on_validated_block_announce(is_best, peer_id, announce) - } else if let Some(ref mut chain_sync) = self.chain_sync { - chain_sync.on_validated_block_announce(is_best, peer_id, announce) - } else { - error!(target: LOG_TARGET, "No syncing strategy is active."); - debug_assert!(false); - Some((announce.header.hash(), *announce.header.number())) - }; - - if let Some(new_best) = new_best { - if let Some(best) = self.peer_best_blocks.get_mut(&peer_id) { - *best = new_best; - } else { - debug!( - target: LOG_TARGET, - "Cannot update `peer_best_blocks` as peer {peer_id} is not known to `Strategy` \ - (already disconnected?)", - ); - } - } - - new_best - } - - fn set_sync_fork_request(&mut self, peers: Vec, hash: &B::Hash, number: NumberFor) { - // Fork requests are only handled by `ChainSync`. - if let Some(ref mut chain_sync) = self.chain_sync { - chain_sync.set_sync_fork_request(peers.clone(), hash, number); - } - } - - fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { - // Justifications can only be requested via `ChainSync`. - if let Some(ref mut chain_sync) = self.chain_sync { - chain_sync.request_justification(hash, number); - } - } - - fn clear_justification_requests(&mut self) { - // Justification requests can only be cleared by `ChainSync`. - if let Some(ref mut chain_sync) = self.chain_sync { - chain_sync.clear_justification_requests(); - } - } - - fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool) { - // Only `ChainSync` is interested in justification import. - if let Some(ref mut chain_sync) = self.chain_sync { - chain_sync.on_justification_import(hash, number, success); - } - } - - fn on_block_response( - &mut self, - peer_id: PeerId, - key: StrategyKey, - request: BlockRequest, - blocks: Vec>, - ) { - if let (StrategyKey::Warp, Some(ref mut warp)) = (key, &mut self.warp) { - warp.on_block_response(peer_id, request, blocks); - } else if let (StrategyKey::ChainSync, Some(ref mut chain_sync)) = - (key, &mut self.chain_sync) - { - chain_sync.on_block_response(peer_id, key, request, blocks); - } else { - error!( - target: LOG_TARGET, - "`on_block_response()` called with unexpected key {key:?} \ - or corresponding strategy is not active.", - ); - debug_assert!(false); - } - } - - fn on_state_response( - &mut self, - peer_id: PeerId, - key: StrategyKey, - response: OpaqueStateResponse, - ) { - if let (StrategyKey::State, Some(ref mut state)) = (key, &mut self.state) { - state.on_state_response(peer_id, response); - } else if let (StrategyKey::ChainSync, Some(ref mut chain_sync)) = - (key, &mut self.chain_sync) - { - chain_sync.on_state_response(peer_id, key, response); - } else { - error!( - target: LOG_TARGET, - "`on_state_response()` called with unexpected key {key:?} \ - or corresponding strategy is not active.", - ); - debug_assert!(false); - } - } - - fn on_warp_proof_response( - &mut self, - peer_id: &PeerId, - key: StrategyKey, - response: EncodedProof, - ) { - if let (StrategyKey::Warp, Some(ref mut warp)) = (key, &mut self.warp) { - warp.on_warp_proof_response(peer_id, response); - } else { - error!( - target: LOG_TARGET, - "`on_warp_proof_response()` called with unexpected key {key:?} \ - or warp strategy is not active", - ); - debug_assert!(false); - } - } - - fn on_blocks_processed( - &mut self, - imported: usize, - count: usize, - results: Vec<(Result>, BlockImportError>, B::Hash)>, - ) { - // Only `StateStrategy` and `ChainSync` are interested in block processing notifications. - if let Some(ref mut state) = self.state { - state.on_blocks_processed(imported, count, results); - } else if let Some(ref mut chain_sync) = self.chain_sync { - chain_sync.on_blocks_processed(imported, count, results); - } - } - - fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor) { - // Only `ChainSync` is interested in block finalization notifications. - if let Some(ref mut chain_sync) = self.chain_sync { - chain_sync.on_block_finalized(hash, number); - } - } - - fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor) { - // This is relevant to `ChainSync` only. - if let Some(ref mut chain_sync) = self.chain_sync { - chain_sync.update_chain_info(best_hash, best_number); - } - } - - fn is_major_syncing(&self) -> bool { - self.warp.is_some() || - self.state.is_some() || - match self.chain_sync { - Some(ref s) => s.status().state.is_major_syncing(), - None => unreachable!("At least one syncing strategy is active; qed"), - } - } - - fn num_peers(&self) -> usize { - self.peer_best_blocks.len() - } - - fn status(&self) -> SyncStatus { - // This function presumes that strategies are executed serially and must be refactored - // once we have parallel strategies. - if let Some(ref warp) = self.warp { - warp.status() - } else if let Some(ref state) = self.state { - state.status() - } else if let Some(ref chain_sync) = self.chain_sync { - chain_sync.status() - } else { - unreachable!("At least one syncing strategy is always active; qed") - } - } - - fn num_downloaded_blocks(&self) -> usize { - self.chain_sync - .as_ref() - .map_or(0, |chain_sync| chain_sync.num_downloaded_blocks()) - } - - fn num_sync_requests(&self) -> usize { - self.chain_sync.as_ref().map_or(0, |chain_sync| chain_sync.num_sync_requests()) - } - - fn actions(&mut self) -> Result>, ClientError> { - // This function presumes that strategies are executed serially and must be refactored once - // we have parallel strategies. - let actions: Vec<_> = if let Some(ref mut warp) = self.warp { - warp.actions().map(Into::into).collect() - } else if let Some(ref mut state) = self.state { - state.actions().map(Into::into).collect() - } else if let Some(ref mut chain_sync) = self.chain_sync { - chain_sync.actions()? - } else { - unreachable!("At least one syncing strategy is always active; qed") - }; - - if actions.iter().any(SyncingAction::is_finished) { - self.proceed_to_next()?; - } - - Ok(actions) - } -} - -impl PolkadotSyncingStrategy -where - B: BlockT, - Client: HeaderBackend - + BlockBackend - + HeaderMetadata - + ProofProvider - + Send - + Sync - + 'static, -{ - /// Initialize a new syncing strategy. - pub fn new( - mut config: SyncingConfig, - client: Arc, - warp_sync_config: Option>, - warp_sync_protocol_name: Option, - ) -> Result { - if config.max_blocks_per_request > MAX_BLOCKS_IN_RESPONSE as u32 { - info!( - target: LOG_TARGET, - "clamping maximum blocks per request to {MAX_BLOCKS_IN_RESPONSE}", - ); - config.max_blocks_per_request = MAX_BLOCKS_IN_RESPONSE as u32; - } - - if let SyncMode::Warp = config.mode { - let warp_sync_config = warp_sync_config - .expect("Warp sync configuration must be supplied in warp sync mode."); - let warp_sync = - WarpSync::new(client.clone(), warp_sync_config, warp_sync_protocol_name); - Ok(Self { - config, - client, - warp: Some(warp_sync), - state: None, - chain_sync: None, - peer_best_blocks: Default::default(), - }) - } else { - let chain_sync = ChainSync::new( - chain_sync_mode(config.mode), - client.clone(), - config.max_parallel_downloads, - config.max_blocks_per_request, - config.state_request_protocol_name.clone(), - config.metrics_registry.as_ref(), - std::iter::empty(), - )?; - Ok(Self { - config, - client, - warp: None, - state: None, - chain_sync: Some(chain_sync), - peer_best_blocks: Default::default(), - }) - } - } - - /// Proceed with the next strategy if the active one finished. - pub fn proceed_to_next(&mut self) -> Result<(), ClientError> { - // The strategies are switched as `WarpSync` -> `StateStrategy` -> `ChainSync`. - if let Some(ref mut warp) = self.warp { - match warp.take_result() { - Some(res) => { - info!( - target: LOG_TARGET, - "Warp sync is complete, continuing with state sync." - ); - let state_sync = StateStrategy::new( - self.client.clone(), - res.target_header, - res.target_body, - res.target_justifications, - false, - self.peer_best_blocks - .iter() - .map(|(peer_id, (_, best_number))| (*peer_id, *best_number)), - self.config.state_request_protocol_name.clone(), - ); - - self.warp = None; - self.state = Some(state_sync); - Ok(()) - }, - None => { - error!( - target: LOG_TARGET, - "Warp sync failed. Continuing with full sync." - ); - let chain_sync = match ChainSync::new( - chain_sync_mode(self.config.mode), - self.client.clone(), - self.config.max_parallel_downloads, - self.config.max_blocks_per_request, - self.config.state_request_protocol_name.clone(), - self.config.metrics_registry.as_ref(), - self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { - (*peer_id, *best_hash, *best_number) - }), - ) { - Ok(chain_sync) => chain_sync, - Err(e) => { - error!(target: LOG_TARGET, "Failed to start `ChainSync`."); - return Err(e) - }, - }; - - self.warp = None; - self.chain_sync = Some(chain_sync); - Ok(()) - }, - } - } else if let Some(state) = &self.state { - if state.is_succeeded() { - info!(target: LOG_TARGET, "State sync is complete, continuing with block sync."); - } else { - error!(target: LOG_TARGET, "State sync failed. Falling back to full sync."); - } - let chain_sync = match ChainSync::new( - chain_sync_mode(self.config.mode), - self.client.clone(), - self.config.max_parallel_downloads, - self.config.max_blocks_per_request, - self.config.state_request_protocol_name.clone(), - self.config.metrics_registry.as_ref(), - self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { - (*peer_id, *best_hash, *best_number) - }), - ) { - Ok(chain_sync) => chain_sync, - Err(e) => { - error!(target: LOG_TARGET, "Failed to start `ChainSync`."); - return Err(e); - }, - }; - self.state = None; - self.chain_sync = Some(chain_sync); - Ok(()) - } else { - unreachable!("Only warp & state strategies can finish; qed") + #[cfg(test)] + pub(crate) fn name(&self) -> &'static str { + match self { + Self::StartRequest { .. } => "StartRequest", + Self::CancelRequest { .. } => "CancelRequest", + Self::DropPeer(_) => "DropPeer", + Self::ImportBlocks { .. } => "ImportBlocks", + Self::ImportJustifications { .. } => "ImportJustifications", + Self::Finished => "Finished", } } } diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index 202033e8e00afc42cd3369baf6e92cd6a9716c0c..18170b77881ea4c79a2460006317e8331dab1b71 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -29,24 +29,28 @@ //! order to update it. use crate::{ + block_relay_protocol::{BlockDownloader, BlockResponseError}, blocks::BlockCollection, justification_requests::ExtraRequests, - schema::v1::StateResponse, + schema::v1::{StateRequest, StateResponse}, + service::network::NetworkServiceHandle, strategy::{ disconnected_peers::DisconnectedPeers, state_sync::{ImportResult, StateSync, StateSyncProvider}, - warp::{EncodedProof, WarpSyncPhase, WarpSyncProgress}, + warp::{WarpSyncPhase, WarpSyncProgress}, StrategyKey, SyncingAction, SyncingStrategy, }, - types::{BadPeer, OpaqueStateRequest, OpaqueStateResponse, SyncState, SyncStatus}, + types::{BadPeer, SyncState, SyncStatus}, LOG_TARGET, }; +use futures::{channel::oneshot, FutureExt}; use log::{debug, error, info, trace, warn}; use prometheus_endpoint::{register, Gauge, PrometheusError, Registry, U64}; +use prost::Message; use sc_client_api::{blockchain::BlockGap, BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; -use sc_network::ProtocolName; +use sc_network::{IfDisconnected, ProtocolName}; use sc_network_common::sync::message::{ BlockAnnounce, BlockAttributes, BlockData, BlockRequest, BlockResponse, Direction, FromBlock, }; @@ -62,6 +66,7 @@ use sp_runtime::{ }; use std::{ + any::Any, collections::{HashMap, HashSet}, ops::Range, sync::Arc, @@ -123,6 +128,9 @@ mod rep { /// Peer response data does not have requested bits. pub const BAD_RESPONSE: Rep = Rep::new(-(1 << 12), "Incomplete response"); + + /// We received a message that failed to decode. + pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message"); } struct Metrics { @@ -324,9 +332,11 @@ pub struct ChainSync { downloaded_blocks: usize, /// State sync in progress, if any. state_sync: Option>, - /// Enable importing existing blocks. This is used used after the state download to + /// Enable importing existing blocks. This is used after the state download to /// catch up to the latest state while re-importing blocks. import_existing: bool, + /// Block downloader + block_downloader: Arc>, /// Gap download process. gap_sync: Option>, /// Pending actions. @@ -348,11 +358,10 @@ where { fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor) { match self.add_peer_inner(peer_id, best_hash, best_number) { - Ok(Some(request)) => self.actions.push(SyncingAction::SendBlockRequest { - peer_id, - key: StrategyKey::ChainSync, - request, - }), + Ok(Some(request)) => { + let action = self.create_block_request_action(peer_id, request); + self.actions.push(action); + }, Ok(None) => {}, Err(bad_peer) => self.actions.push(SyncingAction::DropPeer(bad_peer)), } @@ -564,82 +573,77 @@ where self.allowed_requests.set_all(); } - fn on_block_response( + fn on_generic_response( &mut self, - peer_id: PeerId, + peer_id: &PeerId, key: StrategyKey, - request: BlockRequest, - blocks: Vec>, + protocol_name: ProtocolName, + response: Box, ) { - if key != StrategyKey::ChainSync { - error!( + if Self::STRATEGY_KEY != key { + warn!( target: LOG_TARGET, - "`on_block_response()` called with unexpected key {key:?} for chain sync", + "Unexpected generic response strategy key {key:?}, protocol {protocol_name}", ); debug_assert!(false); + return; } - let block_response = BlockResponse:: { id: request.id, blocks }; - let blocks_range = || match ( - block_response - .blocks - .first() - .and_then(|b| b.header.as_ref().map(|h| h.number())), - block_response.blocks.last().and_then(|b| b.header.as_ref().map(|h| h.number())), - ) { - (Some(first), Some(last)) if first != last => format!(" ({}..{})", first, last), - (Some(first), Some(_)) => format!(" ({})", first), - _ => Default::default(), - }; - trace!( - target: LOG_TARGET, - "BlockResponse {} from {} with {} blocks {}", - block_response.id, - peer_id, - block_response.blocks.len(), - blocks_range(), - ); + if protocol_name == self.state_request_protocol_name { + let Ok(response) = response.downcast::>() else { + warn!(target: LOG_TARGET, "Failed to downcast state response"); + debug_assert!(false); + return; + }; - let res = if request.fields == BlockAttributes::JUSTIFICATION { - self.on_block_justification(peer_id, block_response) - } else { - self.on_block_data(&peer_id, Some(request), block_response) - }; + if let Err(bad_peer) = self.on_state_data(&peer_id, &response) { + self.actions.push(SyncingAction::DropPeer(bad_peer)); + } + } else if &protocol_name == self.block_downloader.protocol_name() { + let Ok(response) = response + .downcast::<(BlockRequest, Result>, BlockResponseError>)>() + else { + warn!(target: LOG_TARGET, "Failed to downcast block response"); + debug_assert!(false); + return; + }; - if let Err(bad_peer) = res { - self.actions.push(SyncingAction::DropPeer(bad_peer)); - } - } + let (request, response) = *response; + let blocks = match response { + Ok(blocks) => blocks, + Err(BlockResponseError::DecodeFailed(e)) => { + debug!( + target: LOG_TARGET, + "Failed to decode block response from peer {:?}: {:?}.", + peer_id, + e + ); + self.actions.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_MESSAGE))); + return; + }, + Err(BlockResponseError::ExtractionFailed(e)) => { + debug!( + target: LOG_TARGET, + "Failed to extract blocks from peer response {:?}: {:?}.", + peer_id, + e + ); + self.actions.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_MESSAGE))); + return; + }, + }; - fn on_state_response( - &mut self, - peer_id: PeerId, - key: StrategyKey, - response: OpaqueStateResponse, - ) { - if key != StrategyKey::ChainSync { - error!( + if let Err(bad_peer) = self.on_block_response(peer_id, key, request, blocks) { + self.actions.push(SyncingAction::DropPeer(bad_peer)); + } + } else { + warn!( target: LOG_TARGET, - "`on_state_response()` called with unexpected key {key:?} for chain sync", + "Unexpected generic response protocol {protocol_name}, strategy key \ + {key:?}", ); debug_assert!(false); } - if let Err(bad_peer) = self.on_state_data(&peer_id, response) { - self.actions.push(SyncingAction::DropPeer(bad_peer)); - } - } - - fn on_warp_proof_response( - &mut self, - _peer_id: &PeerId, - _key: StrategyKey, - _response: EncodedProof, - ) { - error!( - target: LOG_TARGET, - "`on_warp_proof_response()` called for chain sync strategy", - ); - debug_assert!(false); } fn on_blocks_processed( @@ -863,30 +867,56 @@ where .count() } - fn actions(&mut self) -> Result>, ClientError> { + fn actions( + &mut self, + network_service: &NetworkServiceHandle, + ) -> Result>, ClientError> { if !self.peers.is_empty() && self.queue_blocks.is_empty() { if let Some((hash, number, skip_proofs)) = self.pending_state_sync_attempt.take() { self.attempt_state_sync(hash, number, skip_proofs); } } - let block_requests = self.block_requests().into_iter().map(|(peer_id, request)| { - SyncingAction::SendBlockRequest { peer_id, key: StrategyKey::ChainSync, request } - }); + let block_requests = self + .block_requests() + .into_iter() + .map(|(peer_id, request)| self.create_block_request_action(peer_id, request)) + .collect::>(); self.actions.extend(block_requests); - let justification_requests = - self.justification_requests().into_iter().map(|(peer_id, request)| { - SyncingAction::SendBlockRequest { peer_id, key: StrategyKey::ChainSync, request } - }); + let justification_requests = self + .justification_requests() + .into_iter() + .map(|(peer_id, request)| self.create_block_request_action(peer_id, request)) + .collect::>(); self.actions.extend(justification_requests); let state_request = self.state_request().into_iter().map(|(peer_id, request)| { - SyncingAction::SendStateRequest { + trace!( + target: LOG_TARGET, + "Created `StrategyRequest` to {peer_id}.", + ); + + let (tx, rx) = oneshot::channel(); + + network_service.start_request( peer_id, - key: StrategyKey::ChainSync, - protocol_name: self.state_request_protocol_name.clone(), - request, + self.state_request_protocol_name.clone(), + request.encode_to_vec(), + tx, + IfDisconnected::ImmediateError, + ); + + SyncingAction::StartRequest { + peer_id, + key: Self::STRATEGY_KEY, + request: async move { + Ok(rx.await?.and_then(|(response, protocol_name)| { + Ok((Box::new(response) as Box, protocol_name)) + })) + } + .boxed(), + remove_obsolete: false, } }); self.actions.extend(state_request); @@ -906,6 +936,9 @@ where + Sync + 'static, { + /// Strategy key used by chain sync. + pub const STRATEGY_KEY: StrategyKey = StrategyKey::new("ChainSync"); + /// Create a new instance. pub fn new( mode: ChainSyncMode, @@ -913,6 +946,7 @@ where max_parallel_downloads: u32, max_blocks_per_request: u32, state_request_protocol_name: ProtocolName, + block_downloader: Arc>, metrics_registry: Option<&Registry>, initial_peers: impl Iterator)>, ) -> Result { @@ -935,6 +969,7 @@ where downloaded_blocks: 0, state_sync: None, import_existing: false, + block_downloader, gap_sync: None, actions: Vec::new(), metrics: metrics_registry.and_then(|r| match Metrics::register(r) { @@ -1075,6 +1110,33 @@ where } } + fn create_block_request_action( + &mut self, + peer_id: PeerId, + request: BlockRequest, + ) -> SyncingAction { + let downloader = self.block_downloader.clone(); + + SyncingAction::StartRequest { + peer_id, + key: Self::STRATEGY_KEY, + request: async move { + Ok(downloader.download_blocks(peer_id, request.clone()).await?.and_then( + |(response, protocol_name)| { + let decoded_response = + downloader.block_response_into_blocks(&request, response); + let result = Box::new((request, decoded_response)) as Box; + Ok((result, protocol_name)) + }, + )) + } + .boxed(), + // Sending block request implies dropping obsolete pending response as we are not + // interested in it anymore. + remove_obsolete: true, + } + } + /// Submit a block response for processing. #[must_use] fn on_block_data( @@ -1248,11 +1310,8 @@ where state: next_state, }; let request = ancestry_request::(next_num); - self.actions.push(SyncingAction::SendBlockRequest { - peer_id: *peer_id, - key: StrategyKey::ChainSync, - request, - }); + let action = self.create_block_request_action(*peer_id, request); + self.actions.push(action); return Ok(()); } else { // Ancestry search is complete. Check if peer is on a stale fork unknown @@ -1334,6 +1393,49 @@ where Ok(()) } + fn on_block_response( + &mut self, + peer_id: &PeerId, + key: StrategyKey, + request: BlockRequest, + blocks: Vec>, + ) -> Result<(), BadPeer> { + if key != Self::STRATEGY_KEY { + error!( + target: LOG_TARGET, + "`on_block_response()` called with unexpected key {key:?} for chain sync", + ); + debug_assert!(false); + } + let block_response = BlockResponse:: { id: request.id, blocks }; + + let blocks_range = || match ( + block_response + .blocks + .first() + .and_then(|b| b.header.as_ref().map(|h| h.number())), + block_response.blocks.last().and_then(|b| b.header.as_ref().map(|h| h.number())), + ) { + (Some(first), Some(last)) if first != last => format!(" ({}..{})", first, last), + (Some(first), Some(_)) => format!(" ({})", first), + _ => Default::default(), + }; + trace!( + target: LOG_TARGET, + "BlockResponse {} from {} with {} blocks {}", + block_response.id, + peer_id, + block_response.blocks.len(), + blocks_range(), + ); + + if request.fields == BlockAttributes::JUSTIFICATION { + self.on_block_justification(*peer_id, block_response) + } else { + self.on_block_data(peer_id, Some(request), block_response) + } + } + /// Submit a justification response for processing. #[must_use] fn on_block_justification( @@ -1548,10 +1650,8 @@ where PeerSyncState::DownloadingGap(_) | PeerSyncState::DownloadingState => { // Cancel a request first, as `add_peer` may generate a new request. - self.actions.push(SyncingAction::CancelRequest { - peer_id, - key: StrategyKey::ChainSync, - }); + self.actions + .push(SyncingAction::CancelRequest { peer_id, key: Self::STRATEGY_KEY }); self.add_peer(peer_id, peer_sync.best_hash, peer_sync.best_number); }, PeerSyncState::DownloadingJustification(_) => { @@ -1831,7 +1931,7 @@ where } /// Get a state request scheduled by sync to be sent out (if any). - fn state_request(&mut self) -> Option<(PeerId, OpaqueStateRequest)> { + fn state_request(&mut self) -> Option<(PeerId, StateRequest)> { if self.allowed_requests.is_empty() { return None; } @@ -1855,7 +1955,7 @@ where let request = sync.next_request(); trace!(target: LOG_TARGET, "New StateRequest for {}: {:?}", id, request); self.allowed_requests.clear(); - return Some((*id, OpaqueStateRequest(Box::new(request)))); + return Some((*id, request)); } } } @@ -1863,19 +1963,18 @@ where } #[must_use] - fn on_state_data( - &mut self, - peer_id: &PeerId, - response: OpaqueStateResponse, - ) -> Result<(), BadPeer> { - let response: Box = response.0.downcast().map_err(|_error| { - error!( - target: LOG_TARGET, - "Failed to downcast opaque state response, this is an implementation bug." - ); + fn on_state_data(&mut self, peer_id: &PeerId, response: &[u8]) -> Result<(), BadPeer> { + let response = match StateResponse::decode(response) { + Ok(response) => response, + Err(error) => { + debug!( + target: LOG_TARGET, + "Failed to decode state response from peer {peer_id:?}: {error:?}.", + ); - BadPeer(*peer_id, rep::BAD_RESPONSE) - })?; + return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)); + }, + }; if let Some(peer) = self.peers.get_mut(peer_id) { if let PeerSyncState::DownloadingState = peer.state { @@ -1891,7 +1990,7 @@ where response.entries.len(), response.proof.len(), ); - sync.import(*response) + sync.import(response) } else { debug!(target: LOG_TARGET, "Ignored obsolete state response from {peer_id}"); return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)); diff --git a/substrate/client/network/sync/src/strategy/chain_sync/test.rs b/substrate/client/network/sync/src/strategy/chain_sync/test.rs index d13f034e2e8da25df6171c0e21c607d4af722bc2..4a5682722389a9b8e4a6fbbb7c5060d140c057e7 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync/test.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync/test.rs @@ -19,16 +19,64 @@ //! Tests of [`ChainSync`]. use super::*; -use futures::executor::block_on; +use crate::{ + block_relay_protocol::BlockResponseError, mock::MockBlockDownloader, + service::network::NetworkServiceProvider, +}; +use futures::{channel::oneshot::Canceled, executor::block_on}; use sc_block_builder::BlockBuilderBuilder; +use sc_network::RequestFailure; use sc_network_common::sync::message::{BlockAnnounce, BlockData, BlockState, FromBlock}; use sp_blockchain::HeaderBackend; +use std::sync::Mutex; use substrate_test_runtime_client::{ runtime::{Block, Hash, Header}, BlockBuilderExt, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt, TestClient, TestClientBuilder, TestClientBuilderExt, }; +#[derive(Debug)] +struct ProxyBlockDownloader { + protocol_name: ProtocolName, + sender: std::sync::mpsc::Sender>, + request: Mutex>>, +} + +#[async_trait::async_trait] +impl BlockDownloader for ProxyBlockDownloader { + fn protocol_name(&self) -> &ProtocolName { + &self.protocol_name + } + + async fn download_blocks( + &self, + _who: PeerId, + request: BlockRequest, + ) -> Result, ProtocolName), RequestFailure>, Canceled> { + self.sender.send(request).unwrap(); + Ok(Ok((Vec::new(), self.protocol_name.clone()))) + } + + fn block_response_into_blocks( + &self, + _request: &BlockRequest, + _response: Vec, + ) -> Result>, BlockResponseError> { + Ok(Vec::new()) + } +} + +impl ProxyBlockDownloader { + fn new(protocol_name: ProtocolName) -> Self { + let (sender, receiver) = std::sync::mpsc::channel(); + Self { protocol_name, sender, request: Mutex::new(receiver) } + } + + fn next_request(&self) -> BlockRequest { + self.request.lock().unwrap().recv().unwrap() + } +} + #[test] fn processes_empty_response_on_justification_request_for_unknown_block() { // if we ask for a justification for a given block to a peer that doesn't know that block @@ -44,6 +92,7 @@ fn processes_empty_response_on_justification_request_for_unknown_block() { 1, 64, ProtocolName::Static(""), + Arc::new(MockBlockDownloader::new()), None, std::iter::empty(), ) @@ -108,6 +157,7 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { 1, 8, ProtocolName::Static(""), + Arc::new(MockBlockDownloader::new()), None, std::iter::empty(), ) @@ -140,13 +190,15 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { sync.add_peer(peer_id1, Hash::random(), 42); sync.add_peer(peer_id2, Hash::random(), 10); + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); + // we wil send block requests to these peers // for these blocks we don't know about - let actions = sync.actions().unwrap(); + let actions = sync.actions(&network_handle).unwrap(); assert_eq!(actions.len(), 2); assert!(actions.iter().all(|action| match action { - SyncingAction::SendBlockRequest { peer_id, .. } => - peer_id == &peer_id1 || peer_id == &peer_id2, + SyncingAction::StartRequest { peer_id, .. } => peer_id == &peer_id1 || peer_id == &peer_id2, _ => false, })); @@ -176,7 +228,7 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { sync.restart(); // which should make us cancel and send out again block requests to the first two peers - let actions = sync.actions().unwrap(); + let actions = sync.actions(&network_handle).unwrap(); assert_eq!(actions.len(), 4); let mut cancelled_first = HashSet::new(); assert!(actions.iter().all(|action| match action { @@ -184,7 +236,7 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { cancelled_first.insert(peer_id); peer_id == &peer_id1 || peer_id == &peer_id2 }, - SyncingAction::SendBlockRequest { peer_id, .. } => { + SyncingAction::StartRequest { peer_id, .. } => { assert!(cancelled_first.remove(peer_id)); peer_id == &peer_id1 || peer_id == &peer_id2 }, @@ -311,6 +363,7 @@ fn do_ancestor_search_when_common_block_to_best_queued_gap_is_to_big() { 5, 64, ProtocolName::Static(""), + Arc::new(MockBlockDownloader::new()), None, std::iter::empty(), ) @@ -459,12 +512,16 @@ fn can_sync_huge_fork() { let info = client.info(); + let protocol_name = ProtocolName::Static(""); + let proxy_block_downloader = Arc::new(ProxyBlockDownloader::new(protocol_name.clone())); + let mut sync = ChainSync::new( ChainSyncMode::Full, client.clone(), 5, 64, - ProtocolName::Static(""), + protocol_name, + proxy_block_downloader.clone(), None, std::iter::empty(), ) @@ -494,18 +551,21 @@ fn can_sync_huge_fork() { let block = &fork_blocks[unwrap_from_block_number(request.from.clone()) as usize - 1]; let response = create_block_response(vec![block.clone()]); - sync.on_block_data(&peer_id1, Some(request), response).unwrap(); + sync.on_block_data(&peer_id1, Some(request.clone()), response).unwrap(); - let actions = sync.take_actions().collect::>(); + let mut actions = sync.take_actions().collect::>(); request = if actions.is_empty() { // We found the ancestor break } else { assert_eq!(actions.len(), 1); - match &actions[0] { - SyncingAction::SendBlockRequest { peer_id: _, request, key: _ } => request.clone(), - action @ _ => panic!("Unexpected action: {action:?}"), + match actions.pop().unwrap() { + SyncingAction::StartRequest { request, .. } => { + block_on(request).unwrap().unwrap(); + proxy_block_downloader.next_request() + }, + action => panic!("Unexpected action: {}", action.name()), } }; @@ -600,12 +660,16 @@ fn syncs_fork_without_duplicate_requests() { let info = client.info(); + let protocol_name = ProtocolName::Static(""); + let proxy_block_downloader = Arc::new(ProxyBlockDownloader::new(protocol_name.clone())); + let mut sync = ChainSync::new( ChainSyncMode::Full, client.clone(), 5, 64, - ProtocolName::Static(""), + protocol_name, + proxy_block_downloader.clone(), None, std::iter::empty(), ) @@ -637,16 +701,19 @@ fn syncs_fork_without_duplicate_requests() { sync.on_block_data(&peer_id1, Some(request), response).unwrap(); - let actions = sync.take_actions().collect::>(); + let mut actions = sync.take_actions().collect::>(); request = if actions.is_empty() { // We found the ancestor break } else { assert_eq!(actions.len(), 1); - match &actions[0] { - SyncingAction::SendBlockRequest { peer_id: _, request, key: _ } => request.clone(), - action @ _ => panic!("Unexpected action: {action:?}"), + match actions.pop().unwrap() { + SyncingAction::StartRequest { request, .. } => { + block_on(request).unwrap().unwrap(); + proxy_block_downloader.next_request() + }, + action => panic!("Unexpected action: {}", action.name()), } }; @@ -750,6 +817,7 @@ fn removes_target_fork_on_disconnect() { 1, 64, ProtocolName::Static(""), + Arc::new(MockBlockDownloader::new()), None, std::iter::empty(), ) @@ -784,6 +852,7 @@ fn can_import_response_with_missing_blocks() { 1, 64, ProtocolName::Static(""), + Arc::new(MockBlockDownloader::new()), None, std::iter::empty(), ) @@ -824,6 +893,7 @@ fn sync_restart_removes_block_but_not_justification_requests() { 1, 64, ProtocolName::Static(""), + Arc::new(MockBlockDownloader::new()), None, std::iter::empty(), ) @@ -898,17 +968,17 @@ fn sync_restart_removes_block_but_not_justification_requests() { SyncingAction::CancelRequest { peer_id, key: _ } => { pending_responses.remove(&peer_id); }, - SyncingAction::SendBlockRequest { peer_id, .. } => { + SyncingAction::StartRequest { peer_id, .. } => { // we drop obsolete response, but don't register a new request, it's checked in // the `assert!` below pending_responses.remove(&peer_id); }, - action @ _ => panic!("Unexpected action: {action:?}"), + action @ _ => panic!("Unexpected action: {}", action.name()), } } assert!(actions.iter().any(|action| { match action { - SyncingAction::SendBlockRequest { peer_id, .. } => peer_id == &peers[0], + SyncingAction::StartRequest { peer_id, .. } => peer_id == &peers[0], _ => false, } })); @@ -975,6 +1045,7 @@ fn request_across_forks() { 5, 64, ProtocolName::Static(""), + Arc::new(MockBlockDownloader::new()), None, std::iter::empty(), ) diff --git a/substrate/client/network/sync/src/strategy/polkadot.rs b/substrate/client/network/sync/src/strategy/polkadot.rs new file mode 100644 index 0000000000000000000000000000000000000000..44b05966af064c48066c04d17a78a9e05ac3c208 --- /dev/null +++ b/substrate/client/network/sync/src/strategy/polkadot.rs @@ -0,0 +1,481 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! [`PolkadotSyncingStrategy`] is a proxy between [`crate::engine::SyncingEngine`] +//! and specific syncing algorithms. + +use crate::{ + block_relay_protocol::BlockDownloader, + block_request_handler::MAX_BLOCKS_IN_RESPONSE, + service::network::NetworkServiceHandle, + strategy::{ + chain_sync::{ChainSync, ChainSyncMode}, + state::StateStrategy, + warp::{WarpSync, WarpSyncConfig}, + StrategyKey, SyncingAction, SyncingStrategy, + }, + types::SyncStatus, + LOG_TARGET, +}; +use log::{debug, error, info, warn}; +use prometheus_endpoint::Registry; +use sc_client_api::{BlockBackend, ProofProvider}; +use sc_consensus::{BlockImportError, BlockImportStatus}; +use sc_network::ProtocolName; +use sc_network_common::sync::{message::BlockAnnounce, SyncMode}; +use sc_network_types::PeerId; +use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata}; +use sp_runtime::traits::{Block as BlockT, Header, NumberFor}; +use std::{any::Any, collections::HashMap, sync::Arc}; + +/// Corresponding `ChainSync` mode. +fn chain_sync_mode(sync_mode: SyncMode) -> ChainSyncMode { + match sync_mode { + SyncMode::Full => ChainSyncMode::Full, + SyncMode::LightState { skip_proofs, storage_chain_mode } => + ChainSyncMode::LightState { skip_proofs, storage_chain_mode }, + SyncMode::Warp => ChainSyncMode::Full, + } +} + +/// Syncing configuration containing data for [`PolkadotSyncingStrategy`]. +#[derive(Clone, Debug)] +pub struct PolkadotSyncingStrategyConfig +where + Block: BlockT, +{ + /// Syncing mode. + pub mode: SyncMode, + /// The number of parallel downloads to guard against slow peers. + pub max_parallel_downloads: u32, + /// Maximum number of blocks to request. + pub max_blocks_per_request: u32, + /// Prometheus metrics registry. + pub metrics_registry: Option, + /// Protocol name used to send out state requests + pub state_request_protocol_name: ProtocolName, + /// Block downloader + pub block_downloader: Arc>, +} + +/// Proxy to specific syncing strategies used in Polkadot. +pub struct PolkadotSyncingStrategy { + /// Initial syncing configuration. + config: PolkadotSyncingStrategyConfig, + /// Client used by syncing strategies. + client: Arc, + /// Warp strategy. + warp: Option>, + /// State strategy. + state: Option>, + /// `ChainSync` strategy.` + chain_sync: Option>, + /// Connected peers and their best blocks used to seed a new strategy when switching to it in + /// `PolkadotSyncingStrategy::proceed_to_next`. + peer_best_blocks: HashMap)>, +} + +impl SyncingStrategy for PolkadotSyncingStrategy +where + B: BlockT, + Client: HeaderBackend + + BlockBackend + + HeaderMetadata + + ProofProvider + + Send + + Sync + + 'static, +{ + fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor) { + self.peer_best_blocks.insert(peer_id, (best_hash, best_number)); + + self.warp.as_mut().map(|s| s.add_peer(peer_id, best_hash, best_number)); + self.state.as_mut().map(|s| s.add_peer(peer_id, best_hash, best_number)); + self.chain_sync.as_mut().map(|s| s.add_peer(peer_id, best_hash, best_number)); + } + + fn remove_peer(&mut self, peer_id: &PeerId) { + self.warp.as_mut().map(|s| s.remove_peer(peer_id)); + self.state.as_mut().map(|s| s.remove_peer(peer_id)); + self.chain_sync.as_mut().map(|s| s.remove_peer(peer_id)); + + self.peer_best_blocks.remove(peer_id); + } + + fn on_validated_block_announce( + &mut self, + is_best: bool, + peer_id: PeerId, + announce: &BlockAnnounce, + ) -> Option<(B::Hash, NumberFor)> { + let new_best = if let Some(ref mut warp) = self.warp { + warp.on_validated_block_announce(is_best, peer_id, announce) + } else if let Some(ref mut state) = self.state { + state.on_validated_block_announce(is_best, peer_id, announce) + } else if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.on_validated_block_announce(is_best, peer_id, announce) + } else { + error!(target: LOG_TARGET, "No syncing strategy is active."); + debug_assert!(false); + Some((announce.header.hash(), *announce.header.number())) + }; + + if let Some(new_best) = new_best { + if let Some(best) = self.peer_best_blocks.get_mut(&peer_id) { + *best = new_best; + } else { + debug!( + target: LOG_TARGET, + "Cannot update `peer_best_blocks` as peer {peer_id} is not known to `Strategy` \ + (already disconnected?)", + ); + } + } + + new_best + } + + fn set_sync_fork_request(&mut self, peers: Vec, hash: &B::Hash, number: NumberFor) { + // Fork requests are only handled by `ChainSync`. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.set_sync_fork_request(peers.clone(), hash, number); + } + } + + fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { + // Justifications can only be requested via `ChainSync`. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.request_justification(hash, number); + } + } + + fn clear_justification_requests(&mut self) { + // Justification requests can only be cleared by `ChainSync`. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.clear_justification_requests(); + } + } + + fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool) { + // Only `ChainSync` is interested in justification import. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.on_justification_import(hash, number, success); + } + } + + fn on_generic_response( + &mut self, + peer_id: &PeerId, + key: StrategyKey, + protocol_name: ProtocolName, + response: Box, + ) { + match key { + StateStrategy::::STRATEGY_KEY => + if let Some(state) = &mut self.state { + let Ok(response) = response.downcast::>() else { + warn!(target: LOG_TARGET, "Failed to downcast state response"); + debug_assert!(false); + return; + }; + + state.on_state_response(peer_id, *response); + } else if let Some(chain_sync) = &mut self.chain_sync { + chain_sync.on_generic_response(peer_id, key, protocol_name, response); + } else { + error!( + target: LOG_TARGET, + "`on_generic_response()` called with unexpected key {key:?} \ + or corresponding strategy is not active.", + ); + debug_assert!(false); + }, + WarpSync::::STRATEGY_KEY => + if let Some(warp) = &mut self.warp { + warp.on_generic_response(peer_id, protocol_name, response); + } else { + error!( + target: LOG_TARGET, + "`on_generic_response()` called with unexpected key {key:?} \ + or warp strategy is not active", + ); + debug_assert!(false); + }, + ChainSync::::STRATEGY_KEY => + if let Some(chain_sync) = &mut self.chain_sync { + chain_sync.on_generic_response(peer_id, key, protocol_name, response); + } else { + error!( + target: LOG_TARGET, + "`on_generic_response()` called with unexpected key {key:?} \ + or corresponding strategy is not active.", + ); + debug_assert!(false); + }, + key => { + warn!( + target: LOG_TARGET, + "Unexpected generic response strategy key {key:?}, protocol {protocol_name}", + ); + debug_assert!(false); + }, + } + } + + fn on_blocks_processed( + &mut self, + imported: usize, + count: usize, + results: Vec<(Result>, BlockImportError>, B::Hash)>, + ) { + // Only `StateStrategy` and `ChainSync` are interested in block processing notifications. + if let Some(ref mut state) = self.state { + state.on_blocks_processed(imported, count, results); + } else if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.on_blocks_processed(imported, count, results); + } + } + + fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor) { + // Only `ChainSync` is interested in block finalization notifications. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.on_block_finalized(hash, number); + } + } + + fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor) { + // This is relevant to `ChainSync` only. + if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.update_chain_info(best_hash, best_number); + } + } + + fn is_major_syncing(&self) -> bool { + self.warp.is_some() || + self.state.is_some() || + match self.chain_sync { + Some(ref s) => s.status().state.is_major_syncing(), + None => unreachable!("At least one syncing strategy is active; qed"), + } + } + + fn num_peers(&self) -> usize { + self.peer_best_blocks.len() + } + + fn status(&self) -> SyncStatus { + // This function presumes that strategies are executed serially and must be refactored + // once we have parallel strategies. + if let Some(ref warp) = self.warp { + warp.status() + } else if let Some(ref state) = self.state { + state.status() + } else if let Some(ref chain_sync) = self.chain_sync { + chain_sync.status() + } else { + unreachable!("At least one syncing strategy is always active; qed") + } + } + + fn num_downloaded_blocks(&self) -> usize { + self.chain_sync + .as_ref() + .map_or(0, |chain_sync| chain_sync.num_downloaded_blocks()) + } + + fn num_sync_requests(&self) -> usize { + self.chain_sync.as_ref().map_or(0, |chain_sync| chain_sync.num_sync_requests()) + } + + fn actions( + &mut self, + network_service: &NetworkServiceHandle, + ) -> Result>, ClientError> { + // This function presumes that strategies are executed serially and must be refactored once + // we have parallel strategies. + let actions: Vec<_> = if let Some(ref mut warp) = self.warp { + warp.actions(network_service).map(Into::into).collect() + } else if let Some(ref mut state) = self.state { + state.actions(network_service).map(Into::into).collect() + } else if let Some(ref mut chain_sync) = self.chain_sync { + chain_sync.actions(network_service)? + } else { + unreachable!("At least one syncing strategy is always active; qed") + }; + + if actions.iter().any(SyncingAction::is_finished) { + self.proceed_to_next()?; + } + + Ok(actions) + } +} + +impl PolkadotSyncingStrategy +where + B: BlockT, + Client: HeaderBackend + + BlockBackend + + HeaderMetadata + + ProofProvider + + Send + + Sync + + 'static, +{ + /// Initialize a new syncing strategy. + pub fn new( + mut config: PolkadotSyncingStrategyConfig, + client: Arc, + warp_sync_config: Option>, + warp_sync_protocol_name: Option, + ) -> Result { + if config.max_blocks_per_request > MAX_BLOCKS_IN_RESPONSE as u32 { + info!( + target: LOG_TARGET, + "clamping maximum blocks per request to {MAX_BLOCKS_IN_RESPONSE}", + ); + config.max_blocks_per_request = MAX_BLOCKS_IN_RESPONSE as u32; + } + + if let SyncMode::Warp = config.mode { + let warp_sync_config = warp_sync_config + .expect("Warp sync configuration must be supplied in warp sync mode."); + let warp_sync = WarpSync::new( + client.clone(), + warp_sync_config, + warp_sync_protocol_name, + config.block_downloader.clone(), + ); + Ok(Self { + config, + client, + warp: Some(warp_sync), + state: None, + chain_sync: None, + peer_best_blocks: Default::default(), + }) + } else { + let chain_sync = ChainSync::new( + chain_sync_mode(config.mode), + client.clone(), + config.max_parallel_downloads, + config.max_blocks_per_request, + config.state_request_protocol_name.clone(), + config.block_downloader.clone(), + config.metrics_registry.as_ref(), + std::iter::empty(), + )?; + Ok(Self { + config, + client, + warp: None, + state: None, + chain_sync: Some(chain_sync), + peer_best_blocks: Default::default(), + }) + } + } + + /// Proceed with the next strategy if the active one finished. + pub fn proceed_to_next(&mut self) -> Result<(), ClientError> { + // The strategies are switched as `WarpSync` -> `StateStrategy` -> `ChainSync`. + if let Some(ref mut warp) = self.warp { + match warp.take_result() { + Some(res) => { + info!( + target: LOG_TARGET, + "Warp sync is complete, continuing with state sync." + ); + let state_sync = StateStrategy::new( + self.client.clone(), + res.target_header, + res.target_body, + res.target_justifications, + false, + self.peer_best_blocks + .iter() + .map(|(peer_id, (_, best_number))| (*peer_id, *best_number)), + self.config.state_request_protocol_name.clone(), + ); + + self.warp = None; + self.state = Some(state_sync); + Ok(()) + }, + None => { + error!( + target: LOG_TARGET, + "Warp sync failed. Continuing with full sync." + ); + let chain_sync = match ChainSync::new( + chain_sync_mode(self.config.mode), + self.client.clone(), + self.config.max_parallel_downloads, + self.config.max_blocks_per_request, + self.config.state_request_protocol_name.clone(), + self.config.block_downloader.clone(), + self.config.metrics_registry.as_ref(), + self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { + (*peer_id, *best_hash, *best_number) + }), + ) { + Ok(chain_sync) => chain_sync, + Err(e) => { + error!(target: LOG_TARGET, "Failed to start `ChainSync`."); + return Err(e) + }, + }; + + self.warp = None; + self.chain_sync = Some(chain_sync); + Ok(()) + }, + } + } else if let Some(state) = &self.state { + if state.is_succeeded() { + info!(target: LOG_TARGET, "State sync is complete, continuing with block sync."); + } else { + error!(target: LOG_TARGET, "State sync failed. Falling back to full sync."); + } + let chain_sync = match ChainSync::new( + chain_sync_mode(self.config.mode), + self.client.clone(), + self.config.max_parallel_downloads, + self.config.max_blocks_per_request, + self.config.state_request_protocol_name.clone(), + self.config.block_downloader.clone(), + self.config.metrics_registry.as_ref(), + self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { + (*peer_id, *best_hash, *best_number) + }), + ) { + Ok(chain_sync) => chain_sync, + Err(e) => { + error!(target: LOG_TARGET, "Failed to start `ChainSync`."); + return Err(e); + }, + }; + + self.state = None; + self.chain_sync = Some(chain_sync); + Ok(()) + } else { + unreachable!("Only warp & state strategies can finish; qed") + } + } +} diff --git a/substrate/client/network/sync/src/strategy/state.rs b/substrate/client/network/sync/src/strategy/state.rs index d69ab3e2d535e9605d227316b0faa02b87952d46..1abbb96ccd907ef1590d25ccc379897e0948ff39 100644 --- a/substrate/client/network/sync/src/strategy/state.rs +++ b/substrate/client/network/sync/src/strategy/state.rs @@ -19,18 +19,22 @@ //! State sync strategy. use crate::{ - schema::v1::StateResponse, + schema::v1::{StateRequest, StateResponse}, + service::network::NetworkServiceHandle, strategy::{ disconnected_peers::DisconnectedPeers, state_sync::{ImportResult, StateSync, StateSyncProvider}, + StrategyKey, SyncingAction, }, - types::{BadPeer, OpaqueStateRequest, OpaqueStateResponse, SyncState, SyncStatus}, + types::{BadPeer, SyncState, SyncStatus}, LOG_TARGET, }; +use futures::{channel::oneshot, FutureExt}; use log::{debug, error, trace}; +use prost::Message; use sc_client_api::ProofProvider; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; -use sc_network::ProtocolName; +use sc_network::{IfDisconnected, ProtocolName}; use sc_network_common::sync::message::BlockAnnounce; use sc_network_types::PeerId; use sp_consensus::BlockOrigin; @@ -38,7 +42,7 @@ use sp_runtime::{ traits::{Block as BlockT, Header, NumberFor}, Justifications, SaturatedConversion, }; -use std::{collections::HashMap, sync::Arc}; +use std::{any::Any, collections::HashMap, sync::Arc}; mod rep { use sc_network::ReputationChange as Rep; @@ -50,18 +54,6 @@ mod rep { pub const BAD_STATE: Rep = Rep::new(-(1 << 29), "Bad state"); } -/// Action that should be performed on [`StateStrategy`]'s behalf. -pub enum StateStrategyAction { - /// Send state request to peer. - SendStateRequest { peer_id: PeerId, protocol_name: ProtocolName, request: OpaqueStateRequest }, - /// Disconnect and report peer. - DropPeer(BadPeer), - /// Import blocks. - ImportBlocks { origin: BlockOrigin, blocks: Vec> }, - /// State sync has finished. - Finished, -} - enum PeerState { Available, DownloadingState, @@ -83,12 +75,15 @@ pub struct StateStrategy { state_sync: Box>, peers: HashMap>, disconnected_peers: DisconnectedPeers, - actions: Vec>, + actions: Vec>, protocol_name: ProtocolName, succeeded: bool, } impl StateStrategy { + /// Strategy key used by state sync. + pub const STRATEGY_KEY: StrategyKey = StrategyKey::new("State"); + /// Create a new instance. pub fn new( client: Arc, @@ -123,10 +118,11 @@ impl StateStrategy { } } - // Create a new instance with a custom state sync provider. - // Used in tests. - #[cfg(test)] - fn new_with_provider( + /// Create a new instance with a custom state sync provider. + /// + /// Note: In most cases, users should use [`StateStrategy::new`]. + /// This method is intended for custom sync strategies and advanced use cases. + pub fn new_with_provider( state_sync_provider: Box>, initial_peers: impl Iterator)>, protocol_name: ProtocolName, @@ -157,7 +153,7 @@ impl StateStrategy { if let Some(bad_peer) = self.disconnected_peers.on_disconnect_during_request(*peer_id) { - self.actions.push(StateStrategyAction::DropPeer(bad_peer)); + self.actions.push(SyncingAction::DropPeer(bad_peer)); } } } @@ -185,30 +181,32 @@ impl StateStrategy { } /// Process state response. - pub fn on_state_response(&mut self, peer_id: PeerId, response: OpaqueStateResponse) { - if let Err(bad_peer) = self.on_state_response_inner(peer_id, response) { - self.actions.push(StateStrategyAction::DropPeer(bad_peer)); + pub fn on_state_response(&mut self, peer_id: &PeerId, response: Vec) { + if let Err(bad_peer) = self.on_state_response_inner(peer_id, &response) { + self.actions.push(SyncingAction::DropPeer(bad_peer)); } } fn on_state_response_inner( &mut self, - peer_id: PeerId, - response: OpaqueStateResponse, + peer_id: &PeerId, + response: &[u8], ) -> Result<(), BadPeer> { if let Some(peer) = self.peers.get_mut(&peer_id) { peer.state = PeerState::Available; } - let response: Box = response.0.downcast().map_err(|_error| { - error!( - target: LOG_TARGET, - "Failed to downcast opaque state response, this is an implementation bug." - ); - debug_assert!(false); + let response = match StateResponse::decode(response) { + Ok(response) => response, + Err(error) => { + debug!( + target: LOG_TARGET, + "Failed to decode state response from peer {peer_id:?}: {error:?}.", + ); - BadPeer(peer_id, rep::BAD_RESPONSE) - })?; + return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)); + }, + }; debug!( target: LOG_TARGET, @@ -218,7 +216,7 @@ impl StateStrategy { response.proof.len(), ); - match self.state_sync.import(*response) { + match self.state_sync.import(response) { ImportResult::Import(hash, header, state, body, justifications) => { let origin = BlockOrigin::NetworkInitialSync; let block = IncomingBlock { @@ -234,14 +232,13 @@ impl StateStrategy { state: Some(state), }; debug!(target: LOG_TARGET, "State download is complete. Import is queued"); - self.actions - .push(StateStrategyAction::ImportBlocks { origin, blocks: vec![block] }); + self.actions.push(SyncingAction::ImportBlocks { origin, blocks: vec![block] }); Ok(()) }, ImportResult::Continue => Ok(()), ImportResult::BadResponse => { debug!(target: LOG_TARGET, "Bad state data received from {peer_id}"); - Err(BadPeer(peer_id, rep::BAD_STATE)) + Err(BadPeer(*peer_id, rep::BAD_STATE)) }, } } @@ -281,12 +278,12 @@ impl StateStrategy { ); }); self.succeeded |= results.into_iter().any(|result| result.is_ok()); - self.actions.push(StateStrategyAction::Finished); + self.actions.push(SyncingAction::Finished); } } /// Produce state request. - fn state_request(&mut self) -> Option<(PeerId, OpaqueStateRequest)> { + fn state_request(&mut self) -> Option<(PeerId, StateRequest)> { if self.state_sync.is_complete() { return None } @@ -307,7 +304,7 @@ impl StateStrategy { target: LOG_TARGET, "New state request to {peer_id}: {request:?}.", ); - Some((peer_id, OpaqueStateRequest(Box::new(request)))) + Some((peer_id, request)) } fn schedule_next_peer( @@ -352,14 +349,33 @@ impl StateStrategy { } } - /// Get actions that should be performed by the owner on [`WarpSync`]'s behalf + /// Get actions that should be performed. #[must_use] - pub fn actions(&mut self) -> impl Iterator> { + pub fn actions( + &mut self, + network_service: &NetworkServiceHandle, + ) -> impl Iterator> { let state_request = self.state_request().into_iter().map(|(peer_id, request)| { - StateStrategyAction::SendStateRequest { + let (tx, rx) = oneshot::channel(); + + network_service.start_request( + peer_id, + self.protocol_name.clone(), + request.encode_to_vec(), + tx, + IfDisconnected::ImmediateError, + ); + + SyncingAction::StartRequest { peer_id, - protocol_name: self.protocol_name.clone(), - request, + key: Self::STRATEGY_KEY, + request: async move { + Ok(rx.await?.and_then(|(response, protocol_name)| { + Ok((Box::new(response) as Box, protocol_name)) + })) + } + .boxed(), + remove_obsolete: false, } }); self.actions.extend(state_request); @@ -379,6 +395,7 @@ mod test { use super::*; use crate::{ schema::v1::{StateRequest, StateResponse}, + service::network::NetworkServiceProvider, strategy::state_sync::{ImportResult, StateSyncProgress, StateSyncProvider}, }; use codec::Decode; @@ -579,8 +596,7 @@ mod test { ProtocolName::Static(""), ); - let (_peer_id, mut opaque_request) = state_strategy.state_request().unwrap(); - let request: &mut StateRequest = opaque_request.0.downcast_mut().unwrap(); + let (_peer_id, request) = state_strategy.state_request().unwrap(); let hash = Hash::decode(&mut &*request.block).unwrap(); assert_eq!(hash, target_block.header().hash()); @@ -631,8 +647,8 @@ mod test { // Manually set the peer's state. state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState; - let dummy_response = OpaqueStateResponse(Box::new(StateResponse::default())); - state_strategy.on_state_response(peer_id, dummy_response); + let dummy_response = StateResponse::default().encode_to_vec(); + state_strategy.on_state_response(&peer_id, dummy_response); assert!(state_strategy.peers.get(&peer_id).unwrap().state.is_available()); } @@ -651,10 +667,10 @@ mod test { ); // Manually set the peer's state. state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState; - let dummy_response = OpaqueStateResponse(Box::new(StateResponse::default())); + let dummy_response = StateResponse::default().encode_to_vec(); // Receiving response drops the peer. assert!(matches!( - state_strategy.on_state_response_inner(peer_id, dummy_response), + state_strategy.on_state_response_inner(&peer_id, &dummy_response), Err(BadPeer(id, _rep)) if id == peer_id, )); } @@ -674,8 +690,8 @@ mod test { // Manually set the peer's state . state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState; - let dummy_response = OpaqueStateResponse(Box::new(StateResponse::default())); - state_strategy.on_state_response(peer_id, dummy_response); + let dummy_response = StateResponse::default().encode_to_vec(); + state_strategy.on_state_response(&peer_id, dummy_response); // No actions generated. assert_eq!(state_strategy.actions.len(), 0) @@ -737,13 +753,13 @@ mod test { state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState; // Receive response. - let dummy_response = OpaqueStateResponse(Box::new(StateResponse::default())); - state_strategy.on_state_response(peer_id, dummy_response); + let dummy_response = StateResponse::default().encode_to_vec(); + state_strategy.on_state_response(&peer_id, dummy_response); assert_eq!(state_strategy.actions.len(), 1); assert!(matches!( &state_strategy.actions[0], - StateStrategyAction::ImportBlocks { origin, blocks } + SyncingAction::ImportBlocks { origin, blocks } if *origin == expected_origin && *blocks == expected_blocks, )); } @@ -799,7 +815,7 @@ mod test { // Strategy finishes. assert_eq!(state_strategy.actions.len(), 1); - assert!(matches!(&state_strategy.actions[0], StateStrategyAction::Finished)); + assert!(matches!(&state_strategy.actions[0], SyncingAction::Finished)); } #[test] @@ -826,7 +842,7 @@ mod test { // Strategy finishes. assert_eq!(state_strategy.actions.len(), 1); - assert!(matches!(&state_strategy.actions[0], StateStrategyAction::Finished)); + assert!(matches!(&state_strategy.actions[0], SyncingAction::Finished)); } #[test] @@ -854,12 +870,15 @@ mod test { )], ); + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); + // Strategy finishes. - let actions = state_strategy.actions().collect::>(); + let actions = state_strategy.actions(&network_handle).collect::>(); assert_eq!(actions.len(), 1); - assert!(matches!(&actions[0], StateStrategyAction::Finished)); + assert!(matches!(&actions[0], SyncingAction::Finished)); // No more actions generated. - assert_eq!(state_strategy.actions().count(), 0); + assert_eq!(state_strategy.actions(&network_handle).count(), 0); } } diff --git a/substrate/client/network/sync/src/strategy/warp.rs b/substrate/client/network/sync/src/strategy/warp.rs index 0c71dd3c6aeeea59470f30b4dbe2176541d81285..673bc1688ecc680c3ac62f5db0db53652c551bb7 100644 --- a/substrate/client/network/sync/src/strategy/warp.rs +++ b/substrate/client/network/sync/src/strategy/warp.rs @@ -21,13 +21,19 @@ pub use sp_consensus_grandpa::{AuthorityList, SetId}; use crate::{ - strategy::{chain_sync::validate_blocks, disconnected_peers::DisconnectedPeers}, + block_relay_protocol::{BlockDownloader, BlockResponseError}, + service::network::NetworkServiceHandle, + strategy::{ + chain_sync::validate_blocks, disconnected_peers::DisconnectedPeers, StrategyKey, + SyncingAction, + }, types::{BadPeer, SyncState, SyncStatus}, LOG_TARGET, }; use codec::{Decode, Encode}; +use futures::{channel::oneshot, FutureExt}; use log::{debug, error, trace, warn}; -use sc_network::ProtocolName; +use sc_network::{IfDisconnected, ProtocolName}; use sc_network_common::sync::message::{ BlockAnnounce, BlockAttributes, BlockData, BlockRequest, Direction, FromBlock, }; @@ -37,7 +43,7 @@ use sp_runtime::{ traits::{Block as BlockT, Header, NumberFor, Zero}, Justifications, SaturatedConversion, }; -use std::{collections::HashMap, fmt, sync::Arc}; +use std::{any::Any, collections::HashMap, fmt, sync::Arc}; /// Number of peers that need to be connected before warp sync is started. const MIN_PEERS_TO_START_WARP_SYNC: usize = 3; @@ -97,6 +103,9 @@ mod rep { /// Reputation change for peers which send us a block which we fail to verify. pub const VERIFICATION_FAIL: Rep = Rep::new(-(1 << 29), "Block verification failed"); + + /// We received a message that failed to decode. + pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message"); } /// Reported warp sync phase. @@ -186,22 +195,6 @@ struct Peer { state: PeerState, } -/// Action that should be performed on [`WarpSync`]'s behalf. -pub enum WarpSyncAction { - /// Send warp proof request to peer. - SendWarpProofRequest { - peer_id: PeerId, - protocol_name: ProtocolName, - request: WarpProofRequest, - }, - /// Send block request to peer. Always implies dropping a stale block request to the same peer. - SendBlockRequest { peer_id: PeerId, request: BlockRequest }, - /// Disconnect and report peer. - DropPeer(BadPeer), - /// Warp sync has finished. - Finished, -} - pub struct WarpSyncResult { pub target_header: B::Header, pub target_body: Option>, @@ -217,7 +210,8 @@ pub struct WarpSync { peers: HashMap>, disconnected_peers: DisconnectedPeers, protocol_name: Option, - actions: Vec>, + block_downloader: Arc>, + actions: Vec>, result: Option>, } @@ -226,6 +220,9 @@ where B: BlockT, Client: HeaderBackend + 'static, { + /// Strategy key used by warp sync. + pub const STRATEGY_KEY: StrategyKey = StrategyKey::new("Warp"); + /// Create a new instance. When passing a warp sync provider we will be checking for proof and /// authorities. Alternatively we can pass a target block when we want to skip downloading /// proofs, in this case we will continue polling until the target block is known. @@ -233,6 +230,7 @@ where client: Arc, warp_sync_config: WarpSyncConfig, protocol_name: Option, + block_downloader: Arc>, ) -> Self { if client.info().finalized_state.is_some() { error!( @@ -247,7 +245,8 @@ where peers: HashMap::new(), disconnected_peers: DisconnectedPeers::new(), protocol_name, - actions: vec![WarpSyncAction::Finished], + block_downloader, + actions: vec![SyncingAction::Finished], result: None, } } @@ -266,6 +265,7 @@ where peers: HashMap::new(), disconnected_peers: DisconnectedPeers::new(), protocol_name, + block_downloader, actions: Vec::new(), result: None, } @@ -285,7 +285,7 @@ where if let Some(bad_peer) = self.disconnected_peers.on_disconnect_during_request(*peer_id) { - self.actions.push(WarpSyncAction::DropPeer(bad_peer)); + self.actions.push(SyncingAction::DropPeer(bad_peer)); } } } @@ -329,6 +329,58 @@ where trace!(target: LOG_TARGET, "Started warp sync with {} peers.", self.peers.len()); } + pub fn on_generic_response( + &mut self, + peer_id: &PeerId, + protocol_name: ProtocolName, + response: Box, + ) { + if &protocol_name == self.block_downloader.protocol_name() { + let Ok(response) = response + .downcast::<(BlockRequest, Result>, BlockResponseError>)>() + else { + warn!(target: LOG_TARGET, "Failed to downcast block response"); + debug_assert!(false); + return; + }; + + let (request, response) = *response; + let blocks = match response { + Ok(blocks) => blocks, + Err(BlockResponseError::DecodeFailed(e)) => { + debug!( + target: LOG_TARGET, + "Failed to decode block response from peer {:?}: {:?}.", + peer_id, + e + ); + self.actions.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_MESSAGE))); + return; + }, + Err(BlockResponseError::ExtractionFailed(e)) => { + debug!( + target: LOG_TARGET, + "Failed to extract blocks from peer response {:?}: {:?}.", + peer_id, + e + ); + self.actions.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_MESSAGE))); + return; + }, + }; + + self.on_block_response(*peer_id, request, blocks); + } else { + let Ok(response) = response.downcast::>() else { + warn!(target: LOG_TARGET, "Failed to downcast warp sync response"); + debug_assert!(false); + return; + }; + + self.on_warp_proof_response(peer_id, EncodedProof(*response)); + } + } + /// Process warp proof response. pub fn on_warp_proof_response(&mut self, peer_id: &PeerId, response: EncodedProof) { if let Some(peer) = self.peers.get_mut(peer_id) { @@ -340,7 +392,7 @@ where else { debug!(target: LOG_TARGET, "Unexpected warp proof response"); self.actions - .push(WarpSyncAction::DropPeer(BadPeer(*peer_id, rep::UNEXPECTED_RESPONSE))); + .push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::UNEXPECTED_RESPONSE))); return }; @@ -348,7 +400,7 @@ where Err(e) => { debug!(target: LOG_TARGET, "Bad warp proof response: {}", e); self.actions - .push(WarpSyncAction::DropPeer(BadPeer(*peer_id, rep::BAD_WARP_PROOF))) + .push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_WARP_PROOF))) }, Ok(VerificationResult::Partial(new_set_id, new_authorities, new_last_hash)) => { log::debug!(target: LOG_TARGET, "Verified partial proof, set_id={:?}", new_set_id); @@ -379,7 +431,7 @@ where blocks: Vec>, ) { if let Err(bad_peer) = self.on_block_response_inner(peer_id, request, blocks) { - self.actions.push(WarpSyncAction::DropPeer(bad_peer)); + self.actions.push(SyncingAction::DropPeer(bad_peer)); } } @@ -449,7 +501,7 @@ where target_justifications: block.justifications, }); self.phase = Phase::Complete; - self.actions.push(WarpSyncAction::Finished); + self.actions.push(SyncingAction::Finished); Ok(()) } @@ -606,17 +658,67 @@ where /// Get actions that should be performed by the owner on [`WarpSync`]'s behalf #[must_use] - pub fn actions(&mut self) -> impl Iterator> { + pub fn actions( + &mut self, + network_service: &NetworkServiceHandle, + ) -> impl Iterator> { let warp_proof_request = self.warp_proof_request().into_iter().map(|(peer_id, protocol_name, request)| { - WarpSyncAction::SendWarpProofRequest { peer_id, protocol_name, request } + trace!( + target: LOG_TARGET, + "Created `WarpProofRequest` to {}, request: {:?}.", + peer_id, + request, + ); + + let (tx, rx) = oneshot::channel(); + + network_service.start_request( + peer_id, + protocol_name, + request.encode(), + tx, + IfDisconnected::ImmediateError, + ); + + SyncingAction::StartRequest { + peer_id, + key: Self::STRATEGY_KEY, + request: async move { + Ok(rx.await?.and_then(|(response, protocol_name)| { + Ok((Box::new(response) as Box, protocol_name)) + })) + } + .boxed(), + remove_obsolete: false, + } }); self.actions.extend(warp_proof_request); - let target_block_request = self - .target_block_request() - .into_iter() - .map(|(peer_id, request)| WarpSyncAction::SendBlockRequest { peer_id, request }); + let target_block_request = + self.target_block_request().into_iter().map(|(peer_id, request)| { + let downloader = self.block_downloader.clone(); + + SyncingAction::StartRequest { + peer_id, + key: Self::STRATEGY_KEY, + request: async move { + Ok(downloader.download_blocks(peer_id, request.clone()).await?.and_then( + |(response, protocol_name)| { + let decoded_response = + downloader.block_response_into_blocks(&request, response); + let result = + Box::new((request, decoded_response)) as Box; + Ok((result, protocol_name)) + }, + )) + } + .boxed(), + // Sending block request implies dropping obsolete pending response as we are + // not interested in it anymore. + remove_obsolete: true, + } + }); self.actions.extend(target_block_request); std::mem::take(&mut self.actions).into_iter() @@ -632,6 +734,7 @@ where #[cfg(test)] mod test { use super::*; + use crate::{mock::MockBlockDownloader, service::network::NetworkServiceProvider}; use sc_block_builder::BlockBuilderBuilder; use sp_blockchain::{BlockStatus, Error as BlockchainError, HeaderBackend, Info}; use sp_consensus_grandpa::{AuthorityList, SetId}; @@ -716,12 +819,16 @@ mod test { let client = mock_client_with_state(); let provider = MockWarpSyncProvider::::new(); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); + + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); // Warp sync instantly finishes - let actions = warp_sync.actions().collect::>(); + let actions = warp_sync.actions(&network_handle).collect::>(); assert_eq!(actions.len(), 1); - assert!(matches!(actions[0], WarpSyncAction::Finished)); + assert!(matches!(actions[0], SyncingAction::Finished)); // ... with no result. assert!(warp_sync.take_result().is_none()); @@ -737,12 +844,16 @@ mod test { Default::default(), Default::default(), )); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); + + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); // Warp sync instantly finishes - let actions = warp_sync.actions().collect::>(); + let actions = warp_sync.actions(&network_handle).collect::>(); assert_eq!(actions.len(), 1); - assert!(matches!(actions[0], WarpSyncAction::Finished)); + assert!(matches!(actions[0], SyncingAction::Finished)); // ... with no result. assert!(warp_sync.take_result().is_none()); @@ -753,10 +864,14 @@ mod test { let client = mock_client_without_state(); let provider = MockWarpSyncProvider::::new(); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); + + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); // No actions are emitted. - assert_eq!(warp_sync.actions().count(), 0) + assert_eq!(warp_sync.actions(&network_handle).count(), 0) } #[test] @@ -769,10 +884,14 @@ mod test { Default::default(), Default::default(), )); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); + + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); // No actions are emitted. - assert_eq!(warp_sync.actions().count(), 0) + assert_eq!(warp_sync.actions(&network_handle).count(), 0) } #[test] @@ -784,7 +903,8 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); // Warp sync is not started when there is not enough peers. for _ in 0..(MIN_PEERS_TO_START_WARP_SYNC - 1) { @@ -802,7 +922,8 @@ mod test { let client = mock_client_without_state(); let provider = MockWarpSyncProvider::::new(); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); assert!(warp_sync.schedule_next_peer(PeerState::DownloadingProofs, None).is_none()); } @@ -826,7 +947,8 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); for best_number in 1..11 { warp_sync.add_peer(PeerId::random(), Hash::random(), best_number); @@ -847,7 +969,8 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); for best_number in 1..11 { warp_sync.add_peer(PeerId::random(), Hash::random(), best_number); @@ -867,7 +990,8 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); for best_number in 1..11 { warp_sync.add_peer(PeerId::random(), Hash::random(), best_number); @@ -911,7 +1035,12 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); + let mut warp_sync = WarpSync::new( + Arc::new(client), + config, + Some(ProtocolName::Static("")), + Arc::new(MockBlockDownloader::new()), + ); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -940,7 +1069,12 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); + let mut warp_sync = WarpSync::new( + Arc::new(client), + config, + Some(ProtocolName::Static("")), + Arc::new(MockBlockDownloader::new()), + ); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -971,7 +1105,12 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); + let mut warp_sync = WarpSync::new( + Arc::new(client), + config, + Some(ProtocolName::Static("")), + Arc::new(MockBlockDownloader::new()), + ); // Make sure we have enough peers to make requests. for best_number in 1..11 { @@ -998,7 +1137,12 @@ mod test { Err(Box::new(std::io::Error::new(ErrorKind::Other, "test-verification-failure"))) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); + let mut warp_sync = WarpSync::new( + Arc::new(client), + config, + Some(ProtocolName::Static("")), + Arc::new(MockBlockDownloader::new()), + ); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1006,11 +1150,13 @@ mod test { } assert!(matches!(warp_sync.phase, Phase::WarpProof { .. })); + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); + // Consume `SendWarpProofRequest` action. - let actions = warp_sync.actions().collect::>(); + let actions = warp_sync.actions(&network_handle).collect::>(); assert_eq!(actions.len(), 1); - let WarpSyncAction::SendWarpProofRequest { peer_id: request_peer_id, .. } = actions[0] - else { + let SyncingAction::StartRequest { peer_id: request_peer_id, .. } = actions[0] else { panic!("Invalid action"); }; @@ -1021,7 +1167,7 @@ mod test { assert_eq!(actions.len(), 1); assert!(matches!( actions[0], - WarpSyncAction::DropPeer(BadPeer(peer_id, _rep)) if peer_id == request_peer_id + SyncingAction::DropPeer(BadPeer(peer_id, _rep)) if peer_id == request_peer_id )); assert!(matches!(warp_sync.phase, Phase::WarpProof { .. })); } @@ -1039,7 +1185,12 @@ mod test { Ok(VerificationResult::Partial(set_id, authorities, Hash::random())) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); + let mut warp_sync = WarpSync::new( + Arc::new(client), + config, + Some(ProtocolName::Static("")), + Arc::new(MockBlockDownloader::new()), + ); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1047,11 +1198,13 @@ mod test { } assert!(matches!(warp_sync.phase, Phase::WarpProof { .. })); + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); + // Consume `SendWarpProofRequest` action. - let actions = warp_sync.actions().collect::>(); + let actions = warp_sync.actions(&network_handle).collect::>(); assert_eq!(actions.len(), 1); - let WarpSyncAction::SendWarpProofRequest { peer_id: request_peer_id, .. } = actions[0] - else { + let SyncingAction::StartRequest { peer_id: request_peer_id, .. } = actions[0] else { panic!("Invalid action"); }; @@ -1083,7 +1236,12 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config, Some(ProtocolName::Static(""))); + let mut warp_sync = WarpSync::new( + client, + config, + Some(ProtocolName::Static("")), + Arc::new(MockBlockDownloader::new()), + ); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1091,11 +1249,13 @@ mod test { } assert!(matches!(warp_sync.phase, Phase::WarpProof { .. })); + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); + // Consume `SendWarpProofRequest` action. - let actions = warp_sync.actions().collect::>(); + let actions = warp_sync.actions(&network_handle).collect::>(); assert_eq!(actions.len(), 1); - let WarpSyncAction::SendWarpProofRequest { peer_id: request_peer_id, .. } = actions[0] - else { + let SyncingAction::StartRequest { peer_id: request_peer_id, .. } = actions[0] else { panic!("Invalid action."); }; @@ -1116,7 +1276,8 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config, None); + let mut warp_sync = + WarpSync::new(Arc::new(client), config, None, Arc::new(MockBlockDownloader::new())); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1151,7 +1312,8 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config, None); + let mut warp_sync = + WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new())); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1183,7 +1345,8 @@ mod test { .block; let target_header = target_block.header().clone(); let config = WarpSyncConfig::WithTarget(target_header); - let mut warp_sync = WarpSync::new(client, config, None); + let mut warp_sync = + WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new())); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1223,7 +1386,8 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config, None); + let mut warp_sync = + WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new())); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1261,7 +1425,8 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config, None); + let mut warp_sync = + WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new())); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1315,7 +1480,8 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config, None); + let mut warp_sync = + WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new())); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1392,7 +1558,8 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config, None); + let mut warp_sync = + WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new())); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1445,7 +1612,8 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config, None); + let mut warp_sync = + WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new())); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1473,10 +1641,13 @@ mod test { assert!(warp_sync.on_block_response_inner(peer_id, request, response).is_ok()); + let network_provider = NetworkServiceProvider::new(); + let network_handle = network_provider.handle(); + // Strategy finishes. - let actions = warp_sync.actions().collect::>(); + let actions = warp_sync.actions(&network_handle).collect::>(); assert_eq!(actions.len(), 1); - assert!(matches!(actions[0], WarpSyncAction::Finished)); + assert!(matches!(actions[0], SyncingAction::Finished)); // With correct result. let result = warp_sync.take_result().unwrap(); diff --git a/substrate/client/network/sync/src/types.rs b/substrate/client/network/sync/src/types.rs index c3403fe1e5f7561c41fa7e8460c302712282df77..5745a34378df68f65446953b55c8a0ce39f3c3d0 100644 --- a/substrate/client/network/sync/src/types.rs +++ b/substrate/client/network/sync/src/types.rs @@ -23,11 +23,10 @@ use sc_network_common::{role::Roles, types::ReputationChange}; use crate::strategy::{state_sync::StateSyncProgress, warp::WarpSyncProgress}; -use sc_network_common::sync::message::BlockRequest; use sc_network_types::PeerId; use sp_runtime::traits::{Block as BlockT, NumberFor}; -use std::{any::Any, fmt, fmt::Formatter, pin::Pin, sync::Arc}; +use std::{fmt, pin::Pin, sync::Arc}; /// The sync status of a peer we are trying to sync with #[derive(Debug)] @@ -107,52 +106,6 @@ impl fmt::Display for BadPeer { impl std::error::Error for BadPeer {} -#[derive(Debug)] -pub enum PeerRequest { - Block(BlockRequest), - State, - WarpProof, -} - -#[derive(Debug)] -pub enum PeerRequestType { - Block, - State, - WarpProof, -} - -impl PeerRequest { - pub fn get_type(&self) -> PeerRequestType { - match self { - PeerRequest::Block(_) => PeerRequestType::Block, - PeerRequest::State => PeerRequestType::State, - PeerRequest::WarpProof => PeerRequestType::WarpProof, - } - } -} - -/// Wrapper for implementation-specific state request. -/// -/// NOTE: Implementation must be able to encode and decode it for network purposes. -pub struct OpaqueStateRequest(pub Box); - -impl fmt::Debug for OpaqueStateRequest { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("OpaqueStateRequest").finish() - } -} - -/// Wrapper for implementation-specific state response. -/// -/// NOTE: Implementation must be able to encode and decode it for network purposes. -pub struct OpaqueStateResponse(pub Box); - -impl fmt::Debug for OpaqueStateResponse { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("OpaqueStateResponse").finish() - } -} - /// Provides high-level status of syncing. #[async_trait::async_trait] pub trait SyncStatusProvider: Send + Sync { diff --git a/substrate/client/network/test/src/lib.rs b/substrate/client/network/test/src/lib.rs index 06e243342fb2dd4a9f8fac8680545ef481c66b66..825481314c672ae26c2309b43880eb11e68f94c3 100644 --- a/substrate/client/network/test/src/lib.rs +++ b/substrate/client/network/test/src/lib.rs @@ -67,11 +67,11 @@ use sc_network_sync::{ service::{network::NetworkServiceProvider, syncing_service::SyncingService}, state_request_handler::StateRequestHandler, strategy::{ + polkadot::{PolkadotSyncingStrategy, PolkadotSyncingStrategyConfig}, warp::{ AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncConfig, WarpSyncProvider, }, - PolkadotSyncingStrategy, SyncingConfig, }, warp_request_handler, }; @@ -833,8 +833,8 @@ pub trait TestNetFactory: Default + Sized + Send { let fork_id = Some(String::from("test-fork-id")); - let (chain_sync_network_provider, chain_sync_network_handle) = - NetworkServiceProvider::new(); + let chain_sync_network_provider = NetworkServiceProvider::new(); + let chain_sync_network_handle = chain_sync_network_provider.handle(); let mut block_relay_params = BlockRequestHandler::new::>( chain_sync_network_handle.clone(), &protocol_id, @@ -908,12 +908,13 @@ pub trait TestNetFactory: Default + Sized + Send { ::Hash, >>::register_notification_metrics(None); - let syncing_config = SyncingConfig { + let syncing_config = PolkadotSyncingStrategyConfig { mode: network_config.sync_mode, max_parallel_downloads: network_config.max_parallel_downloads, max_blocks_per_request: network_config.max_blocks_per_request, metrics_registry: None, state_request_protocol_name: state_request_protocol_config.name.clone(), + block_downloader: block_relay_params.downloader, }; // Initialize syncing strategy. let syncing_strategy = Box::new( @@ -934,16 +935,14 @@ pub trait TestNetFactory: Default + Sized + Send { metrics, &full_net_config, protocol_id.clone(), - &fork_id, + fork_id.as_deref(), block_announce_validator, syncing_strategy, chain_sync_network_handle, import_queue.service(), - block_relay_params.downloader, peer_store_handle.clone(), ) .unwrap(); - let sync_service_import_queue = Box::new(sync_service.clone()); let sync_service = Arc::new(sync_service.clone()); for config in config.request_response_protocols { @@ -987,8 +986,12 @@ pub trait TestNetFactory: Default + Sized + Send { chain_sync_network_provider.run(service).await; }); - tokio::spawn(async move { - import_queue.run(sync_service_import_queue).await; + tokio::spawn({ + let sync_service = sync_service.clone(); + + async move { + import_queue.run(sync_service.as_ref()).await; + } }); tokio::spawn(async move { diff --git a/substrate/client/network/test/src/service.rs b/substrate/client/network/test/src/service.rs index ad2d1d9ec24de96b00846127b6bb0e71638429b4..688b569c32228448a4e16f7b5a60b175c8411027 100644 --- a/substrate/client/network/test/src/service.rs +++ b/substrate/client/network/test/src/service.rs @@ -32,9 +32,9 @@ use sc_network_light::light_client_requests::handler::LightClientRequestHandler; use sc_network_sync::{ block_request_handler::BlockRequestHandler, engine::SyncingEngine, - service::network::{NetworkServiceHandle, NetworkServiceProvider}, + service::network::NetworkServiceProvider, state_request_handler::StateRequestHandler, - strategy::{PolkadotSyncingStrategy, SyncingConfig}, + strategy::polkadot::{PolkadotSyncingStrategy, PolkadotSyncingStrategyConfig}, }; use sp_blockchain::HeaderBackend; use sp_runtime::traits::{Block as BlockT, Zero}; @@ -78,7 +78,7 @@ struct TestNetworkBuilder { client: Option>, listen_addresses: Vec, set_config: Option, - chain_sync_network: Option<(NetworkServiceProvider, NetworkServiceHandle)>, + chain_sync_network: Option, notification_protocols: Vec, config: Option, } @@ -157,8 +157,9 @@ impl TestNetworkBuilder { let fork_id = Some(String::from("test-fork-id")); let mut full_net_config = FullNetworkConfiguration::new(&network_config, None); - let (chain_sync_network_provider, chain_sync_network_handle) = + let chain_sync_network_provider = self.chain_sync_network.unwrap_or(NetworkServiceProvider::new()); + let chain_sync_network_handle = chain_sync_network_provider.handle(); let mut block_relay_params = BlockRequestHandler::new::< NetworkWorker< @@ -203,12 +204,13 @@ impl TestNetworkBuilder { let peer_store_handle: Arc = Arc::new(peer_store.handle()); tokio::spawn(peer_store.run().boxed()); - let syncing_config = SyncingConfig { + let syncing_config = PolkadotSyncingStrategyConfig { mode: network_config.sync_mode, max_parallel_downloads: network_config.max_parallel_downloads, max_blocks_per_request: network_config.max_blocks_per_request, metrics_registry: None, state_request_protocol_name: state_request_protocol_config.name.clone(), + block_downloader: block_relay_params.downloader, }; // Initialize syncing strategy. let syncing_strategy = Box::new( @@ -222,12 +224,11 @@ impl TestNetworkBuilder { NotificationMetrics::new(None), &full_net_config, protocol_id.clone(), - &None, + None, Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator), syncing_strategy, chain_sync_network_handle, import_queue.service(), - block_relay_params.downloader, Arc::clone(&peer_store_handle), ) .unwrap(); diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index f27b7ec6fbad87725a560192bd063737ce2157d6..ce4ce7c082483d41e57fa239d16223050f83bd31 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -35,7 +35,7 @@ use sc_client_api::{ BlockBackend, BlockchainEvents, ExecutorProvider, ForkBlocks, StorageProvider, UsageProvider, }; use sc_client_db::{Backend, BlocksPruning, DatabaseSettings, PruningMode}; -use sc_consensus::import_queue::ImportQueue; +use sc_consensus::import_queue::{ImportQueue, ImportQueueService}; use sc_executor::{ sp_wasm_interface::HostFunctions, HeapAllocStrategy, NativeExecutionDispatch, RuntimeVersionOf, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY, @@ -50,15 +50,18 @@ use sc_network::{ }, NetworkBackend, NetworkStateInfo, }; -use sc_network_common::role::Roles; +use sc_network_common::role::{Role, Roles}; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; use sc_network_sync::{ - block_relay_protocol::BlockRelayParams, + block_relay_protocol::{BlockDownloader, BlockRelayParams}, block_request_handler::BlockRequestHandler, engine::SyncingEngine, - service::network::NetworkServiceProvider, + service::network::{NetworkServiceHandle, NetworkServiceProvider}, state_request_handler::StateRequestHandler, - strategy::{PolkadotSyncingStrategy, SyncingConfig, SyncingStrategy}, + strategy::{ + polkadot::{PolkadotSyncingStrategy, PolkadotSyncingStrategyConfig}, + SyncingStrategy, + }, warp_request_handler::RequestHandler as WarpSyncRequestHandler, SyncingService, WarpSyncConfig, }; @@ -780,7 +783,7 @@ where Ok(rpc_api) } -/// Parameters to pass into `build_network`. +/// Parameters to pass into [`build_network`]. pub struct BuildNetworkParams<'a, Block, Net, TxPool, IQ, Client> where Block: BlockT, @@ -802,8 +805,8 @@ where pub block_announce_validator_builder: Option< Box) -> Box + Send> + Send>, >, - /// Syncing strategy to use in syncing engine. - pub syncing_strategy: Box>, + /// Optional warp sync config. + pub warp_sync_config: Option>, /// User specified block relay params. If not specified, the default /// block request handler will be used. pub block_relay: Option>, @@ -847,100 +850,217 @@ where spawn_handle, import_queue, block_announce_validator_builder, - syncing_strategy, + warp_sync_config, block_relay, metrics, } = params; - let protocol_id = config.protocol_id(); - let genesis_hash = client.info().genesis_hash; - let block_announce_validator = if let Some(f) = block_announce_validator_builder { f(client.clone()) } else { Box::new(DefaultBlockAnnounceValidator) }; - let (chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - let (mut block_server, block_downloader, block_request_protocol_config) = match block_relay { - Some(params) => (params.server, params.downloader, params.request_response_config), - None => { - // Custom protocol was not specified, use the default block handler. - // Allow both outgoing and incoming requests. - let params = BlockRequestHandler::new::( - chain_sync_network_handle.clone(), - &protocol_id, - config.chain_spec.fork_id(), - client.clone(), - config.network.default_peers_set.in_peers as usize + - config.network.default_peers_set.out_peers as usize, - ); - (params.server, params.downloader, params.request_response_config) + let network_service_provider = NetworkServiceProvider::new(); + let protocol_id = config.protocol_id(); + let fork_id = config.chain_spec.fork_id(); + let metrics_registry = config.prometheus_config.as_ref().map(|config| &config.registry); + + let block_downloader = match block_relay { + Some(params) => { + let BlockRelayParams { mut server, downloader, request_response_config } = params; + + net_config.add_request_response_protocol(request_response_config); + + spawn_handle.spawn("block-request-handler", Some("networking"), async move { + server.run().await; + }); + + downloader }, + None => build_default_block_downloader( + &protocol_id, + fork_id, + &mut net_config, + network_service_provider.handle(), + Arc::clone(&client), + config.network.default_peers_set.in_peers as usize + + config.network.default_peers_set.out_peers as usize, + &spawn_handle, + ), }; - spawn_handle.spawn("block-request-handler", Some("networking"), async move { - block_server.run().await; - }); + + let syncing_strategy = build_polkadot_syncing_strategy( + protocol_id.clone(), + fork_id, + &mut net_config, + warp_sync_config, + block_downloader, + client.clone(), + &spawn_handle, + metrics_registry, + )?; + + let (syncing_engine, sync_service, block_announce_config) = SyncingEngine::new( + Roles::from(&config.role), + Arc::clone(&client), + metrics_registry, + metrics.clone(), + &net_config, + protocol_id.clone(), + fork_id, + block_announce_validator, + syncing_strategy, + network_service_provider.handle(), + import_queue.service(), + net_config.peer_store_handle(), + )?; + + spawn_handle.spawn_blocking("syncing", None, syncing_engine.run()); + + build_network_advanced(BuildNetworkAdvancedParams { + role: config.role, + protocol_id, + fork_id, + ipfs_server: config.network.ipfs_server, + announce_block: config.announce_block, + net_config, + client, + transaction_pool, + spawn_handle, + import_queue, + sync_service, + block_announce_config, + network_service_provider, + metrics_registry, + metrics, + }) +} + +/// Parameters to pass into [`build_network_advanced`]. +pub struct BuildNetworkAdvancedParams<'a, Block, Net, TxPool, IQ, Client> +where + Block: BlockT, + Net: NetworkBackend::Hash>, +{ + /// Role of the local node. + pub role: Role, + /// Protocol name prefix. + pub protocol_id: ProtocolId, + /// Fork ID. + pub fork_id: Option<&'a str>, + /// Enable serving block data over IPFS bitswap. + pub ipfs_server: bool, + /// Announce block automatically after they have been imported. + pub announce_block: bool, + /// Full network configuration. + pub net_config: FullNetworkConfiguration::Hash, Net>, + /// A shared client returned by `new_full_parts`. + pub client: Arc, + /// A shared transaction pool. + pub transaction_pool: Arc, + /// A handle for spawning tasks. + pub spawn_handle: SpawnTaskHandle, + /// An import queue. + pub import_queue: IQ, + /// Syncing service to communicate with syncing engine. + pub sync_service: SyncingService, + /// Block announce config. + pub block_announce_config: Net::NotificationProtocolConfig, + /// Network service provider to drive with network internally. + pub network_service_provider: NetworkServiceProvider, + /// Prometheus metrics registry. + pub metrics_registry: Option<&'a Registry>, + /// Metrics. + pub metrics: NotificationMetrics, +} + +/// Build the network service, the network status sinks and an RPC sender, this is a lower-level +/// version of [`build_network`] for those needing more control. +pub fn build_network_advanced( + params: BuildNetworkAdvancedParams, +) -> Result< + ( + Arc, + TracingUnboundedSender>, + sc_network_transactions::TransactionsHandlerController<::Hash>, + NetworkStarter, + Arc>, + ), + Error, +> +where + Block: BlockT, + Client: ProvideRuntimeApi + + HeaderMetadata + + Chain + + BlockBackend + + BlockIdTo + + ProofProvider + + HeaderBackend + + BlockchainEvents + + 'static, + TxPool: TransactionPool::Hash> + 'static, + IQ: ImportQueue + 'static, + Net: NetworkBackend::Hash>, +{ + let BuildNetworkAdvancedParams { + role, + protocol_id, + fork_id, + ipfs_server, + announce_block, + mut net_config, + client, + transaction_pool, + spawn_handle, + import_queue, + sync_service, + block_announce_config, + network_service_provider, + metrics_registry, + metrics, + } = params; + + let genesis_hash = client.info().genesis_hash; let light_client_request_protocol_config = { // Allow both outgoing and incoming requests. - let (handler, protocol_config) = LightClientRequestHandler::new::( - &protocol_id, - config.chain_spec.fork_id(), - client.clone(), - ); + let (handler, protocol_config) = + LightClientRequestHandler::new::(&protocol_id, fork_id, client.clone()); spawn_handle.spawn("light-client-request-handler", Some("networking"), handler.run()); protocol_config }; // install request handlers to `FullNetworkConfiguration` - net_config.add_request_response_protocol(block_request_protocol_config); net_config.add_request_response_protocol(light_client_request_protocol_config); - let bitswap_config = config.network.ipfs_server.then(|| { + let bitswap_config = ipfs_server.then(|| { let (handler, config) = Net::bitswap_server(client.clone()); spawn_handle.spawn("bitswap-request-handler", Some("networking"), handler); config }); - // create transactions protocol and add it to the list of supported protocols of - let peer_store_handle = net_config.peer_store_handle(); + // Create transactions protocol and add it to the list of supported protocols of let (transactions_handler_proto, transactions_config) = sc_network_transactions::TransactionsHandlerPrototype::new::<_, Block, Net>( protocol_id.clone(), genesis_hash, - config.chain_spec.fork_id(), + fork_id, metrics.clone(), - Arc::clone(&peer_store_handle), + net_config.peer_store_handle(), ); net_config.add_notification_protocol(transactions_config); // Start task for `PeerStore` let peer_store = net_config.take_peer_store(); - let peer_store_handle = peer_store.handle(); spawn_handle.spawn("peer-store", Some("networking"), peer_store.run()); - let (engine, sync_service, block_announce_config) = SyncingEngine::new( - Roles::from(&config.role), - client.clone(), - config.prometheus_config.as_ref().map(|config| config.registry.clone()).as_ref(), - metrics.clone(), - &net_config, - protocol_id.clone(), - &config.chain_spec.fork_id().map(ToOwned::to_owned), - block_announce_validator, - syncing_strategy, - chain_sync_network_handle, - import_queue.service(), - block_downloader, - Arc::clone(&peer_store_handle), - )?; - let sync_service_import_queue = sync_service.clone(); let sync_service = Arc::new(sync_service); let network_params = sc_network::config::Params::::Hash, Net> { - role: config.role, + role, executor: { let spawn_handle = Clone::clone(&spawn_handle); Box::new(move |fut| { @@ -950,8 +1070,8 @@ where network_config: net_config, genesis_hash, protocol_id, - fork_id: config.chain_spec.fork_id().map(ToOwned::to_owned), - metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()), + fork_id: fork_id.map(ToOwned::to_owned), + metrics_registry: metrics_registry.cloned(), block_announce_config, bitswap_config, notification_metrics: metrics, @@ -965,7 +1085,7 @@ where network.clone(), sync_service.clone(), Arc::new(TransactionPoolAdapter { pool: transaction_pool, client: client.clone() }), - config.prometheus_config.as_ref().map(|config| &config.registry), + metrics_registry, )?; spawn_handle.spawn_blocking( "network-transactions-handler", @@ -976,17 +1096,20 @@ where spawn_handle.spawn_blocking( "chain-sync-network-service-provider", Some("networking"), - chain_sync_network_provider.run(Arc::new(network.clone())), + network_service_provider.run(Arc::new(network.clone())), ); - spawn_handle.spawn("import-queue", None, import_queue.run(Box::new(sync_service_import_queue))); - spawn_handle.spawn_blocking("syncing", None, engine.run()); + spawn_handle.spawn("import-queue", None, { + let sync_service = sync_service.clone(); + + async move { import_queue.run(sync_service.as_ref()).await } + }); let (system_rpc_tx, system_rpc_rx) = tracing_unbounded("mpsc_system_rpc", 10_000); spawn_handle.spawn( "system-rpc-handler", Some("networking"), build_system_rpc_future::<_, _, ::Hash>( - config.role, + role, network_mut.network_service(), sync_service.clone(), client.clone(), @@ -999,7 +1122,7 @@ where network_mut, client, sync_service.clone(), - config.announce_block, + announce_block, ); // TODO: Normally, one is supposed to pass a list of notifications protocols supported by the @@ -1047,12 +1170,154 @@ where )) } +/// Configuration for [`build_default_syncing_engine`]. +pub struct DefaultSyncingEngineConfig<'a, Block, Client, Net> +where + Block: BlockT, + Net: NetworkBackend::Hash>, +{ + /// Role of the local node. + pub role: Role, + /// Protocol name prefix. + pub protocol_id: ProtocolId, + /// Fork ID. + pub fork_id: Option<&'a str>, + /// Full network configuration. + pub net_config: &'a mut FullNetworkConfiguration::Hash, Net>, + /// Validator for incoming block announcements. + pub block_announce_validator: Box + Send>, + /// Handle to communicate with `NetworkService`. + pub network_service_handle: NetworkServiceHandle, + /// Warp sync configuration (when used). + pub warp_sync_config: Option>, + /// A shared client returned by `new_full_parts`. + pub client: Arc, + /// Blocks import queue API. + pub import_queue_service: Box>, + /// Expected max total number of peer connections (in + out). + pub num_peers_hint: usize, + /// A handle for spawning tasks. + pub spawn_handle: &'a SpawnTaskHandle, + /// Prometheus metrics registry. + pub metrics_registry: Option<&'a Registry>, + /// Metrics. + pub metrics: NotificationMetrics, +} + +/// Build default syncing engine using [`build_default_block_downloader`] and +/// [`build_polkadot_syncing_strategy`] internally. +pub fn build_default_syncing_engine( + config: DefaultSyncingEngineConfig, +) -> Result<(SyncingService, Net::NotificationProtocolConfig), Error> +where + Block: BlockT, + Client: HeaderBackend + + BlockBackend + + HeaderMetadata + + ProofProvider + + Send + + Sync + + 'static, + Net: NetworkBackend::Hash>, +{ + let DefaultSyncingEngineConfig { + role, + protocol_id, + fork_id, + net_config, + block_announce_validator, + network_service_handle, + warp_sync_config, + client, + import_queue_service, + num_peers_hint, + spawn_handle, + metrics_registry, + metrics, + } = config; + + let block_downloader = build_default_block_downloader( + &protocol_id, + fork_id, + net_config, + network_service_handle.clone(), + client.clone(), + num_peers_hint, + spawn_handle, + ); + let syncing_strategy = build_polkadot_syncing_strategy( + protocol_id.clone(), + fork_id, + net_config, + warp_sync_config, + block_downloader, + client.clone(), + spawn_handle, + metrics_registry, + )?; + + let (syncing_engine, sync_service, block_announce_config) = SyncingEngine::new( + Roles::from(&role), + client, + metrics_registry, + metrics, + &net_config, + protocol_id, + fork_id, + block_announce_validator, + syncing_strategy, + network_service_handle, + import_queue_service, + net_config.peer_store_handle(), + )?; + + spawn_handle.spawn_blocking("syncing", None, syncing_engine.run()); + + Ok((sync_service, block_announce_config)) +} + +/// Build default block downloader +pub fn build_default_block_downloader( + protocol_id: &ProtocolId, + fork_id: Option<&str>, + net_config: &mut FullNetworkConfiguration::Hash, Net>, + network_service_handle: NetworkServiceHandle, + client: Arc, + num_peers_hint: usize, + spawn_handle: &SpawnTaskHandle, +) -> Arc> +where + Block: BlockT, + Client: HeaderBackend + BlockBackend + Send + Sync + 'static, + Net: NetworkBackend::Hash>, +{ + // Custom protocol was not specified, use the default block handler. + // Allow both outgoing and incoming requests. + let BlockRelayParams { mut server, downloader, request_response_config } = + BlockRequestHandler::new::( + network_service_handle, + &protocol_id, + fork_id, + client.clone(), + num_peers_hint, + ); + + spawn_handle.spawn("block-request-handler", Some("networking"), async move { + server.run().await; + }); + + net_config.add_request_response_protocol(request_response_config); + + downloader +} + /// Build standard polkadot syncing strategy pub fn build_polkadot_syncing_strategy( protocol_id: ProtocolId, fork_id: Option<&str>, net_config: &mut FullNetworkConfiguration::Hash, Net>, warp_sync_config: Option>, + block_downloader: Arc>, client: Arc, spawn_handle: &SpawnTaskHandle, metrics_registry: Option<&Registry>, @@ -1066,7 +1331,6 @@ where + Send + Sync + 'static, - Net: NetworkBackend::Hash>, { if warp_sync_config.is_none() && net_config.network_config.sync_mode.is_warp() { @@ -1117,12 +1381,13 @@ where net_config.add_request_response_protocol(config); } - let syncing_config = SyncingConfig { + let syncing_config = PolkadotSyncingStrategyConfig { mode: net_config.network_config.sync_mode, max_parallel_downloads: net_config.network_config.max_parallel_downloads, max_blocks_per_request: net_config.network_config.max_blocks_per_request, metrics_registry: metrics_registry.cloned(), state_request_protocol_name, + block_downloader, }; Ok(Box::new(PolkadotSyncingStrategy::new( syncing_config, diff --git a/substrate/client/service/src/chain_ops/import_blocks.rs b/substrate/client/service/src/chain_ops/import_blocks.rs index 661fc09a8f19e55c38bec280b7ce399a8dbe2e29..8e759faa0775d6aa2f3adbec83273079aded4498 100644 --- a/substrate/client/service/src/chain_ops/import_blocks.rs +++ b/substrate/client/service/src/chain_ops/import_blocks.rs @@ -37,6 +37,10 @@ use sp_runtime::{ use std::{ io::Read, pin::Pin, + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, + }, task::Poll, time::{Duration, Instant}, }; @@ -50,8 +54,6 @@ const DELAY_TIME: u64 = 200; /// Number of milliseconds that must have passed between two updates. const TIME_BETWEEN_UPDATES: u64 = 3_000; -use std::sync::Arc; - /// Build a chain spec json pub fn build_spec(spec: &dyn ChainSpec, raw: bool) -> error::Result { spec.as_json(raw).map_err(Into::into) @@ -301,29 +303,29 @@ where IQ: ImportQueue + 'static, { struct WaitLink { - imported_blocks: u64, - has_error: bool, + imported_blocks: AtomicU64, + has_error: AtomicBool, } impl WaitLink { fn new() -> WaitLink { - WaitLink { imported_blocks: 0, has_error: false } + WaitLink { imported_blocks: AtomicU64::new(0), has_error: AtomicBool::new(false) } } } impl Link for WaitLink { fn blocks_processed( - &mut self, + &self, imported: usize, _num_expected_blocks: usize, results: Vec<(Result>, BlockImportError>, B::Hash)>, ) { - self.imported_blocks += imported as u64; + self.imported_blocks.fetch_add(imported as u64, Ordering::AcqRel); for result in results { if let (Err(err), hash) = result { warn!("There was an error importing block with hash {:?}: {}", hash, err); - self.has_error = true; + self.has_error.store(true, Ordering::Release); break } } @@ -373,7 +375,9 @@ where let read_block_count = block_iter.read_block_count(); match block_result { Ok(block) => { - if read_block_count - link.imported_blocks >= MAX_PENDING_BLOCKS { + if read_block_count - link.imported_blocks.load(Ordering::Acquire) >= + MAX_PENDING_BLOCKS + { // The queue is full, so do not add this block and simply wait // until the queue has made some progress. let delay = Delay::new(Duration::from_millis(DELAY_TIME)); @@ -399,7 +403,9 @@ where }, ImportState::WaitingForImportQueueToCatchUp { block_iter, mut delay, block } => { let read_block_count = block_iter.read_block_count(); - if read_block_count - link.imported_blocks >= MAX_PENDING_BLOCKS { + if read_block_count - link.imported_blocks.load(Ordering::Acquire) >= + MAX_PENDING_BLOCKS + { // Queue is still full, so wait until there is room to insert our block. match Pin::new(&mut delay).poll(cx) { Poll::Pending => { @@ -433,7 +439,11 @@ where } => { // All the blocks have been added to the queue, which doesn't mean they // have all been properly imported. - if importing_is_done(num_expected_blocks, read_block_count, link.imported_blocks) { + if importing_is_done( + num_expected_blocks, + read_block_count, + link.imported_blocks.load(Ordering::Acquire), + ) { // Importing is done, we can log the result and return. info!( "๐ŸŽ‰ Imported {} blocks. Best: #{}", @@ -472,10 +482,10 @@ where let best_number = client.info().best_number; speedometer.notify_user(best_number); - if link.has_error { + if link.has_error.load(Ordering::Acquire) { return Poll::Ready(Err(Error::Other(format!( "Stopping after #{} blocks because of an error", - link.imported_blocks + link.imported_blocks.load(Ordering::Acquire) )))) } diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 54e847791cff174c4c71298d29b27dda1860f475..ee4f4e7622e74b8f07225edd729d32b3b4e98df1 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -59,11 +59,13 @@ use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; pub use self::{ builder::{ - build_network, build_polkadot_syncing_strategy, gen_rpc_module, init_telemetry, new_client, - new_db_backend, new_full_client, new_full_parts, new_full_parts_record_import, + build_default_block_downloader, build_default_syncing_engine, build_network, + build_network_advanced, build_polkadot_syncing_strategy, gen_rpc_module, init_telemetry, + new_client, new_db_backend, new_full_client, new_full_parts, new_full_parts_record_import, new_full_parts_with_genesis_builder, new_wasm_executor, - propagate_transaction_notifications, spawn_tasks, BuildNetworkParams, KeystoreContainer, - NetworkStarter, SpawnTasksParams, TFullBackend, TFullCallExecutor, TFullClient, + propagate_transaction_notifications, spawn_tasks, BuildNetworkAdvancedParams, + BuildNetworkParams, DefaultSyncingEngineConfig, KeystoreContainer, NetworkStarter, + SpawnTasksParams, TFullBackend, TFullCallExecutor, TFullClient, }, client::{ClientConfig, LocalCallExecutor}, error::Error, @@ -98,7 +100,9 @@ pub use sc_transaction_pool::TransactionPoolOptions; pub use sc_transaction_pool_api::{error::IntoPoolError, InPoolTransaction, TransactionPool}; #[doc(hidden)] pub use std::{ops::Deref, result::Result, sync::Arc}; -pub use task_manager::{SpawnTaskHandle, Task, TaskManager, TaskRegistry, DEFAULT_GROUP_NAME}; +pub use task_manager::{ + SpawnEssentialTaskHandle, SpawnTaskHandle, Task, TaskManager, TaskRegistry, DEFAULT_GROUP_NAME, +}; use tokio::runtime::Handle; const DEFAULT_PROTOCOL_ID: &str = "sup"; diff --git a/substrate/frame/benchmarking/src/utils.rs b/substrate/frame/benchmarking/src/utils.rs index ca362f7aa7efe20e4c78a29a2a7d3136ec548775..fb55cee99e810eb160fb0a77eb9d7f31807a3df0 100644 --- a/substrate/frame/benchmarking/src/utils.rs +++ b/substrate/frame/benchmarking/src/utils.rs @@ -238,7 +238,7 @@ sp_api::decl_runtime_apis! { fn benchmark_metadata(extra: bool) -> (Vec, Vec); /// Dispatch the given benchmark. - fn dispatch_benchmark(config: BenchmarkConfig) -> Result, sp_runtime::RuntimeString>; + fn dispatch_benchmark(config: BenchmarkConfig) -> Result, alloc::string::String>; } } diff --git a/substrate/frame/benchmarking/src/v1.rs b/substrate/frame/benchmarking/src/v1.rs index d687f9fdfa1048241e6a4547ce0d5fa5a9110b8d..e73ed1f4382f6fd25759822c21334c3424076db8 100644 --- a/substrate/frame/benchmarking/src/v1.rs +++ b/substrate/frame/benchmarking/src/v1.rs @@ -1734,8 +1734,8 @@ pub fn show_benchmark_debug_info( components: &[(BenchmarkParameter, u32)], verify: &bool, error_message: &str, -) -> sp_runtime::RuntimeString { - sp_runtime::format_runtime_string!( +) -> alloc::string::String { + alloc::format!( "\n* Pallet: {}\n\ * Benchmark: {}\n\ * Components: {:?}\n\ diff --git a/substrate/frame/bounties/src/benchmarking.rs b/substrate/frame/bounties/src/benchmarking.rs index 6fa60e6938b6aa77a2c08d8c3fdb46e9794cd407..8ad85d5420edd09438e52c62a4ca81097eeb231d 100644 --- a/substrate/frame/bounties/src/benchmarking.rs +++ b/substrate/frame/bounties/src/benchmarking.rs @@ -125,6 +125,21 @@ benchmarks_instance_pallet! { Treasury::::on_initialize(frame_system::Pallet::::block_number()); }: _(approve_origin, bounty_id, curator_lookup, fee) + approve_bounty_with_curator { + setup_pot_account::(); + let (caller, curator, fee, value, reason) = setup_bounty::(0, T::MaximumReasonLength::get()); + let curator_lookup = T::Lookup::unlookup(curator.clone()); + Bounties::::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::::get() - 1; + let approve_origin = T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + Treasury::::on_initialize(BlockNumberFor::::zero()); + }: _(approve_origin, bounty_id, curator_lookup, fee) + verify { + assert_last_event::( + Event::CuratorProposed { bounty_id, curator }.into() + ); + } + // Worst case when curator is inactive and any sender unassigns the curator. unassign_curator { setup_pot_account::(); diff --git a/substrate/frame/bounties/src/lib.rs b/substrate/frame/bounties/src/lib.rs index 9d5931f4a608e040b14c3530018ddecd105824c3..3ed408a19120ae734e28428eb04b5ab23f921231 100644 --- a/substrate/frame/bounties/src/lib.rs +++ b/substrate/frame/bounties/src/lib.rs @@ -73,6 +73,8 @@ //! - `approve_bounty` - Accept a specific treasury amount to be earmarked for a predefined body of //! work. //! - `propose_curator` - Assign an account to a bounty as candidate curator. +//! - `approve_bounty_with_curator` - Accept a specific treasury amount for a predefined body of +//! work with assigned candidate curator account. //! - `accept_curator` - Accept a bounty assignment from the Council, setting a curator deposit. //! - `extend_bounty_expiry` - Extend the expiry block number of the bounty and stay active. //! - `award_bounty` - Close and pay out the specified amount for the completed work. @@ -174,6 +176,11 @@ pub enum BountyStatus { /// When the bounty can be claimed. unlock_at: BlockNumber, }, + /// The bounty is approved with curator assigned. + ApprovedWithCurator { + /// The assigned curator of this bounty. + curator: AccountId, + }, } /// The child bounty manager. @@ -181,8 +188,11 @@ pub trait ChildBountyManager { /// Get the active child bounties for a parent bounty. fn child_bounties_count(bounty_id: BountyIndex) -> BountyIndex; - /// Get total curator fees of children-bounty curators. + /// Take total curator fees of children-bounty curators. fn children_curator_fees(bounty_id: BountyIndex) -> Balance; + + /// Hook called when a parent bounty is removed. + fn bounty_removed(bounty_id: BountyIndex); } #[frame_support::pallet] @@ -326,6 +336,7 @@ pub mod pallet { /// Bounty indices that have been approved but not yet funded. #[pallet::storage] + #[allow(deprecated)] pub type BountyApprovals, I: 'static = ()> = StorageValue<_, BoundedVec, ValueQuery>; @@ -471,6 +482,15 @@ pub mod pallet { // No curator to unassign at this point. return Err(Error::::UnexpectedStatus.into()) }, + BountyStatus::ApprovedWithCurator { ref curator } => { + // Bounty not yet funded, but bounty was approved with curator. + // `RejectOrigin` or curator himself can unassign from this bounty. + ensure!(maybe_sender.map_or(true, |sender| sender == *curator), BadOrigin); + // This state can only be while the bounty is not yet funded so we return + // bounty to the `Approved` state without curator + bounty.status = BountyStatus::Approved; + return Ok(()); + }, BountyStatus::CuratorProposed { ref curator } => { // A curator has been proposed, but not accepted yet. // Either `RejectOrigin` or the proposed curator can unassign the curator. @@ -662,6 +682,7 @@ pub mod pallet { *maybe_bounty = None; BountyDescriptions::::remove(bounty_id); + T::ChildBountyManager::bounty_removed(bounty_id); Self::deposit_event(Event::::BountyClaimed { index: bounty_id, @@ -723,7 +744,7 @@ pub mod pallet { Some(>::WeightInfo::close_bounty_proposed()).into() ) }, - BountyStatus::Approved => { + BountyStatus::Approved | BountyStatus::ApprovedWithCurator { .. } => { // For weight reasons, we don't allow a council to cancel in this phase. // We ask for them to wait until it is funded before they can cancel. return Err(Error::::UnexpectedStatus.into()) @@ -759,7 +780,9 @@ pub mod pallet { AllowDeath, ); // should not fail debug_assert!(res.is_ok()); + *maybe_bounty = None; + T::ChildBountyManager::bounty_removed(bounty_id); Self::deposit_event(Event::::BountyCanceled { index: bounty_id }); Ok(Some(>::WeightInfo::close_bounty_active()).into()) @@ -804,6 +827,52 @@ pub mod pallet { Self::deposit_event(Event::::BountyExtended { index: bounty_id }); Ok(()) } + + /// Approve bountry and propose a curator simultaneously. + /// This call is a shortcut to calling `approve_bounty` and `propose_curator` separately. + /// + /// May only be called from `T::SpendOrigin`. + /// + /// - `bounty_id`: Bounty ID to approve. + /// - `curator`: The curator account whom will manage this bounty. + /// - `fee`: The curator fee. + /// + /// ## Complexity + /// - O(1). + #[pallet::call_index(9)] + #[pallet::weight(>::WeightInfo::approve_bounty_with_curator())] + pub fn approve_bounty_with_curator( + origin: OriginFor, + #[pallet::compact] bounty_id: BountyIndex, + curator: AccountIdLookupOf, + #[pallet::compact] fee: BalanceOf, + ) -> DispatchResult { + let max_amount = T::SpendOrigin::ensure_origin(origin)?; + let curator = T::Lookup::lookup(curator)?; + Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + // approve bounty + let bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + ensure!( + bounty.value <= max_amount, + pallet_treasury::Error::::InsufficientPermission + ); + ensure!(bounty.status == BountyStatus::Proposed, Error::::UnexpectedStatus); + ensure!(fee < bounty.value, Error::::InvalidFee); + + BountyApprovals::::try_append(bounty_id) + .map_err(|()| Error::::TooManyQueued)?; + + bounty.status = BountyStatus::ApprovedWithCurator { curator: curator.clone() }; + bounty.fee = fee; + + Ok(()) + })?; + + Self::deposit_event(Event::::BountyApproved { index: bounty_id }); + Self::deposit_event(Event::::CuratorProposed { bounty_id, curator }); + + Ok(()) + } } #[pallet::hooks] @@ -946,7 +1015,13 @@ impl, I: 'static> pallet_treasury::SpendFunds for Pallet ChildBountyManager for () { fn children_curator_fees(_bounty_id: BountyIndex) -> Balance { Zero::zero() } + + fn bounty_removed(_bounty_id: BountyIndex) {} } diff --git a/substrate/frame/bounties/src/tests.rs b/substrate/frame/bounties/src/tests.rs index 37bcadddae5b80961501951a493a14d6f2a3bdfd..447d0edb4122d528a7f002e52befe786782d9f1d 100644 --- a/substrate/frame/bounties/src/tests.rs +++ b/substrate/frame/bounties/src/tests.rs @@ -209,16 +209,29 @@ impl ExtBuilder { } } -fn last_event() -> BountiesEvent { - System::events() +fn last_events(n: usize) -> Vec> { + let mut res = System::events() .into_iter() - .map(|r| r.event) - .filter_map(|e| if let RuntimeEvent::Bounties(inner) = e { Some(inner) } else { None }) - .last() - .unwrap() + .rev() + .filter_map( + |e| if let RuntimeEvent::Bounties(inner) = e.event { Some(inner) } else { None }, + ) + .take(n) + .collect::>(); + res.reverse(); + res +} + +fn last_event() -> BountiesEvent { + last_events(1).into_iter().next().unwrap() +} + +fn expect_events(e: Vec>) { + assert_eq!(last_events(e.len()), e); } #[test] +#[allow(deprecated)] fn genesis_config_works() { ExtBuilder::default().build_and_execute(|| { assert_eq!(Treasury::pot(), 0); @@ -236,6 +249,7 @@ fn minting_works() { } #[test] +#[allow(deprecated)] fn accepted_spend_proposal_ignored_outside_spend_period() { ExtBuilder::default().build_and_execute(|| { Balances::make_free_balance_be(&Treasury::account_id(), 101); @@ -262,6 +276,7 @@ fn unused_pot_should_diminish() { } #[test] +#[allow(deprecated)] fn accepted_spend_proposal_enacted_on_spend_period() { ExtBuilder::default().build_and_execute(|| { Balances::make_free_balance_be(&Treasury::account_id(), 101); @@ -276,6 +291,7 @@ fn accepted_spend_proposal_enacted_on_spend_period() { } #[test] +#[allow(deprecated)] fn pot_underflow_should_not_diminish() { ExtBuilder::default().build_and_execute(|| { Balances::make_free_balance_be(&Treasury::account_id(), 101); @@ -296,6 +312,7 @@ fn pot_underflow_should_not_diminish() { // Treasury account doesn't get deleted if amount approved to spend is all its free balance. // i.e. pot should not include existential deposit needed for account survival. #[test] +#[allow(deprecated)] fn treasury_account_doesnt_get_deleted() { ExtBuilder::default().build_and_execute(|| { Balances::make_free_balance_be(&Treasury::account_id(), 101); @@ -318,6 +335,7 @@ fn treasury_account_doesnt_get_deleted() { // In case treasury account is not existing then it works fine. // This is useful for chain that will just update runtime. #[test] +#[allow(deprecated)] fn inexistent_account_works() { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(0, 100), (1, 99), (2, 1)] } @@ -411,6 +429,7 @@ fn propose_bounty_validation_works() { } #[test] +#[allow(deprecated)] fn close_bounty_works() { ExtBuilder::default().build_and_execute(|| { Balances::make_free_balance_be(&Treasury::account_id(), 101); @@ -1181,3 +1200,212 @@ fn propose_curator_instance1_insufficient_spend_limit_errors() { ); }); } + +#[test] +fn approve_bounty_with_curator_works() { + ExtBuilder::default().build_and_execute(|| { + let fee = 10; + let curator = 4; + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_noop!( + Bounties::approve_bounty_with_curator(RuntimeOrigin::signed(1), 0, curator, 10), + BadOrigin + ); + + SpendLimit::set(1); + assert_noop!( + Bounties::approve_bounty_with_curator(RuntimeOrigin::root(), 0, curator, 10), + TreasuryError::InsufficientPermission + ); + SpendLimit::set(u64::MAX); + + assert_noop!( + Bounties::approve_bounty_with_curator(RuntimeOrigin::root(), 0, curator, 51), + Error::::InvalidFee + ); + + assert_eq!(pallet_bounties::BountyApprovals::::get().len(), 0); + assert_ok!(Bounties::approve_bounty_with_curator(RuntimeOrigin::root(), 0, curator, 10)); + assert_eq!(pallet_bounties::BountyApprovals::::get().len(), 1); + + assert_eq!( + pallet_bounties::Bounties::::get(0).unwrap(), + Bounty { + proposer: 0, + fee, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::ApprovedWithCurator { curator }, + } + ); + + expect_events(vec![ + BountiesEvent::BountyApproved { index: 0 }, + BountiesEvent::CuratorProposed { bounty_id: 0, curator }, + ]); + + assert_noop!( + Bounties::approve_bounty_with_curator(RuntimeOrigin::root(), 0, curator, 10), + Error::::UnexpectedStatus + ); + + System::set_block_number(2); + >::on_initialize(2); + assert_eq!(pallet_bounties::BountyApprovals::::get().len(), 0); + + expect_events(vec![BountiesEvent::BountyBecameActive { index: 0 }]); + + assert_eq!( + pallet_bounties::Bounties::::get(0).unwrap(), + Bounty { + proposer: 0, + fee, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::CuratorProposed { curator }, + } + ); + + assert_noop!( + Bounties::accept_curator(RuntimeOrigin::signed(curator), 0), + pallet_balances::Error::::InsufficientBalance + ); + Balances::make_free_balance_be(&curator, 6); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(curator), 0)); + + assert_eq!( + pallet_bounties::Bounties::::get(0).unwrap(), + Bounty { + proposer: 0, + fee, + curator_deposit: 5, + value: 50, + bond: 85, + status: BountyStatus::Active { curator, update_due: 22 }, + } + ); + + assert_ok!(Bounties::award_bounty(RuntimeOrigin::signed(curator), 0, 5)); + System::set_block_number(5); + >::on_initialize(5); + assert_ok!(Bounties::claim_bounty(RuntimeOrigin::signed(curator), 0)); + assert_eq!( + last_event(), + BountiesEvent::BountyClaimed { index: 0, payout: 40, beneficiary: 5 } + ); + assert_eq!(Balances::free_balance(5), 40); // 50 - 10 + }); +} + +#[test] +fn approve_bounty_with_curator_early_unassign_works() { + ExtBuilder::default().build_and_execute(|| { + let fee = 10; + let curator = 4; + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::approve_bounty_with_curator(RuntimeOrigin::root(), 0, curator, 10)); + + // unassign curator while bounty is not yet funded + assert_ok!(Bounties::unassign_curator(RuntimeOrigin::root(), 0)); + + assert_eq!( + pallet_bounties::Bounties::::get(0).unwrap(), + Bounty { + proposer: 0, + fee, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::Approved, + } + ); + + assert_eq!(last_event(), BountiesEvent::CuratorUnassigned { bounty_id: 0 }); + + System::set_block_number(2); + >::on_initialize(2); + assert_eq!(last_event(), BountiesEvent::BountyBecameActive { index: 0 }); + assert_eq!( + pallet_bounties::Bounties::::get(0).unwrap(), + Bounty { + proposer: 0, + fee, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::Funded, + } + ); + + // assign curator again through separate process + let new_fee = 15; + let new_curator = 5; + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, new_curator, new_fee)); + + assert_eq!( + pallet_bounties::Bounties::::get(0).unwrap(), + Bounty { + proposer: 0, + fee: new_fee, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::CuratorProposed { curator: new_curator }, + } + ); + assert_eq!( + last_event(), + BountiesEvent::CuratorProposed { bounty_id: 0, curator: new_curator } + ); + }); +} + +#[test] +fn approve_bounty_with_curator_proposed_unassign_works() { + ExtBuilder::default().build_and_execute(|| { + let fee = 10; + let curator = 4; + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::approve_bounty_with_curator(RuntimeOrigin::root(), 0, curator, 10)); + + System::set_block_number(2); + >::on_initialize(2); + + assert_eq!( + pallet_bounties::Bounties::::get(0).unwrap(), + Bounty { + proposer: 0, + fee, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::CuratorProposed { curator }, + } + ); + + assert_ok!(Bounties::unassign_curator(RuntimeOrigin::signed(curator), 0)); + assert_eq!( + pallet_bounties::Bounties::::get(0).unwrap(), + Bounty { + proposer: 0, + fee, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::Funded, + } + ); + assert_eq!(last_event(), BountiesEvent::CuratorUnassigned { bounty_id: 0 }); + }); +} diff --git a/substrate/frame/bounties/src/weights.rs b/substrate/frame/bounties/src/weights.rs index c9f551ec9bb2421098b13eaa7ea4beecbdf13d70..7230fa4a6a77f4c90f400f9a995ee2c04f0fc5a5 100644 --- a/substrate/frame/bounties/src/weights.rs +++ b/substrate/frame/bounties/src/weights.rs @@ -18,27 +18,25 @@ //! Autogenerated weights for `pallet_bounties` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-04-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-22, 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-augrssgt-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_bounties -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --output=./substrate/frame/bounties/src/weights.rs +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_bounties +// --chain=dev // --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/bounties/src/weights.rs // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -54,6 +52,7 @@ pub trait WeightInfo { fn propose_bounty(d: u32, ) -> Weight; fn approve_bounty() -> Weight; fn propose_curator() -> Weight; + fn approve_bounty_with_curator() -> Weight; fn unassign_curator() -> Weight; fn accept_curator() -> Weight; fn award_bounty() -> Weight; @@ -78,12 +77,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `d` is `[0, 300]`. fn propose_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `309` + // Measured: `343` // Estimated: `3593` - // Minimum execution time: 25_206_000 picoseconds. - Weight::from_parts(26_925_800, 3593) - // Standard Error: 239 - .saturating_add(Weight::from_parts(501, 0).saturating_mul(d.into())) + // Minimum execution time: 31_284_000 picoseconds. + Weight::from_parts(33_484_932, 3593) + // Standard Error: 299 + .saturating_add(Weight::from_parts(1_444, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -93,10 +92,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) fn approve_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `401` + // Measured: `434` // Estimated: `3642` - // Minimum execution time: 13_150_000 picoseconds. - Weight::from_parts(13_708_000, 3642) + // Minimum execution time: 17_656_000 picoseconds. + Weight::from_parts(18_501_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -104,23 +103,36 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `421` + // Measured: `454` // Estimated: `3642` - // Minimum execution time: 12_277_000 picoseconds. - Weight::from_parts(12_769_000, 3642) + // Minimum execution time: 15_416_000 picoseconds. + Weight::from_parts(16_463_000, 3642) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Bounties::Bounties` (r:1 w:1) /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + fn approve_bounty_with_curator() -> Weight { + // Proof Size summary in bytes: + // Measured: `454` + // Estimated: `3642` + // Minimum execution time: 21_802_000 picoseconds. + Weight::from_parts(22_884_000, 3642) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `597` + // Measured: `630` // Estimated: `3642` - // Minimum execution time: 29_041_000 picoseconds. - Weight::from_parts(29_979_000, 3642) + // Minimum execution time: 45_843_000 picoseconds. + Weight::from_parts(47_558_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -130,10 +142,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `593` + // Measured: `626` // Estimated: `3642` - // Minimum execution time: 27_936_000 picoseconds. - Weight::from_parts(28_925_000, 3642) + // Minimum execution time: 35_720_000 picoseconds. + Weight::from_parts(37_034_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -143,10 +155,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) fn award_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `605` + // Measured: `638` // Estimated: `3642` - // Minimum execution time: 16_759_000 picoseconds. - Weight::from_parts(17_699_000, 3642) + // Minimum execution time: 23_318_000 picoseconds. + Weight::from_parts(24_491_000, 3642) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -160,10 +172,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn claim_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `969` + // Measured: `1069` // Estimated: `8799` - // Minimum execution time: 112_056_000 picoseconds. - Weight::from_parts(114_275_000, 8799) + // Minimum execution time: 127_643_000 picoseconds. + Weight::from_parts(130_844_000, 8799) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -177,10 +189,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: - // Measured: `649` + // Measured: `683` // Estimated: `3642` - // Minimum execution time: 32_625_000 picoseconds. - Weight::from_parts(33_719_000, 3642) + // Minimum execution time: 49_963_000 picoseconds. + Weight::from_parts(51_484_000, 3642) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -194,10 +206,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `885` + // Measured: `985` // Estimated: `6196` - // Minimum execution time: 76_895_000 picoseconds. - Weight::from_parts(79_161_000, 6196) + // Minimum execution time: 89_310_000 picoseconds. + Weight::from_parts(92_223_000, 6196) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -205,10 +217,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn extend_bounty_expiry() -> Weight { // Proof Size summary in bytes: - // Measured: `457` + // Measured: `490` // Estimated: `3642` - // Minimum execution time: 12_635_000 picoseconds. - Weight::from_parts(13_423_000, 3642) + // Minimum execution time: 16_630_000 picoseconds. + Weight::from_parts(17_171_000, 3642) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -221,12 +233,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `b` is `[0, 100]`. fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `37 + b * (297 ยฑ0)` + // Measured: `205 + b * (297 ยฑ0)` // Estimated: `1887 + b * (5206 ยฑ0)` - // Minimum execution time: 2_840_000 picoseconds. - Weight::from_parts(6_076_743, 1887) - // Standard Error: 18_569 - .saturating_add(Weight::from_parts(34_771_846, 0).saturating_mul(b.into())) + // Minimum execution time: 4_334_000 picoseconds. + Weight::from_parts(1_256_424, 1887) + // Standard Error: 42_406 + .saturating_add(Weight::from_parts(36_979_844, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -248,12 +260,12 @@ impl WeightInfo for () { /// The range of component `d` is `[0, 300]`. fn propose_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `309` + // Measured: `343` // Estimated: `3593` - // Minimum execution time: 25_206_000 picoseconds. - Weight::from_parts(26_925_800, 3593) - // Standard Error: 239 - .saturating_add(Weight::from_parts(501, 0).saturating_mul(d.into())) + // Minimum execution time: 31_284_000 picoseconds. + Weight::from_parts(33_484_932, 3593) + // Standard Error: 299 + .saturating_add(Weight::from_parts(1_444, 0).saturating_mul(d.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -263,10 +275,10 @@ impl WeightInfo for () { /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) fn approve_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `401` + // Measured: `434` // Estimated: `3642` - // Minimum execution time: 13_150_000 picoseconds. - Weight::from_parts(13_708_000, 3642) + // Minimum execution time: 17_656_000 picoseconds. + Weight::from_parts(18_501_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -274,23 +286,36 @@ impl WeightInfo for () { /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `421` + // Measured: `454` // Estimated: `3642` - // Minimum execution time: 12_277_000 picoseconds. - Weight::from_parts(12_769_000, 3642) + // Minimum execution time: 15_416_000 picoseconds. + Weight::from_parts(16_463_000, 3642) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Bounties::Bounties` (r:1 w:1) /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + fn approve_bounty_with_curator() -> Weight { + // Proof Size summary in bytes: + // Measured: `454` + // Estimated: `3642` + // Minimum execution time: 21_802_000 picoseconds. + Weight::from_parts(22_884_000, 3642) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn unassign_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `597` + // Measured: `630` // Estimated: `3642` - // Minimum execution time: 29_041_000 picoseconds. - Weight::from_parts(29_979_000, 3642) + // Minimum execution time: 45_843_000 picoseconds. + Weight::from_parts(47_558_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -300,10 +325,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `593` + // Measured: `626` // Estimated: `3642` - // Minimum execution time: 27_936_000 picoseconds. - Weight::from_parts(28_925_000, 3642) + // Minimum execution time: 35_720_000 picoseconds. + Weight::from_parts(37_034_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -313,10 +338,10 @@ impl WeightInfo for () { /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) fn award_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `605` + // Measured: `638` // Estimated: `3642` - // Minimum execution time: 16_759_000 picoseconds. - Weight::from_parts(17_699_000, 3642) + // Minimum execution time: 23_318_000 picoseconds. + Weight::from_parts(24_491_000, 3642) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -330,10 +355,10 @@ impl WeightInfo for () { /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn claim_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `969` + // Measured: `1069` // Estimated: `8799` - // Minimum execution time: 112_056_000 picoseconds. - Weight::from_parts(114_275_000, 8799) + // Minimum execution time: 127_643_000 picoseconds. + Weight::from_parts(130_844_000, 8799) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -347,10 +372,10 @@ impl WeightInfo for () { /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: - // Measured: `649` + // Measured: `683` // Estimated: `3642` - // Minimum execution time: 32_625_000 picoseconds. - Weight::from_parts(33_719_000, 3642) + // Minimum execution time: 49_963_000 picoseconds. + Weight::from_parts(51_484_000, 3642) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -364,10 +389,10 @@ impl WeightInfo for () { /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(314), added: 2789, mode: `MaxEncodedLen`) fn close_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `885` + // Measured: `985` // Estimated: `6196` - // Minimum execution time: 76_895_000 picoseconds. - Weight::from_parts(79_161_000, 6196) + // Minimum execution time: 89_310_000 picoseconds. + Weight::from_parts(92_223_000, 6196) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -375,10 +400,10 @@ impl WeightInfo for () { /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) fn extend_bounty_expiry() -> Weight { // Proof Size summary in bytes: - // Measured: `457` + // Measured: `490` // Estimated: `3642` - // Minimum execution time: 12_635_000 picoseconds. - Weight::from_parts(13_423_000, 3642) + // Minimum execution time: 16_630_000 picoseconds. + Weight::from_parts(17_171_000, 3642) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -391,12 +416,12 @@ impl WeightInfo for () { /// The range of component `b` is `[0, 100]`. fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `37 + b * (297 ยฑ0)` + // Measured: `205 + b * (297 ยฑ0)` // Estimated: `1887 + b * (5206 ยฑ0)` - // Minimum execution time: 2_840_000 picoseconds. - Weight::from_parts(6_076_743, 1887) - // Standard Error: 18_569 - .saturating_add(Weight::from_parts(34_771_846, 0).saturating_mul(b.into())) + // Minimum execution time: 4_334_000 picoseconds. + Weight::from_parts(1_256_424, 1887) + // Standard Error: 42_406 + .saturating_add(Weight::from_parts(36_979_844, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) diff --git a/substrate/frame/child-bounties/src/benchmarking.rs b/substrate/frame/child-bounties/src/benchmarking.rs index 68e99e21a45653f4b402f2093153a4c4f6bf8605..67074f90cbf6030c0bdc7c8dc7cfe6529d917a50 100644 --- a/substrate/frame/child-bounties/src/benchmarking.rs +++ b/substrate/frame/child-bounties/src/benchmarking.rs @@ -151,7 +151,7 @@ fn activate_child_bounty( bounty_setup.reason.clone(), )?; - bounty_setup.child_bounty_id = ChildBountyCount::::get() - 1; + bounty_setup.child_bounty_id = ParentTotalChildBounties::::get(bounty_setup.bounty_id) - 1; ChildBounties::::propose_curator( RawOrigin::Signed(bounty_setup.curator.clone()).into(), @@ -205,7 +205,7 @@ benchmarks! { bounty_setup.child_bounty_value, bounty_setup.reason.clone(), )?; - let child_bounty_id = ChildBountyCount::::get() - 1; + let child_bounty_id = ParentTotalChildBounties::::get(bounty_setup.bounty_id) - 1; }: _(RawOrigin::Signed(bounty_setup.curator), bounty_setup.bounty_id, child_bounty_id, child_curator_lookup, bounty_setup.child_bounty_fee) @@ -221,7 +221,7 @@ benchmarks! { bounty_setup.child_bounty_value, bounty_setup.reason.clone(), )?; - bounty_setup.child_bounty_id = ChildBountyCount::::get() - 1; + bounty_setup.child_bounty_id = ParentTotalChildBounties::::get(bounty_setup.bounty_id) - 1; ChildBounties::::propose_curator( RawOrigin::Signed(bounty_setup.curator.clone()).into(), @@ -296,7 +296,7 @@ benchmarks! { bounty_setup.child_bounty_value, bounty_setup.reason.clone(), )?; - bounty_setup.child_bounty_id = ChildBountyCount::::get() - 1; + bounty_setup.child_bounty_id = ParentTotalChildBounties::::get(bounty_setup.bounty_id) - 1; }: close_child_bounty(RawOrigin::Root, bounty_setup.bounty_id, bounty_setup.child_bounty_id) diff --git a/substrate/frame/child-bounties/src/lib.rs b/substrate/frame/child-bounties/src/lib.rs index 1e970b6ae67c3f012755f468e03ba3909702181d..ea1d9547d465191b78a83b9c242d386ef41c91cf 100644 --- a/substrate/frame/child-bounties/src/lib.rs +++ b/substrate/frame/child-bounties/src/lib.rs @@ -53,11 +53,15 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; +pub mod migration; mod tests; pub mod weights; extern crate alloc; +/// The log target for this pallet. +const LOG_TARGET: &str = "runtime::child-bounties"; + use alloc::vec::Vec; use frame_support::traits::{ @@ -134,7 +138,11 @@ pub mod pallet { use super::*; + /// The in-code storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] @@ -184,16 +192,22 @@ pub mod pallet { Canceled { index: BountyIndex, child_index: BountyIndex }, } - /// Number of total child bounties. + /// DEPRECATED: Replaced with `ParentTotalChildBounties` storage item keeping dedicated counts + /// for each parent bounty. Number of total child bounties. Will be removed in May 2025. #[pallet::storage] pub type ChildBountyCount = StorageValue<_, BountyIndex, ValueQuery>; - /// Number of child bounties per parent bounty. + /// Number of active child bounties per parent bounty. /// Map of parent bounty index to number of child bounties. #[pallet::storage] pub type ParentChildBounties = StorageMap<_, Twox64Concat, BountyIndex, u32, ValueQuery>; + /// Number of total child bounties per parent bounty, including completed bounties. + #[pallet::storage] + pub type ParentTotalChildBounties = + StorageMap<_, Twox64Concat, BountyIndex, u32, ValueQuery>; + /// Child bounties that have been added. #[pallet::storage] pub type ChildBounties = StorageDoubleMap< @@ -205,10 +219,27 @@ pub mod pallet { ChildBounty, BlockNumberFor>, >; - /// The description of each child-bounty. + /// The description of each child-bounty. Indexed by `(parent_id, child_id)`. + /// + /// This item replaces the `ChildBountyDescriptions` storage item from the V0 storage version. + #[pallet::storage] + pub type ChildBountyDescriptionsV1 = StorageDoubleMap< + _, + Twox64Concat, + BountyIndex, + Twox64Concat, + BountyIndex, + BoundedVec, + >; + + /// The mapping of the child bounty ids from storage version `V0` to the new `V1` version. + /// + /// The `V0` ids based on total child bounty count [`ChildBountyCount`]`. The `V1` version ids + /// based on the child bounty count per parent bounty [`ParentTotalChildBounties`]. + /// The item intended solely for client convenience and not used in the pallet's core logic. #[pallet::storage] - pub type ChildBountyDescriptions = - StorageMap<_, Twox64Concat, BountyIndex, BoundedVec>; + pub type V0ToV1ChildBountyIds = + StorageMap<_, Twox64Concat, BountyIndex, (BountyIndex, BountyIndex)>; /// The cumulative child-bounty curator fee for each parent bounty. #[pallet::storage] @@ -276,15 +307,19 @@ pub mod pallet { )?; // Get child-bounty ID. - let child_bounty_id = ChildBountyCount::::get(); - let child_bounty_account = Self::child_bounty_account_id(child_bounty_id); + let child_bounty_id = ParentTotalChildBounties::::get(parent_bounty_id); + let child_bounty_account = + Self::child_bounty_account_id(parent_bounty_id, child_bounty_id); // Transfer funds from parent bounty to child-bounty. T::Currency::transfer(&parent_bounty_account, &child_bounty_account, value, KeepAlive)?; // Increment the active child-bounty count. ParentChildBounties::::mutate(parent_bounty_id, |count| count.saturating_inc()); - ChildBountyCount::::put(child_bounty_id.saturating_add(1)); + ParentTotalChildBounties::::insert( + parent_bounty_id, + child_bounty_id.saturating_add(1), + ); // Create child-bounty instance. Self::create_child_bounty( @@ -672,7 +707,8 @@ pub mod pallet { ); // Make curator fee payment. - let child_bounty_account = Self::child_bounty_account_id(child_bounty_id); + let child_bounty_account = + Self::child_bounty_account_id(parent_bounty_id, child_bounty_id); let balance = T::Currency::free_balance(&child_bounty_account); let curator_fee = child_bounty.fee.min(balance); let payout = balance.saturating_sub(curator_fee); @@ -716,7 +752,7 @@ pub mod pallet { }); // Remove the child-bounty description. - ChildBountyDescriptions::::remove(child_bounty_id); + ChildBountyDescriptionsV1::::remove(parent_bounty_id, child_bounty_id); // Remove the child-bounty instance from the state. *maybe_child_bounty = None; @@ -772,6 +808,19 @@ pub mod pallet { Ok(()) } } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn integrity_test() { + let parent_bounty_id: BountyIndex = 1; + let child_bounty_id: BountyIndex = 2; + let _: T::AccountId = T::PalletId::get() + .try_into_sub_account(("cb", parent_bounty_id, child_bounty_id)) + .expect( + "The `AccountId` type must be large enough to fit the child bounty account ID.", + ); + } + } } impl Pallet { @@ -797,11 +846,14 @@ impl Pallet { } /// The account ID of a child-bounty account. - pub fn child_bounty_account_id(id: BountyIndex) -> T::AccountId { + pub fn child_bounty_account_id( + parent_bounty_id: BountyIndex, + child_bounty_id: BountyIndex, + ) -> T::AccountId { // This function is taken from the parent (bounties) pallet, but the // prefix is changed to have different AccountId when the index of // parent and child is same. - T::PalletId::get().into_sub_account_truncating(("cb", id)) + T::PalletId::get().into_sub_account_truncating(("cb", parent_bounty_id, child_bounty_id)) } fn create_child_bounty( @@ -818,7 +870,7 @@ impl Pallet { status: ChildBountyStatus::Added, }; ChildBounties::::insert(parent_bounty_id, child_bounty_id, &child_bounty); - ChildBountyDescriptions::::insert(child_bounty_id, description); + ChildBountyDescriptionsV1::::insert(parent_bounty_id, child_bounty_id, description); Self::deposit_event(Event::Added { index: parent_bounty_id, child_index: child_bounty_id }); } @@ -877,7 +929,8 @@ impl Pallet { // Transfer fund from child-bounty to parent bounty. let parent_bounty_account = pallet_bounties::Pallet::::bounty_account_id(parent_bounty_id); - let child_bounty_account = Self::child_bounty_account_id(child_bounty_id); + let child_bounty_account = + Self::child_bounty_account_id(parent_bounty_id, child_bounty_id); let balance = T::Currency::free_balance(&child_bounty_account); let transfer_result = T::Currency::transfer( &child_bounty_account, @@ -888,7 +941,7 @@ impl Pallet { debug_assert!(transfer_result.is_ok()); // Remove the child-bounty description. - ChildBountyDescriptions::::remove(child_bounty_id); + ChildBountyDescriptionsV1::::remove(parent_bounty_id, child_bounty_id); *maybe_child_bounty = None; @@ -902,16 +955,22 @@ impl Pallet { } } -// Implement ChildBountyManager to connect with the bounties pallet. This is -// where we pass the active child bounties and child curator fees to the parent -// bounty. +/// Implement ChildBountyManager to connect with the bounties pallet. This is +/// where we pass the active child bounties and child curator fees to the parent +/// bounty. +/// +/// Function `children_curator_fees` not only returns the fee but also removes cumulative curator +/// fees during call. impl pallet_bounties::ChildBountyManager> for Pallet { + /// Returns number of active child bounties for `bounty_id` fn child_bounties_count( bounty_id: pallet_bounties::BountyIndex, ) -> pallet_bounties::BountyIndex { ParentChildBounties::::get(bounty_id) } + /// Returns cumulative child bounty curator fees for `bounty_id` also removing the associated + /// storage item. This function is assumed to be called when parent bounty is claimed. fn children_curator_fees(bounty_id: pallet_bounties::BountyIndex) -> BalanceOf { // This is asked for when the parent bounty is being claimed. No use of // keeping it in state after that. Hence removing. @@ -919,4 +978,14 @@ impl pallet_bounties::ChildBountyManager> for Pallet ChildrenCuratorFees::::remove(bounty_id); children_fee_total } + + /// Clean up the storage on a parent bounty removal. + fn bounty_removed(bounty_id: BountyIndex) { + debug_assert!(ParentChildBounties::::get(bounty_id).is_zero()); + debug_assert!(ChildrenCuratorFees::::get(bounty_id).is_zero()); + debug_assert!(ChildBounties::::iter_key_prefix(bounty_id).count().is_zero()); + debug_assert!(ChildBountyDescriptionsV1::::iter_key_prefix(bounty_id).count().is_zero()); + ParentChildBounties::::remove(bounty_id); + ParentTotalChildBounties::::remove(bounty_id); + } } diff --git a/substrate/frame/child-bounties/src/migration.rs b/substrate/frame/child-bounties/src/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..52232a5a7f2fc60b01c0d5ad03c3bb293fe6874d --- /dev/null +++ b/substrate/frame/child-bounties/src/migration.rs @@ -0,0 +1,229 @@ +// 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 core::marker::PhantomData; +use frame_support::{ + storage_alias, + traits::{Get, UncheckedOnRuntimeUpgrade}, +}; + +use alloc::collections::BTreeSet; +#[cfg(feature = "try-runtime")] +use alloc::vec::Vec; +#[cfg(feature = "try-runtime")] +use frame_support::ensure; + +pub mod v1 { + use super::*; + + /// Creates a new ids for the child balances based on the child bounty count per parent bounty + /// instead of the total child bounty count. Translates the existing child bounties to the new + /// ids. Creates the `V0ToV1ChildBountyIds` map from `old_child_id` to new (`parent_id`, + /// `new_child_id`). + /// + /// `TransferWeight` returns `Weight` of `T::Currency::transfer` and `T::Currency::free_balance` + /// operation which is performed during this migration. + pub struct MigrateToV1Impl(PhantomData<(T, TransferWeight)>); + + #[storage_alias] + type ChildBountyDescriptions = StorageMap< + Pallet, + Twox64Concat, + BountyIndex, + BoundedVec::MaximumReasonLength>, + >; + + impl> UncheckedOnRuntimeUpgrade + for MigrateToV1Impl + { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + // increment reads/writes after the action + let mut reads = 0u64; + let mut writes = 0u64; + let mut transfer_weights: Weight = Weight::zero(); + + // keep ids order roughly the same with the old order + let mut old_bounty_ids = BTreeSet::new(); + // first iteration collect all existing ids not to mutate map as we iterate it + for (parent_bounty_id, old_child_bounty_id) in ChildBounties::::iter_keys() { + reads += 1; + old_bounty_ids.insert((parent_bounty_id, old_child_bounty_id)); + } + + log::info!( + target: LOG_TARGET, + "Migrating {} child bounties", + old_bounty_ids.len(), + ); + + for (parent_bounty_id, old_child_bounty_id) in old_bounty_ids { + // assign new child bounty id + let new_child_bounty_id = ParentTotalChildBounties::::get(parent_bounty_id); + reads += 1; + ParentTotalChildBounties::::insert( + parent_bounty_id, + new_child_bounty_id.saturating_add(1), + ); + writes += 1; + + V0ToV1ChildBountyIds::::insert( + old_child_bounty_id, + (parent_bounty_id, new_child_bounty_id), + ); + writes += 1; + + let old_child_bounty_account = + Self::old_child_bounty_account_id(old_child_bounty_id); + let new_child_bounty_account = + Pallet::::child_bounty_account_id(parent_bounty_id, new_child_bounty_id); + let old_balance = T::Currency::free_balance(&old_child_bounty_account); + log::info!( + "Transferring {:?} funds from old child bounty account {:?} to new child bounty account {:?}", + old_balance, old_child_bounty_account, new_child_bounty_account + ); + if let Err(err) = T::Currency::transfer( + &old_child_bounty_account, + &new_child_bounty_account, + old_balance, + AllowDeath, + ) { + log::error!( + target: LOG_TARGET, + "Error transferring funds: {:?}", + err + ); + } + transfer_weights += TransferWeight::get(); + + log::info!( + target: LOG_TARGET, + "Remapped parent bounty {} child bounty id {}->{}", + parent_bounty_id, + old_child_bounty_id, + new_child_bounty_id, + ); + + let bounty_description = ChildBountyDescriptions::::take(old_child_bounty_id); + writes += 1; + let child_bounty = ChildBounties::::take(parent_bounty_id, old_child_bounty_id); + writes += 1; + + // should always be some + if let Some(taken) = child_bounty { + ChildBounties::::insert(parent_bounty_id, new_child_bounty_id, taken); + writes += 1; + } else { + log::error!( + "child bounty with old id {} not found, should be impossible", + old_child_bounty_id + ); + } + if let Some(bounty_description) = bounty_description { + super::super::ChildBountyDescriptionsV1::::insert( + parent_bounty_id, + new_child_bounty_id, + bounty_description, + ); + writes += 1; + } else { + log::error!( + "child bounty description with old id {} not found, should be impossible", + old_child_bounty_id + ); + } + } + + log::info!( + target: LOG_TARGET, + "Migration done, reads: {}, writes: {}, transfer weights: {}", + reads, writes, transfer_weights + ); + + T::DbWeight::get().reads_writes(reads, writes) + transfer_weights + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + let old_child_bounty_count = ChildBounties::::iter_keys().count() as u32; + let old_child_bounty_descriptions = + v1::ChildBountyDescriptions::::iter_keys().count() as u32; + let old_child_bounty_ids = ChildBounties::::iter_keys().collect::>(); + Ok((old_child_bounty_count, old_child_bounty_descriptions, old_child_bounty_ids) + .encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + type StateType = (u32, u32, Vec<(u32, u32)>); + let (old_child_bounty_count, old_child_bounty_descriptions, old_child_bounty_ids) = + StateType::decode(&mut &state[..]).expect("Can't decode previous state"); + let new_child_bounty_count = ChildBounties::::iter_keys().count() as u32; + let new_child_bounty_descriptions = + super::super::ChildBountyDescriptionsV1::::iter_keys().count() as u32; + + ensure!( + old_child_bounty_count == new_child_bounty_count, + "child bounty count doesn't match" + ); + ensure!( + old_child_bounty_descriptions == new_child_bounty_descriptions, + "child bounty descriptions count doesn't match" + ); + + let old_child_bounty_descriptions_storage = + v1::ChildBountyDescriptions::::iter_keys().count(); + log::info!("old child bounty descriptions: {}", old_child_bounty_descriptions_storage); + ensure!( + old_child_bounty_descriptions_storage == 0, + "Old bounty descriptions should have been drained." + ); + + for (_, old_child_bounty_id) in old_child_bounty_ids { + let old_account_id = Self::old_child_bounty_account_id(old_child_bounty_id); + let balance = T::Currency::total_balance(&old_account_id); + if !balance.is_zero() { + log::error!( + "Old child bounty id {} still has balance {:?}", + old_child_bounty_id, + balance + ); + } + } + + Ok(()) + } + } + + impl> MigrateToV1Impl { + fn old_child_bounty_account_id(id: BountyIndex) -> T::AccountId { + // This function is taken from the parent (bounties) pallet, but the + // prefix is changed to have different AccountId when the index of + // parent and child is same. + T::PalletId::get().into_sub_account_truncating(("cb", id)) + } + } +} + +/// Migrate the pallet storage from `0` to `1`. +pub type MigrateV0ToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + v1::MigrateToV1Impl, + Pallet, + ::DbWeight, +>; diff --git a/substrate/frame/child-bounties/src/tests.rs b/substrate/frame/child-bounties/src/tests.rs index 96d01b03560d09d7087cd3cdddb464fd2c015609..939983054f667dfe73cd92ec8b6e59ae293373fb 100644 --- a/substrate/frame/child-bounties/src/tests.rs +++ b/substrate/frame/child-bounties/src/tests.rs @@ -66,10 +66,16 @@ parameter_types! { } type Balance = u64; +// must be at least 20 bytes long because of child-bounty account derivation. +type AccountId = sp_core::U256; + +fn account_id(id: u8) -> AccountId { + sp_core::U256::from(id) +} #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { - type AccountId = u128; + type AccountId = AccountId; type Lookup = IdentityLookup; type Block = Block; type AccountData = pallet_balances::AccountData; @@ -82,14 +88,14 @@ impl pallet_balances::Config for Test { parameter_types! { pub const Burn: Permill = Permill::from_percent(50); pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); - pub TreasuryAccount: u128 = Treasury::account_id(); + pub TreasuryAccount: AccountId = Treasury::account_id(); pub const SpendLimit: Balance = u64::MAX; } impl pallet_treasury::Config for Test { type PalletId = TreasuryPalletId; type Currency = pallet_balances::Pallet; - type RejectOrigin = frame_system::EnsureRoot; + type RejectOrigin = frame_system::EnsureRoot; type RuntimeEvent = RuntimeEvent; type SpendPeriod = ConstU64<2>; type Burn = Burn; @@ -141,7 +147,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { // Total issuance will be 200 with treasury account initialized at ED. - balances: vec![(0, 100), (1, 98), (2, 1)], + balances: vec![(account_id(0), 100), (account_id(1), 98), (account_id(2), 1)], } .assimilate_storage(&mut t) .unwrap(); @@ -161,6 +167,7 @@ fn last_event() -> ChildBountiesEvent { } #[test] +#[allow(deprecated)] fn genesis_config_works() { new_test_ext().execute_with(|| { assert_eq!(Treasury::pot(), 0); @@ -194,52 +201,71 @@ fn add_child_bounty() { go_to_block(1); Balances::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); let fee = 8; - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, fee)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), fee)); - Balances::make_free_balance_be(&4, 10); + Balances::make_free_balance_be(&account_id(4), 10); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // This verifies that the accept curator logic took a deposit. let expected_deposit = CuratorDepositMultiplier::get() * fee; - assert_eq!(Balances::reserved_balance(&4), expected_deposit); - assert_eq!(Balances::free_balance(&4), 10 - expected_deposit); + assert_eq!(Balances::reserved_balance(&account_id(4)), expected_deposit); + assert_eq!(Balances::free_balance(&account_id(4)), 10 - expected_deposit); // Add child-bounty. // Acc-4 is the parent curator. // Call from invalid origin & check for error "RequireCurator". assert_noop!( - ChildBounties::add_child_bounty(RuntimeOrigin::signed(0), 0, 10, b"12345-p1".to_vec()), + ChildBounties::add_child_bounty( + RuntimeOrigin::signed(account_id(0)), + 0, + 10, + b"12345-p1".to_vec() + ), BountiesError::RequireCurator, ); // Update the parent curator balance. - Balances::make_free_balance_be(&4, 101); + Balances::make_free_balance_be(&account_id(4), 101); // parent curator fee is reserved on parent bounty account. assert_eq!(Balances::free_balance(Bounties::bounty_account_id(0)), 50); assert_eq!(Balances::reserved_balance(Bounties::bounty_account_id(0)), 0); assert_noop!( - ChildBounties::add_child_bounty(RuntimeOrigin::signed(4), 0, 50, b"12345-p1".to_vec()), + ChildBounties::add_child_bounty( + RuntimeOrigin::signed(account_id(4)), + 0, + 50, + b"12345-p1".to_vec() + ), TokenError::NotExpendable, ); assert_noop!( - ChildBounties::add_child_bounty(RuntimeOrigin::signed(4), 0, 100, b"12345-p1".to_vec()), + ChildBounties::add_child_bounty( + RuntimeOrigin::signed(account_id(4)), + 0, + 100, + b"12345-p1".to_vec() + ), Error::::InsufficientBountyBalance, ); // Add child-bounty with valid value, which can be funded by parent bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -248,8 +274,8 @@ fn add_child_bounty() { // Check for the event child-bounty added. assert_eq!(last_event(), ChildBountiesEvent::Added { index: 0, child_index: 0 }); - assert_eq!(Balances::free_balance(4), 101); - assert_eq!(Balances::reserved_balance(4), expected_deposit); + assert_eq!(Balances::free_balance(account_id(4)), 101); + assert_eq!(Balances::reserved_balance(account_id(4)), expected_deposit); // DB check. // Check the child-bounty status. @@ -269,7 +295,7 @@ fn add_child_bounty() { // Check the child-bounty description status. assert_eq!( - pallet_child_bounties::ChildBountyDescriptions::::get(0).unwrap(), + pallet_child_bounties::ChildBountyDescriptionsV1::::get(0, 0).unwrap(), b"12345-p1".to_vec(), ); }); @@ -286,18 +312,22 @@ fn child_bounty_assign_curator() { // Make the parent bounty. go_to_block(1); Balances::make_free_balance_be(&Treasury::account_id(), 101); - Balances::make_free_balance_be(&4, 101); - Balances::make_free_balance_be(&8, 101); + Balances::make_free_balance_be(&account_id(4), 101); + Balances::make_free_balance_be(&account_id(8), 101); - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); let fee = 4; - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, fee)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), fee)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Bounty account status before adding child-bounty. assert_eq!(Balances::free_balance(Bounties::bounty_account_id(0)), 50); @@ -306,13 +336,13 @@ fn child_bounty_assign_curator() { // Check the balance of parent curator. // Curator deposit is reserved for parent curator on parent bounty. let expected_deposit = Bounties::calculate_curator_deposit(&fee); - assert_eq!(Balances::free_balance(4), 101 - expected_deposit); - assert_eq!(Balances::reserved_balance(4), expected_deposit); + assert_eq!(Balances::free_balance(account_id(4)), 101 - expected_deposit); + assert_eq!(Balances::reserved_balance(account_id(4)), expected_deposit); // Add child-bounty. // Acc-4 is the parent curator & make sure enough deposit. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -325,11 +355,17 @@ fn child_bounty_assign_curator() { assert_eq!(Balances::reserved_balance(Bounties::bounty_account_id(0)), 0); // Child-bounty account status. - assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0)), 10); - assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0)), 0); + assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0, 0)), 10); + assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0, 0)), 0); let fee = 6u64; - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 8, fee)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(8), + fee + )); assert_eq!( pallet_child_bounties::ChildBounties::::get(0, 0).unwrap(), @@ -338,20 +374,20 @@ fn child_bounty_assign_curator() { value: 10, fee, curator_deposit: 0, - status: ChildBountyStatus::CuratorProposed { curator: 8 }, + status: ChildBountyStatus::CuratorProposed { curator: account_id(8) }, } ); // Check the balance of parent curator. - assert_eq!(Balances::free_balance(4), 101 - expected_deposit); - assert_eq!(Balances::reserved_balance(4), expected_deposit); + assert_eq!(Balances::free_balance(account_id(4)), 101 - expected_deposit); + assert_eq!(Balances::reserved_balance(account_id(4)), expected_deposit); assert_noop!( - ChildBounties::accept_curator(RuntimeOrigin::signed(3), 0, 0), + ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(3)), 0, 0), BountiesError::RequireCurator, ); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(8), 0, 0)); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(8)), 0, 0)); let expected_child_deposit = CuratorDepositMultiplier::get() * fee; @@ -362,21 +398,21 @@ fn child_bounty_assign_curator() { value: 10, fee, curator_deposit: expected_child_deposit, - status: ChildBountyStatus::Active { curator: 8 }, + status: ChildBountyStatus::Active { curator: account_id(8) }, } ); // Deposit for child-bounty curator deposit is reserved. - assert_eq!(Balances::free_balance(8), 101 - expected_child_deposit); - assert_eq!(Balances::reserved_balance(8), expected_child_deposit); + assert_eq!(Balances::free_balance(account_id(8)), 101 - expected_child_deposit); + assert_eq!(Balances::reserved_balance(account_id(8)), expected_child_deposit); // Bounty account status at exit. assert_eq!(Balances::free_balance(Bounties::bounty_account_id(0)), 40); assert_eq!(Balances::reserved_balance(Bounties::bounty_account_id(0)), 0); // Child-bounty account status at exit. - assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0)), 10); - assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0)), 0); + assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0, 0)), 10); + assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0, 0)), 0); // Treasury account status at exit. assert_eq!(Balances::free_balance(Treasury::account_id()), 26); @@ -394,21 +430,25 @@ fn award_claim_child_bounty() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), 6)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -418,17 +458,33 @@ fn award_claim_child_bounty() { // Propose and accept curator for child-bounty. let fee = 8; - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 8, fee)); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(8), 0, 0)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(8), + fee + )); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(8)), 0, 0)); // Award child-bounty. // Test for non child-bounty curator. assert_noop!( - ChildBounties::award_child_bounty(RuntimeOrigin::signed(3), 0, 0, 7), + ChildBounties::award_child_bounty( + RuntimeOrigin::signed(account_id(3)), + 0, + 0, + account_id(7) + ), BountiesError::RequireCurator, ); - assert_ok!(ChildBounties::award_child_bounty(RuntimeOrigin::signed(8), 0, 0, 7)); + assert_ok!(ChildBounties::award_child_bounty( + RuntimeOrigin::signed(account_id(8)), + 0, + 0, + account_id(7) + )); let expected_deposit = CuratorDepositMultiplier::get() * fee; assert_eq!( @@ -439,8 +495,8 @@ fn award_claim_child_bounty() { fee, curator_deposit: expected_deposit, status: ChildBountyStatus::PendingPayout { - curator: 8, - beneficiary: 7, + curator: account_id(8), + beneficiary: account_id(7), unlock_at: 5 }, } @@ -449,25 +505,25 @@ fn award_claim_child_bounty() { // Claim child-bounty. // Test for Premature condition. assert_noop!( - ChildBounties::claim_child_bounty(RuntimeOrigin::signed(7), 0, 0), + ChildBounties::claim_child_bounty(RuntimeOrigin::signed(account_id(7)), 0, 0), BountiesError::Premature ); go_to_block(9); - assert_ok!(ChildBounties::claim_child_bounty(RuntimeOrigin::signed(7), 0, 0)); + assert_ok!(ChildBounties::claim_child_bounty(RuntimeOrigin::signed(account_id(7)), 0, 0)); // Ensure child-bounty curator is paid with curator fee & deposit refund. - assert_eq!(Balances::free_balance(8), 101 + fee); - assert_eq!(Balances::reserved_balance(8), 0); + assert_eq!(Balances::free_balance(account_id(8)), 101 + fee); + assert_eq!(Balances::reserved_balance(account_id(8)), 0); // Ensure executor is paid with beneficiary amount. - assert_eq!(Balances::free_balance(7), 10 - fee); - assert_eq!(Balances::reserved_balance(7), 0); + assert_eq!(Balances::free_balance(account_id(7)), 10 - fee); + assert_eq!(Balances::reserved_balance(account_id(7)), 0); // Child-bounty account status. - assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0)), 0); - assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0)), 0); + assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0, 0)), 0); + assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0, 0)), 0); // Check the child-bounty count. assert_eq!(pallet_child_bounties::ParentChildBounties::::get(0), 0); @@ -484,22 +540,26 @@ fn close_child_bounty_added() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, 6)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -511,11 +571,17 @@ fn close_child_bounty_added() { // Close child-bounty. // Wrong origin. - assert_noop!(ChildBounties::close_child_bounty(RuntimeOrigin::signed(7), 0, 0), BadOrigin); - assert_noop!(ChildBounties::close_child_bounty(RuntimeOrigin::signed(8), 0, 0), BadOrigin); + assert_noop!( + ChildBounties::close_child_bounty(RuntimeOrigin::signed(account_id(7)), 0, 0), + BadOrigin + ); + assert_noop!( + ChildBounties::close_child_bounty(RuntimeOrigin::signed(account_id(8)), 0, 0), + BadOrigin + ); // Correct origin - parent curator. - assert_ok!(ChildBounties::close_child_bounty(RuntimeOrigin::signed(4), 0, 0)); + assert_ok!(ChildBounties::close_child_bounty(RuntimeOrigin::signed(account_id(4)), 0, 0)); // Check the child-bounty count. assert_eq!(pallet_child_bounties::ParentChildBounties::::get(0), 0); @@ -525,8 +591,8 @@ fn close_child_bounty_added() { assert_eq!(Balances::reserved_balance(Bounties::bounty_account_id(0)), 0); // Child-bounty account status. - assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0)), 0); - assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0)), 0); + assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0, 0)), 0); + assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0, 0)), 0); }); } @@ -540,22 +606,26 @@ fn close_child_bounty_active() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, 6)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -564,26 +634,32 @@ fn close_child_bounty_active() { assert_eq!(last_event(), ChildBountiesEvent::Added { index: 0, child_index: 0 }); // Propose and accept curator for child-bounty. - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 8, 2)); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(8), 0, 0)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(8), + 2 + )); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(8)), 0, 0)); // Close child-bounty in active state. - assert_ok!(ChildBounties::close_child_bounty(RuntimeOrigin::signed(4), 0, 0)); + assert_ok!(ChildBounties::close_child_bounty(RuntimeOrigin::signed(account_id(4)), 0, 0)); // Check the child-bounty count. assert_eq!(pallet_child_bounties::ParentChildBounties::::get(0), 0); // Ensure child-bounty curator balance is unreserved. - assert_eq!(Balances::free_balance(8), 101); - assert_eq!(Balances::reserved_balance(8), 0); + assert_eq!(Balances::free_balance(account_id(8)), 101); + assert_eq!(Balances::reserved_balance(account_id(8)), 0); // Parent-bounty account status. assert_eq!(Balances::free_balance(Bounties::bounty_account_id(0)), 50); assert_eq!(Balances::reserved_balance(Bounties::bounty_account_id(0)), 0); // Child-bounty account status. - assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0)), 0); - assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0)), 0); + assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0, 0)), 0); + assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0, 0)), 0); }); } @@ -597,22 +673,26 @@ fn close_child_bounty_pending() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); let parent_fee = 6; - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, parent_fee)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), parent_fee)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -622,15 +702,26 @@ fn close_child_bounty_pending() { // Propose and accept curator for child-bounty. let child_fee = 4; - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 8, child_fee)); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(8), 0, 0)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(8), + child_fee + )); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(8)), 0, 0)); let expected_child_deposit = CuratorDepositMin::get(); - assert_ok!(ChildBounties::award_child_bounty(RuntimeOrigin::signed(8), 0, 0, 7)); + assert_ok!(ChildBounties::award_child_bounty( + RuntimeOrigin::signed(account_id(8)), + 0, + 0, + account_id(7) + )); // Close child-bounty in pending_payout state. assert_noop!( - ChildBounties::close_child_bounty(RuntimeOrigin::signed(4), 0, 0), + ChildBounties::close_child_bounty(RuntimeOrigin::signed(account_id(4)), 0, 0), BountiesError::PendingPayout ); @@ -638,12 +729,12 @@ fn close_child_bounty_pending() { assert_eq!(pallet_child_bounties::ParentChildBounties::::get(0), 1); // Ensure no changes in child-bounty curator balance. - assert_eq!(Balances::reserved_balance(8), expected_child_deposit); - assert_eq!(Balances::free_balance(8), 101 - expected_child_deposit); + assert_eq!(Balances::reserved_balance(account_id(8)), expected_child_deposit); + assert_eq!(Balances::free_balance(account_id(8)), 101 - expected_child_deposit); // Child-bounty account status. - assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0)), 10); - assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0)), 0); + assert_eq!(Balances::free_balance(ChildBounties::child_bounty_account_id(0, 0)), 10); + assert_eq!(Balances::reserved_balance(ChildBounties::child_bounty_account_id(0, 0)), 0); }); } @@ -657,22 +748,26 @@ fn child_bounty_added_unassign_curator() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, 6)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -682,7 +777,7 @@ fn child_bounty_added_unassign_curator() { // Unassign curator in added state. assert_noop!( - ChildBounties::unassign_curator(RuntimeOrigin::signed(4), 0, 0), + ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(4)), 0, 0), BountiesError::UnexpectedStatus ); }); @@ -698,22 +793,26 @@ fn child_bounty_curator_proposed_unassign_curator() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, 6)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -722,7 +821,13 @@ fn child_bounty_curator_proposed_unassign_curator() { assert_eq!(last_event(), ChildBountiesEvent::Added { index: 0, child_index: 0 }); // Propose curator for child-bounty. - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 8, 2)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(8), + 2 + )); assert_eq!( pallet_child_bounties::ChildBounties::::get(0, 0).unwrap(), @@ -731,15 +836,18 @@ fn child_bounty_curator_proposed_unassign_curator() { value: 10, fee: 2, curator_deposit: 0, - status: ChildBountyStatus::CuratorProposed { curator: 8 }, + status: ChildBountyStatus::CuratorProposed { curator: account_id(8) }, } ); // Random account cannot unassign the curator when in proposed state. - assert_noop!(ChildBounties::unassign_curator(RuntimeOrigin::signed(99), 0, 0), BadOrigin); + assert_noop!( + ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(99)), 0, 0), + BadOrigin + ); // Unassign curator. - assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(4), 0, 0)); + assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(4)), 0, 0)); // Verify updated child-bounty status. assert_eq!( @@ -772,23 +880,27 @@ fn child_bounty_active_unassign_curator() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator. - Balances::make_free_balance_be(&6, 101); // Child-bounty curator 1. - Balances::make_free_balance_be(&7, 101); // Child-bounty curator 2. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator 3. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator. + Balances::make_free_balance_be(&account_id(6), 101); // Child-bounty curator 1. + Balances::make_free_balance_be(&account_id(7), 101); // Child-bounty curator 2. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator 3. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), 6)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Create Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -799,8 +911,14 @@ fn child_bounty_active_unassign_curator() { // Propose and accept curator for child-bounty. let fee = 6; - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 8, fee)); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(8), 0, 0)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(8), + fee + )); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(8)), 0, 0)); let expected_child_deposit = CuratorDepositMultiplier::get() * fee; assert_eq!( @@ -810,7 +928,7 @@ fn child_bounty_active_unassign_curator() { value: 10, fee, curator_deposit: expected_child_deposit, - status: ChildBountyStatus::Active { curator: 8 }, + status: ChildBountyStatus::Active { curator: account_id(8) }, } ); @@ -832,13 +950,19 @@ fn child_bounty_active_unassign_curator() { ); // Ensure child-bounty curator was slashed. - assert_eq!(Balances::free_balance(8), 101 - expected_child_deposit); - assert_eq!(Balances::reserved_balance(8), 0); // slashed + assert_eq!(Balances::free_balance(account_id(8)), 101 - expected_child_deposit); + assert_eq!(Balances::reserved_balance(account_id(8)), 0); // slashed // Propose and accept curator for child-bounty again. let fee = 2; - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 7, fee)); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(7), 0, 0)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(7), + fee + )); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(7)), 0, 0)); let expected_child_deposit = CuratorDepositMin::get(); assert_eq!( @@ -848,14 +972,14 @@ fn child_bounty_active_unassign_curator() { value: 10, fee, curator_deposit: expected_child_deposit, - status: ChildBountyStatus::Active { curator: 7 }, + status: ChildBountyStatus::Active { curator: account_id(7) }, } ); go_to_block(5); // Unassign curator again - from parent curator. - assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(4), 0, 0)); + assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(4)), 0, 0)); // Verify updated child-bounty status. assert_eq!( @@ -870,12 +994,18 @@ fn child_bounty_active_unassign_curator() { ); // Ensure child-bounty curator was slashed. - assert_eq!(Balances::free_balance(7), 101 - expected_child_deposit); - assert_eq!(Balances::reserved_balance(7), 0); // slashed + assert_eq!(Balances::free_balance(account_id(7)), 101 - expected_child_deposit); + assert_eq!(Balances::reserved_balance(account_id(7)), 0); // slashed // Propose and accept curator for child-bounty again. - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 6, 2)); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(6), 0, 0)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(6), + 2 + )); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(6)), 0, 0)); assert_eq!( pallet_child_bounties::ChildBounties::::get(0, 0).unwrap(), @@ -884,14 +1014,14 @@ fn child_bounty_active_unassign_curator() { value: 10, fee, curator_deposit: expected_child_deposit, - status: ChildBountyStatus::Active { curator: 6 }, + status: ChildBountyStatus::Active { curator: account_id(6) }, } ); go_to_block(6); // Unassign curator again - from child-bounty curator. - assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(6), 0, 0)); + assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(6)), 0, 0)); // Verify updated child-bounty status. assert_eq!( @@ -906,13 +1036,19 @@ fn child_bounty_active_unassign_curator() { ); // Ensure child-bounty curator was **not** slashed. - assert_eq!(Balances::free_balance(6), 101); // not slashed - assert_eq!(Balances::reserved_balance(6), 0); + assert_eq!(Balances::free_balance(account_id(6)), 101); // not slashed + assert_eq!(Balances::reserved_balance(account_id(6)), 0); // Propose and accept curator for child-bounty one last time. let fee = 2; - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 6, fee)); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(6), 0, 0)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(6), + fee + )); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(6)), 0, 0)); let expected_child_deposit = CuratorDepositMin::get(); assert_eq!( @@ -922,7 +1058,7 @@ fn child_bounty_active_unassign_curator() { value: 10, fee, curator_deposit: expected_child_deposit, - status: ChildBountyStatus::Active { curator: 6 }, + status: ChildBountyStatus::Active { curator: account_id(6) }, } ); @@ -931,14 +1067,14 @@ fn child_bounty_active_unassign_curator() { // Unassign curator again - from non curator; non reject origin; some random guy. // Bounty update period is not yet complete. assert_noop!( - ChildBounties::unassign_curator(RuntimeOrigin::signed(3), 0, 0), + ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(3)), 0, 0), BountiesError::Premature ); go_to_block(20); // Unassign child curator from random account after inactivity. - assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(3), 0, 0)); + assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(3)), 0, 0)); // Verify updated child-bounty status. assert_eq!( @@ -953,8 +1089,8 @@ fn child_bounty_active_unassign_curator() { ); // Ensure child-bounty curator was slashed. - assert_eq!(Balances::free_balance(6), 101 - expected_child_deposit); // slashed - assert_eq!(Balances::reserved_balance(6), 0); + assert_eq!(Balances::free_balance(account_id(6)), 101 - expected_child_deposit); // slashed + assert_eq!(Balances::reserved_balance(account_id(6)), 0); }); } @@ -970,23 +1106,27 @@ fn parent_bounty_inactive_unassign_curator_child_bounty() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator 1. - Balances::make_free_balance_be(&5, 101); // Parent-bounty curator 2. - Balances::make_free_balance_be(&6, 101); // Child-bounty curator 1. - Balances::make_free_balance_be(&7, 101); // Child-bounty curator 2. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator 3. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator 1. + Balances::make_free_balance_be(&account_id(5), 101); // Parent-bounty curator 2. + Balances::make_free_balance_be(&account_id(6), 101); // Child-bounty curator 1. + Balances::make_free_balance_be(&account_id(7), 101); // Child-bounty curator 2. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator 3. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), 6)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Create Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -997,8 +1137,14 @@ fn parent_bounty_inactive_unassign_curator_child_bounty() { // Propose and accept curator for child-bounty. let fee = 8; - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 8, fee)); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(8), 0, 0)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(8), + fee + )); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(8)), 0, 0)); let expected_child_deposit = CuratorDepositMultiplier::get() * fee; assert_eq!( @@ -1008,7 +1154,7 @@ fn parent_bounty_inactive_unassign_curator_child_bounty() { value: 10, fee, curator_deposit: expected_child_deposit, - status: ChildBountyStatus::Active { curator: 8 }, + status: ChildBountyStatus::Active { curator: account_id(8) }, } ); @@ -1022,7 +1168,7 @@ fn parent_bounty_inactive_unassign_curator_child_bounty() { // Try unassign child-bounty curator - from non curator; non reject // origin; some random guy. Bounty update period is not yet complete. assert_noop!( - ChildBounties::unassign_curator(RuntimeOrigin::signed(3), 0, 0), + ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(3)), 0, 0), Error::::ParentBountyNotActive ); @@ -1042,21 +1188,27 @@ fn parent_bounty_inactive_unassign_curator_child_bounty() { ); // Ensure child-bounty curator was slashed. - assert_eq!(Balances::free_balance(8), 101 - expected_child_deposit); - assert_eq!(Balances::reserved_balance(8), 0); // slashed + assert_eq!(Balances::free_balance(account_id(8)), 101 - expected_child_deposit); + assert_eq!(Balances::reserved_balance(account_id(8)), 0); // slashed go_to_block(6); // Propose and accept curator for parent-bounty again. - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 5, 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(5), 0)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(5), 6)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(5)), 0)); go_to_block(7); // Propose and accept curator for child-bounty again. let fee = 2; - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(5), 0, 0, 7, fee)); - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(7), 0, 0)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(5)), + 0, + 0, + account_id(7), + fee + )); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(7)), 0, 0)); let expected_deposit = CuratorDepositMin::get(); assert_eq!( @@ -1066,24 +1218,24 @@ fn parent_bounty_inactive_unassign_curator_child_bounty() { value: 10, fee, curator_deposit: expected_deposit, - status: ChildBountyStatus::Active { curator: 7 }, + status: ChildBountyStatus::Active { curator: account_id(7) }, } ); go_to_block(8); assert_noop!( - ChildBounties::unassign_curator(RuntimeOrigin::signed(3), 0, 0), + ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(3)), 0, 0), BountiesError::Premature ); // Unassign parent bounty curator again. - assert_ok!(Bounties::unassign_curator(RuntimeOrigin::signed(5), 0)); + assert_ok!(Bounties::unassign_curator(RuntimeOrigin::signed(account_id(5)), 0)); go_to_block(9); // Unassign curator again - from parent curator. - assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(7), 0, 0)); + assert_ok!(ChildBounties::unassign_curator(RuntimeOrigin::signed(account_id(7)), 0, 0)); // Verify updated child-bounty status. assert_eq!( @@ -1098,8 +1250,8 @@ fn parent_bounty_inactive_unassign_curator_child_bounty() { ); // Ensure child-bounty curator was not slashed. - assert_eq!(Balances::free_balance(7), 101); - assert_eq!(Balances::reserved_balance(7), 0); // slashed + assert_eq!(Balances::free_balance(account_id(7)), 101); + assert_eq!(Balances::reserved_balance(account_id(7)), 0); // slashed }); } @@ -1113,27 +1265,36 @@ fn close_parent_with_child_bounty() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); // Try add child-bounty. // Should fail, parent bounty not active yet. assert_noop!( - ChildBounties::add_child_bounty(RuntimeOrigin::signed(4), 0, 10, b"12345-p1".to_vec()), + ChildBounties::add_child_bounty( + RuntimeOrigin::signed(account_id(4)), + 0, + 10, + b"12345-p1".to_vec() + ), Error::::ParentBountyNotActive ); go_to_block(2); - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), 6)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -1154,10 +1315,14 @@ fn close_parent_with_child_bounty() { // Check the child-bounty count. assert_eq!(pallet_child_bounties::ParentChildBounties::::get(0), 0); + assert_eq!(pallet_child_bounties::ParentTotalChildBounties::::get(0), 1); // Try close parent-bounty again. // Should pass this time. assert_ok!(Bounties::close_bounty(RuntimeOrigin::root(), 0)); + + // Check the total count is removed after the parent bounty removal. + assert_eq!(pallet_child_bounties::ParentTotalChildBounties::::get(0), 0); }); } @@ -1173,20 +1338,24 @@ fn children_curator_fee_calculation_test() { assert_eq!(Balances::reserved_balance(Treasury::account_id()), 0); // Bounty curator initial balance. - Balances::make_free_balance_be(&4, 101); // Parent-bounty curator. - Balances::make_free_balance_be(&8, 101); // Child-bounty curator. + Balances::make_free_balance_be(&account_id(4), 101); // Parent-bounty curator. + Balances::make_free_balance_be(&account_id(8), 101); // Child-bounty curator. - assert_ok!(Bounties::propose_bounty(RuntimeOrigin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties::propose_bounty( + RuntimeOrigin::signed(account_id(0)), + 50, + b"12345".to_vec() + )); assert_ok!(Bounties::approve_bounty(RuntimeOrigin::root(), 0)); go_to_block(2); - assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, 4, 6)); - assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(4), 0)); + assert_ok!(Bounties::propose_curator(RuntimeOrigin::root(), 0, account_id(4), 6)); + assert_ok!(Bounties::accept_curator(RuntimeOrigin::signed(account_id(4)), 0)); // Child-bounty. assert_ok!(ChildBounties::add_child_bounty( - RuntimeOrigin::signed(4), + RuntimeOrigin::signed(account_id(4)), 0, 10, b"12345-p1".to_vec() @@ -1198,13 +1367,24 @@ fn children_curator_fee_calculation_test() { let fee = 6; // Propose curator for child-bounty. - assert_ok!(ChildBounties::propose_curator(RuntimeOrigin::signed(4), 0, 0, 8, fee)); + assert_ok!(ChildBounties::propose_curator( + RuntimeOrigin::signed(account_id(4)), + 0, + 0, + account_id(8), + fee + )); // Check curator fee added to the sum. assert_eq!(pallet_child_bounties::ChildrenCuratorFees::::get(0), fee); // Accept curator for child-bounty. - assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(8), 0, 0)); + assert_ok!(ChildBounties::accept_curator(RuntimeOrigin::signed(account_id(8)), 0, 0)); // Award child-bounty. - assert_ok!(ChildBounties::award_child_bounty(RuntimeOrigin::signed(8), 0, 0, 7)); + assert_ok!(ChildBounties::award_child_bounty( + RuntimeOrigin::signed(account_id(8)), + 0, + 0, + account_id(7) + )); let expected_child_deposit = CuratorDepositMultiplier::get() * fee; @@ -1216,8 +1396,8 @@ fn children_curator_fee_calculation_test() { fee, curator_deposit: expected_child_deposit, status: ChildBountyStatus::PendingPayout { - curator: 8, - beneficiary: 7, + curator: account_id(8), + beneficiary: account_id(7), unlock_at: 7 }, } @@ -1226,26 +1406,32 @@ fn children_curator_fee_calculation_test() { go_to_block(9); // Claim child-bounty. - assert_ok!(ChildBounties::claim_child_bounty(RuntimeOrigin::signed(7), 0, 0)); + assert_ok!(ChildBounties::claim_child_bounty(RuntimeOrigin::signed(account_id(7)), 0, 0)); // Check the child-bounty count. assert_eq!(pallet_child_bounties::ParentChildBounties::::get(0), 0); // Award the parent bounty. - assert_ok!(Bounties::award_bounty(RuntimeOrigin::signed(4), 0, 9)); + assert_ok!(Bounties::award_bounty(RuntimeOrigin::signed(account_id(4)), 0, account_id(9))); go_to_block(15); + // Check the total count. + assert_eq!(pallet_child_bounties::ParentTotalChildBounties::::get(0), 1); + // Claim the parent bounty. - assert_ok!(Bounties::claim_bounty(RuntimeOrigin::signed(9), 0)); + assert_ok!(Bounties::claim_bounty(RuntimeOrigin::signed(account_id(9)), 0)); + + // Check the total count after the parent bounty removal. + assert_eq!(pallet_child_bounties::ParentTotalChildBounties::::get(0), 0); // Ensure parent-bounty curator received correctly reduced fee. - assert_eq!(Balances::free_balance(4), 101 + 6 - fee); // 101 + 6 - 2 - assert_eq!(Balances::reserved_balance(4), 0); + assert_eq!(Balances::free_balance(account_id(4)), 101 + 6 - fee); // 101 + 6 - 2 + assert_eq!(Balances::reserved_balance(account_id(4)), 0); // Verify parent-bounty beneficiary balance. - assert_eq!(Balances::free_balance(9), 34); - assert_eq!(Balances::reserved_balance(9), 0); + assert_eq!(Balances::free_balance(account_id(9)), 34); + assert_eq!(Balances::reserved_balance(account_id(9)), 0); }); } @@ -1255,7 +1441,7 @@ fn accept_curator_handles_different_deposit_calculations() { // in a different curator deposit, and if the child curator matches the parent curator. new_test_ext().execute_with(|| { // Setup a parent bounty. - let parent_curator = 0; + let parent_curator = account_id(0); let parent_index = 0; let parent_value = 1_000_000; let parent_fee = 10_000; @@ -1284,7 +1470,7 @@ fn accept_curator_handles_different_deposit_calculations() { // Case 1: Parent and child curator are not the same. let child_index = 0; - let child_curator = 1; + let child_curator = account_id(1); let child_value = 1_000; let child_fee = 100; let starting_balance = 100 * child_fee + child_value; @@ -1351,7 +1537,7 @@ fn accept_curator_handles_different_deposit_calculations() { // Case 3: Upper Limit let child_index = 2; - let child_curator = 2; + let child_curator = account_id(2); let child_value = 10_000; let child_fee = 5_000; @@ -1386,7 +1572,7 @@ fn accept_curator_handles_different_deposit_calculations() { // Case 4: Lower Limit let child_index = 3; - let child_curator = 3; + let child_curator = account_id(3); let child_value = 10_000; let child_fee = 0; @@ -1416,3 +1602,10 @@ fn accept_curator_handles_different_deposit_calculations() { assert_eq!(Balances::reserved_balance(child_curator), expected_deposit); }); } + +#[test] +fn integrity_test() { + new_test_ext().execute_with(|| { + ChildBounties::integrity_test(); + }); +} diff --git a/substrate/frame/election-provider-multi-phase/src/benchmarking.rs b/substrate/frame/election-provider-multi-phase/src/benchmarking.rs index 2a3994ff2aa6598f8c2a21c324ca4c00feac7a86..222e79ab99c6c1f9039f023371a0b739d92b0743 100644 --- a/substrate/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/substrate/frame/election-provider-multi-phase/src/benchmarking.rs @@ -17,10 +17,9 @@ //! Two phase election pallet benchmarking. -use super::*; -use crate::{unsigned::IndexAssignmentOf, Pallet as MultiPhase}; -use frame_benchmarking::account; -use frame_election_provider_support::bounds::DataProviderBounds; +use core::cmp::Reverse; +use frame_benchmarking::{v2::*, BenchmarkError}; +use frame_election_provider_support::{bounds::DataProviderBounds, IndexAssignment}; use frame_support::{ assert_ok, traits::{Hooks, TryCollect}, @@ -31,6 +30,8 @@ use rand::{prelude::SliceRandom, rngs::SmallRng, SeedableRng}; use sp_arithmetic::{per_things::Percent, traits::One}; use sp_runtime::InnerOf; +use crate::{unsigned::IndexAssignmentOf, *}; + const SEED: u32 = 999; /// Creates a **valid** solution with exactly the given size. @@ -133,7 +134,7 @@ fn solution_with_size( .map(|(voter, _stake, votes)| { let percent_per_edge: InnerOf> = (100 / votes.len()).try_into().unwrap_or_else(|_| panic!("failed to convert")); - crate::unsigned::Assignment:: { + unsigned::Assignment:: { who: voter.clone(), distribution: votes .iter() @@ -190,140 +191,179 @@ fn set_up_data_provider(v: u32, t: u32) { }); } -frame_benchmarking::benchmarks! { - on_initialize_nothing { +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn on_initialize_nothing() { assert!(CurrentPhase::::get().is_off()); - }: { - MultiPhase::::on_initialize(1u32.into()); - } verify { + + #[block] + { + Pallet::::on_initialize(1_u32.into()); + } + assert!(CurrentPhase::::get().is_off()); } - on_initialize_open_signed { + #[benchmark] + fn on_initialize_open_signed() { assert!(Snapshot::::get().is_none()); assert!(CurrentPhase::::get().is_off()); - }: { - MultiPhase::::phase_transition(Phase::Signed); - } verify { + + #[block] + { + Pallet::::phase_transition(Phase::Signed); + } + assert!(Snapshot::::get().is_none()); assert!(CurrentPhase::::get().is_signed()); } - on_initialize_open_unsigned { + #[benchmark] + fn on_initialize_open_unsigned() { assert!(Snapshot::::get().is_none()); assert!(CurrentPhase::::get().is_off()); - }: { - let now = frame_system::Pallet::::block_number(); - MultiPhase::::phase_transition(Phase::Unsigned((true, now))); - } verify { + + #[block] + { + let now = frame_system::Pallet::::block_number(); + Pallet::::phase_transition(Phase::Unsigned((true, now))); + } + assert!(Snapshot::::get().is_none()); assert!(CurrentPhase::::get().is_unsigned()); } - finalize_signed_phase_accept_solution { + #[benchmark] + fn finalize_signed_phase_accept_solution() { let receiver = account("receiver", 0, SEED); - let initial_balance = T::Currency::minimum_balance() + 10u32.into(); + let initial_balance = T::Currency::minimum_balance() + 10_u32.into(); T::Currency::make_free_balance_be(&receiver, initial_balance); let ready = Default::default(); - let deposit: BalanceOf = 10u32.into(); + let deposit: BalanceOf = 10_u32.into(); let reward: BalanceOf = T::SignedRewardBase::get(); - let call_fee: BalanceOf = 30u32.into(); + let call_fee: BalanceOf = 30_u32.into(); assert_ok!(T::Currency::reserve(&receiver, deposit)); assert_eq!(T::Currency::free_balance(&receiver), T::Currency::minimum_balance()); - }: { - MultiPhase::::finalize_signed_phase_accept_solution( - ready, - &receiver, - deposit, - call_fee - ) - } verify { - assert_eq!( - T::Currency::free_balance(&receiver), - initial_balance + reward + call_fee - ); - assert_eq!(T::Currency::reserved_balance(&receiver), 0u32.into()); + + #[block] + { + Pallet::::finalize_signed_phase_accept_solution(ready, &receiver, deposit, call_fee); + } + + assert_eq!(T::Currency::free_balance(&receiver), initial_balance + reward + call_fee); + assert_eq!(T::Currency::reserved_balance(&receiver), 0_u32.into()); } - finalize_signed_phase_reject_solution { + #[benchmark] + fn finalize_signed_phase_reject_solution() { let receiver = account("receiver", 0, SEED); - let initial_balance = T::Currency::minimum_balance() + 10u32.into(); - let deposit: BalanceOf = 10u32.into(); + let initial_balance = T::Currency::minimum_balance() + 10_u32.into(); + let deposit: BalanceOf = 10_u32.into(); T::Currency::make_free_balance_be(&receiver, initial_balance); assert_ok!(T::Currency::reserve(&receiver, deposit)); assert_eq!(T::Currency::free_balance(&receiver), T::Currency::minimum_balance()); - assert_eq!(T::Currency::reserved_balance(&receiver), 10u32.into()); - }: { - MultiPhase::::finalize_signed_phase_reject_solution(&receiver, deposit) - } verify { + assert_eq!(T::Currency::reserved_balance(&receiver), 10_u32.into()); + + #[block] + { + Pallet::::finalize_signed_phase_reject_solution(&receiver, deposit) + } + assert_eq!(T::Currency::free_balance(&receiver), T::Currency::minimum_balance()); - assert_eq!(T::Currency::reserved_balance(&receiver), 0u32.into()); + assert_eq!(T::Currency::reserved_balance(&receiver), 0_u32.into()); } - create_snapshot_internal { - // number of votes in snapshot. - let v in (T::BenchmarkingConfig::VOTERS[0]) .. T::BenchmarkingConfig::VOTERS[1]; - // number of targets in snapshot. - let t in (T::BenchmarkingConfig::TARGETS[0]) .. T::BenchmarkingConfig::TARGETS[1]; - - // we don't directly need the data-provider to be populated, but it is just easy to use it. + #[benchmark] + fn create_snapshot_internal( + // Number of votes in snapshot. + v: Linear<{ T::BenchmarkingConfig::VOTERS[0] }, { T::BenchmarkingConfig::VOTERS[1] }>, + // Number of targets in snapshot. + t: Linear<{ T::BenchmarkingConfig::TARGETS[0] }, { T::BenchmarkingConfig::TARGETS[1] }>, + ) -> Result<(), BenchmarkError> { + // We don't directly need the data-provider to be populated, but it is just easy to use it. set_up_data_provider::(v, t); - // default bounds are unbounded. + // Default bounds are unbounded. let targets = T::DataProvider::electable_targets(DataProviderBounds::default())?; let voters = T::DataProvider::electing_voters(DataProviderBounds::default())?; let desired_targets = T::DataProvider::desired_targets()?; assert!(Snapshot::::get().is_none()); - }: { - MultiPhase::::create_snapshot_internal(targets, voters, desired_targets) - } verify { + + #[block] + { + Pallet::::create_snapshot_internal(targets, voters, desired_targets) + } + assert!(Snapshot::::get().is_some()); assert_eq!(SnapshotMetadata::::get().ok_or("metadata missing")?.voters, v); assert_eq!(SnapshotMetadata::::get().ok_or("metadata missing")?.targets, t); + + Ok(()) } - // a call to `::elect` where we only return the queued solution. - elect_queued { - // number of assignments, i.e. solution.len(). This means the active nominators, thus must be - // a subset of `v`. - let a in (T::BenchmarkingConfig::ACTIVE_VOTERS[0]) .. T::BenchmarkingConfig::ACTIVE_VOTERS[1]; - // number of desired targets. Must be a subset of `t`. - let d in (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. T::BenchmarkingConfig::DESIRED_TARGETS[1]; - - // number of votes in snapshot. Not dominant. - let v = T::BenchmarkingConfig::VOTERS[1]; - // number of targets in snapshot. Not dominant. + // A call to `::elect` where we only return the queued solution. + #[benchmark] + fn elect_queued( + // Number of assignments, i.e. `solution.len()`. + // This means the active nominators, thus must be a subset of `v`. + a: Linear< + { T::BenchmarkingConfig::ACTIVE_VOTERS[0] }, + { T::BenchmarkingConfig::ACTIVE_VOTERS[1] }, + >, + // Number of desired targets. Must be a subset of `t`. + d: Linear< + { T::BenchmarkingConfig::DESIRED_TARGETS[0] }, + { T::BenchmarkingConfig::DESIRED_TARGETS[1] }, + >, + ) -> Result<(), BenchmarkError> { + // Number of votes in snapshot. Not dominant. + let v = T::BenchmarkingConfig::VOTERS[1]; + // Number of targets in snapshot. Not dominant. let t = T::BenchmarkingConfig::TARGETS[1]; let witness = SolutionOrSnapshotSize { voters: v, targets: t }; let raw_solution = solution_with_size::(witness, a, d)?; - let ready_solution = - MultiPhase::::feasibility_check(raw_solution, ElectionCompute::Signed) - .map_err(<&str>::from)?; + let ready_solution = Pallet::::feasibility_check(raw_solution, ElectionCompute::Signed) + .map_err(<&str>::from)?; CurrentPhase::::put(Phase::Signed); - // assume a queued solution is stored, regardless of where it comes from. + // Assume a queued solution is stored, regardless of where it comes from. QueuedSolution::::put(ready_solution); - // these are set by the `solution_with_size` function. + // These are set by the `solution_with_size` function. assert!(DesiredTargets::::get().is_some()); assert!(Snapshot::::get().is_some()); assert!(SnapshotMetadata::::get().is_some()); - }: { - assert_ok!( as ElectionProvider>::elect()); - } verify { + + let result; + + #[block] + { + result = as ElectionProvider>::elect(); + } + + assert!(result.is_ok()); assert!(QueuedSolution::::get().is_none()); assert!(DesiredTargets::::get().is_none()); assert!(Snapshot::::get().is_none()); assert!(SnapshotMetadata::::get().is_none()); - assert_eq!(CurrentPhase::::get(), >>::Off); + assert_eq!( + CurrentPhase::::get(), + >>::Off + ); + + Ok(()) } - submit { - // the queue is full and the solution is only better than the worse. - MultiPhase::::create_snapshot().map_err(<&str>::from)?; - MultiPhase::::phase_transition(Phase::Signed); + #[benchmark] + fn submit() -> Result<(), BenchmarkError> { + // The queue is full and the solution is only better than the worse. + Pallet::::create_snapshot().map_err(<&str>::from)?; + Pallet::::phase_transition(Phase::Signed); Round::::put(1); let mut signed_submissions = SignedSubmissions::::get(); @@ -331,7 +371,10 @@ frame_benchmarking::benchmarks! { // Insert `max` submissions for i in 0..(T::SignedMaxSubmissions::get() - 1) { let raw_solution = RawSolution { - score: ElectionScore { minimal_stake: 10_000_000u128 + (i as u128), ..Default::default() }, + score: ElectionScore { + minimal_stake: 10_000_000u128 + (i as u128), + ..Default::default() + }, ..Default::default() }; let signed_submission = SignedSubmission { @@ -344,67 +387,95 @@ frame_benchmarking::benchmarks! { } signed_submissions.put(); - // this score will eject the weakest one. + // This score will eject the weakest one. let solution = RawSolution { score: ElectionScore { minimal_stake: 10_000_000u128 + 1, ..Default::default() }, ..Default::default() }; let caller = frame_benchmarking::whitelisted_caller(); - let deposit = MultiPhase::::deposit_for( - &solution, - SnapshotMetadata::::get().unwrap_or_default(), + let deposit = + Pallet::::deposit_for(&solution, SnapshotMetadata::::get().unwrap_or_default()); + T::Currency::make_free_balance_be( + &caller, + T::Currency::minimum_balance() * 1000u32.into() + deposit, ); - T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance() * 1000u32.into() + deposit); - }: _(RawOrigin::Signed(caller), Box::new(solution)) - verify { - assert!(MultiPhase::::signed_submissions().len() as u32 == T::SignedMaxSubmissions::get()); - } + #[extrinsic_call] + _(RawOrigin::Signed(caller), Box::new(solution)); - submit_unsigned { - // number of votes in snapshot. - let v in (T::BenchmarkingConfig::VOTERS[0]) .. T::BenchmarkingConfig::VOTERS[1]; - // number of targets in snapshot. - let t in (T::BenchmarkingConfig::TARGETS[0]) .. T::BenchmarkingConfig::TARGETS[1]; - // number of assignments, i.e. solution.len(). This means the active nominators, thus must be - // a subset of `v` component. - let a in - (T::BenchmarkingConfig::ACTIVE_VOTERS[0]) .. T::BenchmarkingConfig::ACTIVE_VOTERS[1]; - // number of desired targets. Must be a subset of `t` component. - let d in - (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. - T::BenchmarkingConfig::DESIRED_TARGETS[1]; + assert!(Pallet::::signed_submissions().len() as u32 == T::SignedMaxSubmissions::get()); + Ok(()) + } + + #[benchmark] + fn submit_unsigned( + // Number of votes in snapshot. + v: Linear<{ T::BenchmarkingConfig::VOTERS[0] }, { T::BenchmarkingConfig::VOTERS[1] }>, + // Number of targets in snapshot. + t: Linear<{ T::BenchmarkingConfig::TARGETS[0] }, { T::BenchmarkingConfig::TARGETS[1] }>, + // Number of assignments, i.e. `solution.len()`. + // This means the active nominators, thus must be a subset of `v` component. + a: Linear< + { T::BenchmarkingConfig::ACTIVE_VOTERS[0] }, + { T::BenchmarkingConfig::ACTIVE_VOTERS[1] }, + >, + // Number of desired targets. Must be a subset of `t` component. + d: Linear< + { T::BenchmarkingConfig::DESIRED_TARGETS[0] }, + { T::BenchmarkingConfig::DESIRED_TARGETS[1] }, + >, + ) -> Result<(), BenchmarkError> { let witness = SolutionOrSnapshotSize { voters: v, targets: t }; let raw_solution = solution_with_size::(witness, a, d)?; assert!(QueuedSolution::::get().is_none()); - CurrentPhase::::put(Phase::Unsigned((true, 1u32.into()))); - }: _(RawOrigin::None, Box::new(raw_solution), witness) - verify { + CurrentPhase::::put(Phase::Unsigned((true, 1_u32.into()))); + + #[extrinsic_call] + _(RawOrigin::None, Box::new(raw_solution), witness); + assert!(QueuedSolution::::get().is_some()); + + Ok(()) } // This is checking a valid solution. The worse case is indeed a valid solution. - feasibility_check { - // number of votes in snapshot. - let v in (T::BenchmarkingConfig::VOTERS[0]) .. T::BenchmarkingConfig::VOTERS[1]; - // number of targets in snapshot. - let t in (T::BenchmarkingConfig::TARGETS[0]) .. T::BenchmarkingConfig::TARGETS[1]; - // number of assignments, i.e. solution.len(). This means the active nominators, thus must be - // a subset of `v` component. - let a in (T::BenchmarkingConfig::ACTIVE_VOTERS[0]) .. T::BenchmarkingConfig::ACTIVE_VOTERS[1]; - // number of desired targets. Must be a subset of `t` component. - let d in (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. T::BenchmarkingConfig::DESIRED_TARGETS[1]; - + #[benchmark] + fn feasibility_check( + // Number of votes in snapshot. + v: Linear<{ T::BenchmarkingConfig::VOTERS[0] }, { T::BenchmarkingConfig::VOTERS[1] }>, + // Number of targets in snapshot. + t: Linear<{ T::BenchmarkingConfig::TARGETS[0] }, { T::BenchmarkingConfig::TARGETS[1] }>, + // Number of assignments, i.e. `solution.len()`. + // This means the active nominators, thus must be a subset of `v` component. + a: Linear< + { T::BenchmarkingConfig::ACTIVE_VOTERS[0] }, + { T::BenchmarkingConfig::ACTIVE_VOTERS[1] }, + >, + // Number of desired targets. Must be a subset of `t` component. + d: Linear< + { T::BenchmarkingConfig::DESIRED_TARGETS[0] }, + { T::BenchmarkingConfig::DESIRED_TARGETS[1] }, + >, + ) -> Result<(), BenchmarkError> { let size = SolutionOrSnapshotSize { voters: v, targets: t }; let raw_solution = solution_with_size::(size, a, d)?; assert_eq!(raw_solution.solution.voter_count() as u32, a); assert_eq!(raw_solution.solution.unique_targets().len() as u32, d); - }: { - assert!(MultiPhase::::feasibility_check(raw_solution, ElectionCompute::Unsigned).is_ok()); + + let result; + + #[block] + { + result = Pallet::::feasibility_check(raw_solution, ElectionCompute::Unsigned); + } + + assert!(result.is_ok()); + + Ok(()) } // NOTE: this weight is not used anywhere, but the fact that it should succeed when execution in @@ -419,20 +490,23 @@ frame_benchmarking::benchmarks! { // This benchmark is doing more work than a raw call to `OffchainWorker_offchain_worker` runtime // api call, since it is also setting up some mock data, which will itself exhaust the heap to // some extent. - #[extra] - mine_solution_offchain_memory { - // number of votes in snapshot. Fixed to maximum. + #[benchmark(extra)] + fn mine_solution_offchain_memory() { + // Number of votes in snapshot. Fixed to maximum. let v = T::BenchmarkingConfig::MINER_MAXIMUM_VOTERS; - // number of targets in snapshot. Fixed to maximum. + // Number of targets in snapshot. Fixed to maximum. let t = T::BenchmarkingConfig::MAXIMUM_TARGETS; set_up_data_provider::(v, t); let now = frame_system::Pallet::::block_number(); CurrentPhase::::put(Phase::Unsigned((true, now))); - MultiPhase::::create_snapshot().unwrap(); - }: { - // we can't really verify this as it won't write anything to state, check logs. - MultiPhase::::offchain_worker(now) + Pallet::::create_snapshot().unwrap(); + + #[block] + { + // we can't really verify this as it won't write anything to state, check logs. + Pallet::::offchain_worker(now) + } } // NOTE: this weight is not used anywhere, but the fact that it should succeed when execution in @@ -441,41 +515,48 @@ frame_benchmarking::benchmarks! { // numbers. // // ONLY run this benchmark in isolation, and pass the `--extra` flag to enable it. - #[extra] - create_snapshot_memory { - // number of votes in snapshot. Fixed to maximum. + #[benchmark(extra)] + fn create_snapshot_memory() -> Result<(), BenchmarkError> { + // Number of votes in snapshot. Fixed to maximum. let v = T::BenchmarkingConfig::SNAPSHOT_MAXIMUM_VOTERS; - // number of targets in snapshot. Fixed to maximum. + // Number of targets in snapshot. Fixed to maximum. let t = T::BenchmarkingConfig::MAXIMUM_TARGETS; set_up_data_provider::(v, t); assert!(Snapshot::::get().is_none()); - }: { - MultiPhase::::create_snapshot().map_err(|_| "could not create snapshot")?; - } verify { + + #[block] + { + Pallet::::create_snapshot().map_err(|_| "could not create snapshot")?; + } + assert!(Snapshot::::get().is_some()); assert_eq!(SnapshotMetadata::::get().ok_or("snapshot missing")?.voters, v); assert_eq!(SnapshotMetadata::::get().ok_or("snapshot missing")?.targets, t); - } - #[extra] - trim_assignments_length { - // number of votes in snapshot. - let v in (T::BenchmarkingConfig::VOTERS[0]) .. T::BenchmarkingConfig::VOTERS[1]; - // number of targets in snapshot. - let t in (T::BenchmarkingConfig::TARGETS[0]) .. T::BenchmarkingConfig::TARGETS[1]; - // number of assignments, i.e. solution.len(). This means the active nominators, thus must be - // a subset of `v` component. - let a in - (T::BenchmarkingConfig::ACTIVE_VOTERS[0]) .. T::BenchmarkingConfig::ACTIVE_VOTERS[1]; - // number of desired targets. Must be a subset of `t` component. - let d in - (T::BenchmarkingConfig::DESIRED_TARGETS[0]) .. - T::BenchmarkingConfig::DESIRED_TARGETS[1]; - // Subtract this percentage from the actual encoded size - let f in 0 .. 95; - use frame_election_provider_support::IndexAssignment; + Ok(()) + } + #[benchmark(extra)] + fn trim_assignments_length( + // Number of votes in snapshot. + v: Linear<{ T::BenchmarkingConfig::VOTERS[0] }, { T::BenchmarkingConfig::VOTERS[1] }>, + // Number of targets in snapshot. + t: Linear<{ T::BenchmarkingConfig::TARGETS[0] }, { T::BenchmarkingConfig::TARGETS[1] }>, + // Number of assignments, i.e. `solution.len()`. + // This means the active nominators, thus must be a subset of `v` component. + a: Linear< + { T::BenchmarkingConfig::ACTIVE_VOTERS[0] }, + { T::BenchmarkingConfig::ACTIVE_VOTERS[1] }, + >, + // Number of desired targets. Must be a subset of `t` component. + d: Linear< + { T::BenchmarkingConfig::DESIRED_TARGETS[0] }, + { T::BenchmarkingConfig::DESIRED_TARGETS[1] }, + >, + // Subtract this percentage from the actual encoded size. + f: Linear<0, 95>, + ) -> Result<(), BenchmarkError> { // Compute a random solution, then work backwards to get the lists of voters, targets, and // assignments let witness = SolutionOrSnapshotSize { voters: v, targets: t }; @@ -483,7 +564,9 @@ frame_benchmarking::benchmarks! { let RoundSnapshot { voters, targets } = Snapshot::::get().ok_or("snapshot missing")?; let voter_at = helpers::voter_at_fn::(&voters); let target_at = helpers::target_at_fn::(&targets); - let mut assignments = solution.into_assignment(voter_at, target_at).expect("solution generated by `solution_with_size` must be valid."); + let mut assignments = solution + .into_assignment(voter_at, target_at) + .expect("solution generated by `solution_with_size` must be valid."); // make a voter cache and some helper functions for access let cache = helpers::generate_voter_cache::(&voters); @@ -491,12 +574,15 @@ frame_benchmarking::benchmarks! { let target_index = helpers::target_index_fn::(&targets); // sort assignments by decreasing voter stake - assignments.sort_by_key(|crate::unsigned::Assignment:: { who, .. }| { - let stake = cache.get(who).map(|idx| { - let (_, stake, _) = voters[*idx]; - stake - }).unwrap_or_default(); - core::cmp::Reverse(stake) + assignments.sort_by_key(|unsigned::Assignment:: { who, .. }| { + let stake = cache + .get(who) + .map(|idx| { + let (_, stake, _) = voters[*idx]; + stake + }) + .unwrap_or_default(); + Reverse(stake) }); let mut index_assignments = assignments @@ -506,20 +592,26 @@ frame_benchmarking::benchmarks! { .unwrap(); let encoded_size_of = |assignments: &[IndexAssignmentOf]| { - SolutionOf::::try_from(assignments).map(|solution| solution.encoded_size()) + SolutionOf::::try_from(assignments) + .map(|solution| solution.encoded_size()) }; let desired_size = Percent::from_percent(100 - f.saturated_into::()) .mul_ceil(encoded_size_of(index_assignments.as_slice()).unwrap()); log!(trace, "desired_size = {}", desired_size); - }: { - crate::Miner::::trim_assignments_length( - desired_size.saturated_into(), - &mut index_assignments, - &encoded_size_of, - ).unwrap(); - } verify { - let solution = SolutionOf::::try_from(index_assignments.as_slice()).unwrap(); + + #[block] + { + Miner::::trim_assignments_length( + desired_size.saturated_into(), + &mut index_assignments, + &encoded_size_of, + ) + .unwrap(); + } + + let solution = + SolutionOf::::try_from(index_assignments.as_slice()).unwrap(); let encoding = solution.encode(); log!( trace, @@ -528,11 +620,13 @@ frame_benchmarking::benchmarks! { ); log!(trace, "actual encoded size = {}", encoding.len()); assert!(encoding.len() <= desired_size); + + Ok(()) } - impl_benchmark_test_suite!( - MultiPhase, - crate::mock::ExtBuilder::default().build_offchainify(10).0, - crate::mock::Runtime, - ); + impl_benchmark_test_suite! { + Pallet, + mock::ExtBuilder::default().build_offchainify(10).0, + mock::Runtime, + } } diff --git a/substrate/frame/election-provider-support/benchmarking/src/inner.rs b/substrate/frame/election-provider-support/benchmarking/src/inner.rs index 8cca0d459eac303028045c17f52694aa9140f782..7fb8c1bdb72906b65deebd033565b440b0578e5a 100644 --- a/substrate/frame/election-provider-support/benchmarking/src/inner.rs +++ b/substrate/frame/election-provider-support/benchmarking/src/inner.rs @@ -20,17 +20,19 @@ use alloc::vec::Vec; use codec::Decode; -use frame_benchmarking::v1::benchmarks; +use frame_benchmarking::v2::*; use frame_election_provider_support::{NposSolver, PhragMMS, SequentialPhragmen}; - -pub struct Pallet(frame_system::Pallet); -pub trait Config: frame_system::Config {} +use sp_runtime::Perbill; const VOTERS: [u32; 2] = [1_000, 2_000]; const TARGETS: [u32; 2] = [500, 1_000]; const VOTES_PER_VOTER: [u32; 2] = [5, 16]; - const SEED: u32 = 999; + +pub trait Config: frame_system::Config {} + +pub struct Pallet(frame_system::Pallet); + fn set_up_voters_targets( voters_len: u32, targets_len: u32, @@ -54,36 +56,47 @@ fn set_up_voters_targets( (voters, targets) } -benchmarks! { - phragmen { - // number of votes in snapshot. - let v in (VOTERS[0]) .. VOTERS[1]; - // number of targets in snapshot. - let t in (TARGETS[0]) .. TARGETS[1]; - // number of votes per voter (ie the degree). - let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1]; - - let (voters, targets) = set_up_voters_targets::(v, t, d as usize); - }: { - assert!( - SequentialPhragmen:: - ::solve(d as usize, targets, voters).is_ok() - ); +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn phragmen( + // Number of votes in snapshot. + v: Linear<{ VOTERS[0] }, { VOTERS[1] }>, + // Number of targets in snapshot. + t: Linear<{ TARGETS[0] }, { TARGETS[1] }>, + // Number of votes per voter (ie the degree). + d: Linear<{ VOTES_PER_VOTER[0] }, { VOTES_PER_VOTER[1] }>, + ) { + let (voters, targets) = set_up_voters_targets::(v, t, d as _); + let result; + + #[block] + { + result = SequentialPhragmen::::solve(d as _, targets, voters); + } + + assert!(result.is_ok()); } - phragmms { - // number of votes in snapshot. - let v in (VOTERS[0]) .. VOTERS[1]; - // number of targets in snapshot. - let t in (TARGETS[0]) .. TARGETS[1]; - // number of votes per voter (ie the degree). - let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1]; - - let (voters, targets) = set_up_voters_targets::(v, t, d as usize); - }: { - assert!( - PhragMMS:: - ::solve(d as usize, targets, voters).is_ok() - ); + #[benchmark] + fn phragmms( + // Number of votes in snapshot. + v: Linear<{ VOTERS[0] }, { VOTERS[1] }>, + // Number of targets in snapshot. + t: Linear<{ TARGETS[0] }, { TARGETS[1] }>, + // Number of votes per voter (ie the degree). + d: Linear<{ VOTES_PER_VOTER[0] }, { VOTES_PER_VOTER[1] }>, + ) { + let (voters, targets) = set_up_voters_targets::(v, t, d as _); + let result; + + #[block] + { + result = PhragMMS::::solve(d as _, targets, voters); + } + + assert!(result.is_ok()); } } diff --git a/substrate/frame/elections-phragmen/src/benchmarking.rs b/substrate/frame/elections-phragmen/src/benchmarking.rs index 8e762f667b2a6a0d3b58916e93e91bb8fc56a67b..60771fa89ad7ea6378bd1e97f8d47054bca58674 100644 --- a/substrate/frame/elections-phragmen/src/benchmarking.rs +++ b/substrate/frame/elections-phragmen/src/benchmarking.rs @@ -19,47 +19,47 @@ #![cfg(feature = "runtime-benchmarks")] -use super::*; - -use frame_benchmarking::v1::{account, benchmarks, whitelist, BenchmarkError, BenchmarkResult}; +use frame_benchmarking::v2::*; use frame_support::{dispatch::DispatchResultWithPostInfo, traits::OnInitialize}; use frame_system::RawOrigin; -use crate::Pallet as Elections; +#[cfg(test)] +use crate::tests::MEMBERS; +use crate::*; const BALANCE_FACTOR: u32 = 250; -/// grab new account with infinite balance. +// grab new account with infinite balance. fn endowed_account(name: &'static str, index: u32) -> T::AccountId { let account: T::AccountId = account(name, index, 0); // Fund each account with at-least their stake but still a sane amount as to not mess up // the vote calculation. let amount = default_stake::(T::MaxVoters::get()) * BalanceOf::::from(BALANCE_FACTOR); let _ = T::Currency::make_free_balance_be(&account, amount); - // important to increase the total issuance since T::CurrencyToVote will need it to be sane for - // phragmen to work. + // Important to increase the total issuance since `T::CurrencyToVote` will need it to be sane + // for phragmen to work. let _ = T::Currency::issue(amount); account } -/// Account to lookup type of system trait. +// Account to lookup type of system trait. fn as_lookup(account: T::AccountId) -> AccountIdLookupOf { T::Lookup::unlookup(account) } -/// Get a reasonable amount of stake based on the execution trait's configuration +// Get a reasonable amount of stake based on the execution trait's configuration. fn default_stake(num_votes: u32) -> BalanceOf { let min = T::Currency::minimum_balance(); - Elections::::deposit_of(num_votes as usize).max(min) + Pallet::::deposit_of(num_votes as usize).max(min) } -/// Get the current number of candidates. +// Get the current number of candidates. fn candidate_count() -> u32 { Candidates::::decode_len().unwrap_or(0usize) as u32 } -/// Add `c` new candidates. +// Add `c` new candidates. fn submit_candidates( c: u32, prefix: &'static str, @@ -67,7 +67,7 @@ fn submit_candidates( (0..c) .map(|i| { let account = endowed_account::(prefix, i); - Elections::::submit_candidacy( + Pallet::::submit_candidacy( RawOrigin::Signed(account.clone()).into(), candidate_count::(), ) @@ -77,7 +77,7 @@ fn submit_candidates( .collect::>() } -/// Add `c` new candidates with self vote. +// Add `c` new candidates with self vote. fn submit_candidates_with_self_vote( c: u32, prefix: &'static str, @@ -90,17 +90,17 @@ fn submit_candidates_with_self_vote( Ok(candidates) } -/// Submit one voter. +// Submit one voter. fn submit_voter( caller: T::AccountId, votes: Vec, stake: BalanceOf, ) -> DispatchResultWithPostInfo { - Elections::::vote(RawOrigin::Signed(caller).into(), votes, stake) + Pallet::::vote(RawOrigin::Signed(caller).into(), votes, stake) } -/// create `num_voter` voters who randomly vote for at most `votes` of `all_candidates` if -/// available. +// Create `num_voter` voters who randomly vote for at most `votes` of `all_candidates` if +// available. fn distribute_voters( mut all_candidates: Vec, num_voters: u32, @@ -117,12 +117,12 @@ fn distribute_voters( Ok(()) } -/// Fill the seats of members and runners-up up until `m`. Note that this might include either only -/// members, or members and runners-up. +// Fill the seats of members and runners-up up until `m`. Note that this might include either only +// members, or members and runners-up. fn fill_seats_up_to(m: u32) -> Result, &'static str> { let _ = submit_candidates_with_self_vote::(m, "fill_seats_up_to")?; assert_eq!(Candidates::::get().len() as u32, m, "wrong number of candidates."); - Elections::::do_phragmen(); + Pallet::::do_phragmen(); assert_eq!(Candidates::::get().len(), 0, "some candidates remaining."); assert_eq!( Members::::get().len() + RunnersUp::::get().len(), @@ -136,7 +136,7 @@ fn fill_seats_up_to(m: u32) -> Result, &'static str .collect()) } -/// removes all the storage items to reverse any genesis state. +// Removes all the storage items to reverse any genesis state. fn clean() { Members::::kill(); Candidates::::kill(); @@ -145,10 +145,13 @@ fn clean() { Voting::::remove_all(None); } -benchmarks! { +#[benchmarks] +mod benchmarks { + use super::*; + // -- Signed ones - vote_equal { - let v in 1 .. T::MaxVotesPerVoter::get(); + #[benchmark] + fn vote_equal(v: Linear<1, { T::MaxVotesPerVoter::get() }>) -> Result<(), BenchmarkError> { clean::(); // create a bunch of candidates. @@ -157,65 +160,81 @@ benchmarks! { let caller = endowed_account::("caller", 0); let stake = default_stake::(v); - // original votes. + // Original votes. let mut votes = all_candidates; submit_voter::(caller.clone(), votes.clone(), stake)?; - // new votes. + // New votes. votes.rotate_left(1); whitelist!(caller); - }: vote(RawOrigin::Signed(caller), votes, stake) - vote_more { - let v in 2 .. T::MaxVotesPerVoter::get(); + #[extrinsic_call] + vote(RawOrigin::Signed(caller), votes, stake); + + Ok(()) + } + + #[benchmark] + fn vote_more(v: Linear<2, { T::MaxVotesPerVoter::get() }>) -> Result<(), BenchmarkError> { clean::(); - // create a bunch of candidates. + // Create a bunch of candidates. let all_candidates = submit_candidates::(v, "candidates")?; let caller = endowed_account::("caller", 0); // Multiply the stake with 10 since we want to be able to divide it by 10 again. - let stake = default_stake::(v) * BalanceOf::::from(10u32); + let stake = default_stake::(v) * BalanceOf::::from(10_u32); - // original votes. + // Original votes. let mut votes = all_candidates.iter().skip(1).cloned().collect::>(); - submit_voter::(caller.clone(), votes.clone(), stake / BalanceOf::::from(10u32))?; + submit_voter::(caller.clone(), votes.clone(), stake / BalanceOf::::from(10_u32))?; - // new votes. + // New votes. votes = all_candidates; assert!(votes.len() > Voting::::get(caller.clone()).votes.len()); whitelist!(caller); - }: vote(RawOrigin::Signed(caller), votes, stake / BalanceOf::::from(10u32)) - vote_less { - let v in 2 .. T::MaxVotesPerVoter::get(); + #[extrinsic_call] + vote(RawOrigin::Signed(caller), votes, stake / BalanceOf::::from(10_u32)); + + Ok(()) + } + + #[benchmark] + fn vote_less(v: Linear<2, { T::MaxVotesPerVoter::get() }>) -> Result<(), BenchmarkError> { clean::(); - // create a bunch of candidates. + // Create a bunch of candidates. let all_candidates = submit_candidates::(v, "candidates")?; let caller = endowed_account::("caller", 0); let stake = default_stake::(v); - // original votes. + // Original votes. let mut votes = all_candidates; submit_voter::(caller.clone(), votes.clone(), stake)?; - // new votes. + // New votes. votes = votes.into_iter().skip(1).collect::>(); assert!(votes.len() < Voting::::get(caller.clone()).votes.len()); whitelist!(caller); - }: vote(RawOrigin::Signed(caller), votes, stake) - remove_voter { - // we fix the number of voted candidates to max + #[extrinsic_call] + vote(RawOrigin::Signed(caller), votes, stake); + + Ok(()) + } + + #[benchmark] + fn remove_voter() -> Result<(), BenchmarkError> { + // We fix the number of voted candidates to max. let v = T::MaxVotesPerVoter::get(); clean::(); - // create a bunch of candidates. + // Create a bunch of candidates. let all_candidates = submit_candidates::(v, "candidates")?; let caller = endowed_account::("caller", 0); @@ -224,207 +243,245 @@ benchmarks! { submit_voter::(caller.clone(), all_candidates, stake)?; whitelist!(caller); - }: _(RawOrigin::Signed(caller)) - submit_candidacy { - // number of already existing candidates. - let c in 1 .. T::MaxCandidates::get(); - // we fix the number of members to the number of desired members and runners-up. We'll be in - // this state almost always. + #[extrinsic_call] + _(RawOrigin::Signed(caller)); + + Ok(()) + } + + #[benchmark] + fn submit_candidacy( + // Number of already existing candidates. + c: Linear<1, { T::MaxCandidates::get() }>, + ) -> Result<(), BenchmarkError> { + // We fix the number of members to the number of desired members and runners-up. + // We'll be in this state almost always. let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); clean::(); - let stake = default_stake::(c); - // create m members and runners combined. + // Create `m` members and runners combined. let _ = fill_seats_up_to::(m)?; - // create previous candidates; + // Create previous candidates. let _ = submit_candidates::(c, "candidates")?; - // we assume worse case that: extrinsic is successful and candidate is not duplicate. + // We assume worse case that: extrinsic is successful and candidate is not duplicate. let candidate_account = endowed_account::("caller", 0); whitelist!(candidate_account); - }: _(RawOrigin::Signed(candidate_account.clone()), candidate_count::()) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(candidate_account), candidate_count::()); + + // Reset members in between benchmark tests. #[cfg(test)] - { - // reset members in between benchmark tests. - use crate::tests::MEMBERS; - MEMBERS.with(|m| *m.borrow_mut() = vec![]); - } + MEMBERS.with(|m| *m.borrow_mut() = vec![]); + + Ok(()) } - renounce_candidacy_candidate { - // this will check members, runners-up and candidate for removal. Members and runners-up are - // limited by the runtime bound, nonetheless we fill them by `m`. - // number of already existing candidates. - let c in 1 .. T::MaxCandidates::get(); - // we fix the number of members to the number of desired members and runners-up. We'll be in - // this state almost always. + #[benchmark] + fn renounce_candidacy_candidate( + // This will check members, runners-up and candidate for removal. + // Members and runners-up are limited by the runtime bound, nonetheless we fill them by + // `m`. + // Number of already existing candidates. + c: Linear<1, { T::MaxCandidates::get() }>, + ) -> Result<(), BenchmarkError> { + // We fix the number of members to the number of desired members and runners-up. + // We'll be in this state almost always. let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); clean::(); - // create m members and runners combined. + // Create `m` members and runners combined. let _ = fill_seats_up_to::(m)?; let all_candidates = submit_candidates::(c, "caller")?; let bailing = all_candidates[0].clone(); // Should be ("caller", 0) let count = candidate_count::(); whitelist!(bailing); - }: renounce_candidacy(RawOrigin::Signed(bailing), Renouncing::Candidate(count)) - verify { + + #[extrinsic_call] + renounce_candidacy(RawOrigin::Signed(bailing), Renouncing::Candidate(count)); + + // Reset members in between benchmark tests. #[cfg(test)] - { - // reset members in between benchmark tests. - use crate::tests::MEMBERS; - MEMBERS.with(|m| *m.borrow_mut() = vec![]); - } + MEMBERS.with(|m| *m.borrow_mut() = vec![]); + + Ok(()) } - renounce_candidacy_members { - // removing members and runners will be cheaper than a candidate. - // we fix the number of members to when members and runners-up to the desired. We'll be in - // this state almost always. + #[benchmark] + fn renounce_candidacy_members() -> Result<(), BenchmarkError> { + // Removing members and runners will be cheaper than a candidate. + // We fix the number of members to when members and runners-up to the desired. + // We'll be in this state almost always. let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); clean::(); - // create m members and runners combined. + // Create `m` members and runners combined. let members_and_runners_up = fill_seats_up_to::(m)?; let bailing = members_and_runners_up[0].clone(); - assert!(Elections::::is_member(&bailing)); + assert!(Pallet::::is_member(&bailing)); whitelist!(bailing); - }: renounce_candidacy(RawOrigin::Signed(bailing.clone()), Renouncing::Member) - verify { + + #[extrinsic_call] + renounce_candidacy(RawOrigin::Signed(bailing.clone()), Renouncing::Member); + + // Reset members in between benchmark tests. #[cfg(test)] - { - // reset members in between benchmark tests. - use crate::tests::MEMBERS; - MEMBERS.with(|m| *m.borrow_mut() = vec![]); - } + MEMBERS.with(|m| *m.borrow_mut() = vec![]); + + Ok(()) } - renounce_candidacy_runners_up { - // removing members and runners will be cheaper than a candidate. - // we fix the number of members to when members and runners-up to the desired. We'll be in - // this state almost always. + #[benchmark] + fn renounce_candidacy_runners_up() -> Result<(), BenchmarkError> { + // Removing members and runners will be cheaper than a candidate. + // We fix the number of members to when members and runners-up to the desired. + // We'll be in this state almost always. let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); clean::(); - // create m members and runners combined. + // Create `m` members and runners combined. let members_and_runners_up = fill_seats_up_to::(m)?; let bailing = members_and_runners_up[T::DesiredMembers::get() as usize + 1].clone(); - assert!(Elections::::is_runner_up(&bailing)); + assert!(Pallet::::is_runner_up(&bailing)); whitelist!(bailing); - }: renounce_candidacy(RawOrigin::Signed(bailing.clone()), Renouncing::RunnerUp) - verify { + + #[extrinsic_call] + renounce_candidacy(RawOrigin::Signed(bailing.clone()), Renouncing::RunnerUp); + + // Reset members in between benchmark tests. #[cfg(test)] - { - // reset members in between benchmark tests. - use crate::tests::MEMBERS; - MEMBERS.with(|m| *m.borrow_mut() = vec![]); - } + MEMBERS.with(|m| *m.borrow_mut() = vec![]); + + Ok(()) } // We use the max block weight for this extrinsic for now. See below. - remove_member_without_replacement {}: { - Err(BenchmarkError::Override( - BenchmarkResult::from_weight(T::BlockWeights::get().max_block) - ))?; + #[benchmark] + fn remove_member_without_replacement() -> Result<(), BenchmarkError> { + #[block] + { + Err(BenchmarkError::Override(BenchmarkResult::from_weight( + T::BlockWeights::get().max_block, + )))?; + } + + Ok(()) } - remove_member_with_replacement { - // easy case. We have a runner up. Nothing will have that much of an impact. m will be - // number of members and runners. There is always at least one runner. + #[benchmark] + fn remove_member_with_replacement() -> Result<(), BenchmarkError> { + // Easy case. + // We have a runner up. + // Nothing will have that much of an impact. + // `m` will be number of members and runners. + // There is always at least one runner. let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); clean::(); let _ = fill_seats_up_to::(m)?; - let removing = as_lookup::(Elections::::members_ids()[0].clone()); - }: remove_member(RawOrigin::Root, removing, true, false) - verify { - // must still have enough members. + let removing = as_lookup::(Pallet::::members_ids()[0].clone()); + + #[extrinsic_call] + remove_member(RawOrigin::Root, removing, true, false); + + // Must still have enough members. assert_eq!(Members::::get().len() as u32, T::DesiredMembers::get()); + + // Reset members in between benchmark tests. #[cfg(test)] - { - // reset members in between benchmark tests. - use crate::tests::MEMBERS; - MEMBERS.with(|m| *m.borrow_mut() = vec![]); - } - } + MEMBERS.with(|m| *m.borrow_mut() = vec![]); - clean_defunct_voters { - // total number of voters. - let v in (T::MaxVoters::get() / 2) .. T::MaxVoters::get(); - // those that are defunct and need removal. - let d in 0 .. (T::MaxVoters::get() / 2); + Ok(()) + } - // remove any previous stuff. + #[benchmark] + fn clean_defunct_voters( + // Total number of voters. + v: Linear<{ T::MaxVoters::get() / 2 }, { T::MaxVoters::get() }>, + // Those that are defunct and need removal. + d: Linear<0, { T::MaxVoters::get() / 2 }>, + ) -> Result<(), BenchmarkError> { + // Remove any previous stuff. clean::(); let all_candidates = submit_candidates::(T::MaxCandidates::get(), "candidates")?; distribute_voters::(all_candidates, v, T::MaxVotesPerVoter::get() as usize)?; - // all candidates leave. + // All candidates leave. Candidates::::kill(); - // now everyone is defunct - assert!(Voting::::iter().all(|(_, v)| Elections::::is_defunct_voter(&v.votes))); + // Now everyone is defunct. + assert!(Voting::::iter().all(|(_, v)| Pallet::::is_defunct_voter(&v.votes))); assert_eq!(Voting::::iter().count() as u32, v); - let root = RawOrigin::Root; - }: _(root, v, d) - verify { + + #[extrinsic_call] + _(RawOrigin::Root, v, d); + assert_eq!(Voting::::iter().count() as u32, v - d); + + Ok(()) } - election_phragmen { - // This is just to focus on phragmen in the context of this module. We always select 20 - // members, this is hard-coded in the runtime and cannot be trivially changed at this stage. - // Yet, change the number of voters, candidates and edge per voter to see the impact. Note - // that we give all candidates a self vote to make sure they are all considered. - let c in 1 .. T::MaxCandidates::get(); - let v in 1 .. T::MaxVoters::get(); - let e in (T::MaxVoters::get()) .. T::MaxVoters::get() * T::MaxVotesPerVoter::get(); + #[benchmark] + fn election_phragmen( + // This is just to focus on phragmen in the context of this module. + // We always select 20 members, this is hard-coded in the runtime and cannot be trivially + // changed at this stage. Yet, change the number of voters, candidates and edge per voter + // to see the impact. Note that we give all candidates a self vote to make sure they are + // all considered. + c: Linear<1, { T::MaxCandidates::get() }>, + v: Linear<1, { T::MaxVoters::get() }>, + e: Linear<{ T::MaxVoters::get() }, { T::MaxVoters::get() * T::MaxVotesPerVoter::get() }>, + ) -> Result<(), BenchmarkError> { clean::(); - // so we have a situation with v and e. we want e to basically always be in the range of `e - // -> e * T::MaxVotesPerVoter::get()`, but we cannot express that now with the benchmarks. - // So what we do is: when c is being iterated, v, and e are max and fine. when v is being - // iterated, e is being set to max and this is a problem. In these cases, we cap e to a - // lower value, namely v * T::MaxVotesPerVoter::get(). when e is being iterated, v is at - // max, and again fine. all in all, votes_per_voter can never be more than - // T::MaxVotesPerVoter::get(). Note that this might cause `v` to be an overestimate. + // So we have a situation with `v` and `e`. + // We want `e` to basically always be in the range of + // `e -> e * T::MaxVotesPerVoter::get()`, but we cannot express that now with the + // benchmarks. So what we do is: when `c` is being iterated, `v`, and `e` are max and + // fine. When `v` is being iterated, `e` is being set to max and this is a problem. + // In these cases, we cap `e` to a lower value, namely `v * T::MaxVotesPerVoter::get()`. + // When `e` is being iterated, `v` is at max, and again fine. + // All in all, `votes_per_voter` can never be more than `T::MaxVotesPerVoter::get()`. + // Note that this might cause `v` to be an overestimate. let votes_per_voter = (e / v).min(T::MaxVotesPerVoter::get()); let all_candidates = submit_candidates_with_self_vote::(c, "candidates")?; - let _ = distribute_voters::(all_candidates, v.saturating_sub(c), votes_per_voter as usize)?; - }: { - Elections::::on_initialize(T::TermDuration::get()); - } - verify { + let _ = + distribute_voters::(all_candidates, v.saturating_sub(c), votes_per_voter as usize)?; + + #[block] + { + Pallet::::on_initialize(T::TermDuration::get()); + } + assert_eq!(Members::::get().len() as u32, T::DesiredMembers::get().min(c)); assert_eq!( RunnersUp::::get().len() as u32, T::DesiredRunnersUp::get().min(c.saturating_sub(T::DesiredMembers::get())), ); + // reset members in between benchmark tests. #[cfg(test)] - { - // reset members in between benchmark tests. - use crate::tests::MEMBERS; - MEMBERS.with(|m| *m.borrow_mut() = vec![]); - } + MEMBERS.with(|m| *m.borrow_mut() = vec![]); + + Ok(()) } - impl_benchmark_test_suite!( - Elections, - crate::tests::ExtBuilder::default().desired_members(13).desired_runners_up(7), - crate::tests::Test, + impl_benchmark_test_suite! { + Pallet, + tests::ExtBuilder::default().desired_members(13).desired_runners_up(7), + tests::Test, exec_name = build_and_execute, - ); + } } diff --git a/substrate/frame/examples/multi-block-migrations/src/migrations/v1/weights.rs b/substrate/frame/examples/multi-block-migrations/src/migrations/v1/weights.rs index 6a5cf2ac59365460c3f556457ea618e7f7696f7b..a436d6a8ab40b055bf0e25dde67ad729d2276706 100644 --- a/substrate/frame/examples/multi-block-migrations/src/migrations/v1/weights.rs +++ b/substrate/frame/examples/multi-block-migrations/src/migrations/v1/weights.rs @@ -33,7 +33,7 @@ // --pallet // pallet_example_mbm // --extrinsic -// +// // --template // substrate/.maintain/frame-weight-template.hbs // --output diff --git a/substrate/frame/glutton/src/benchmarking.rs b/substrate/frame/glutton/src/benchmarking.rs index 0b1309e6330465580d4b2829efc3d2de33e53fd1..b5fbbd4cd200caada731c3e7a7e1e9c0a54c5fd7 100644 --- a/substrate/frame/glutton/src/benchmarking.rs +++ b/substrate/frame/glutton/src/benchmarking.rs @@ -20,80 +20,122 @@ //! Has to be compiled and run twice to calibrate on new hardware. #[cfg(feature = "runtime-benchmarks")] -use super::*; - -use frame_benchmarking::benchmarks; +use frame_benchmarking::v2::*; use frame_support::{pallet_prelude::*, weights::constants::*}; -use frame_system::RawOrigin as SystemOrigin; +use frame_system::RawOrigin; use sp_runtime::{traits::One, Perbill}; -use crate::Pallet as Glutton; -use frame_system::Pallet as System; +use crate::*; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn initialize_pallet_grow(n: Linear<0, 1_000>) -> Result<(), BenchmarkError> { + #[block] + { + Pallet::::initialize_pallet(RawOrigin::Root.into(), n, None)?; + } -benchmarks! { - initialize_pallet_grow { - let n in 0 .. 1_000; - }: { - Glutton::::initialize_pallet(SystemOrigin::Root.into(), n, None).unwrap() - } verify { assert_eq!(TrashDataCount::::get(), n); + + Ok(()) } - initialize_pallet_shrink { - let n in 0 .. 1_000; + #[benchmark] + fn initialize_pallet_shrink(n: Linear<0, 1_000>) -> Result<(), BenchmarkError> { + Pallet::::initialize_pallet(RawOrigin::Root.into(), n, None)?; + + #[block] + { + Pallet::::initialize_pallet(RawOrigin::Root.into(), 0, Some(n))?; + } - Glutton::::initialize_pallet(SystemOrigin::Root.into(), n, None).unwrap(); - }: { - Glutton::::initialize_pallet(SystemOrigin::Root.into(), 0, Some(n)).unwrap() - } verify { assert_eq!(TrashDataCount::::get(), 0); - } - waste_ref_time_iter { - let i in 0..100_000; - }: { - Glutton::::waste_ref_time_iter(vec![0u8; 64], i); + Ok(()) } - waste_proof_size_some { - let i in 0..5_000; + #[benchmark] + fn waste_ref_time_iter(i: Linear<0, 100_000>) { + #[block] + { + Pallet::::waste_ref_time_iter(vec![0u8; 64], i); + } + } + #[benchmark] + fn waste_proof_size_some(i: Linear<0, 5_000>) { (0..5000).for_each(|i| TrashData::::insert(i, [i as u8; 1024])); - }: { - (0..i).for_each(|i| { - TrashData::::get(i); - }) + + #[block] + { + (0..i).for_each(|i| { + TrashData::::get(i); + }) + } } // For manual verification only. - on_idle_high_proof_waste { + #[benchmark] + fn on_idle_high_proof_waste() { (0..5000).for_each(|i| TrashData::::insert(i, [i as u8; 1024])); - let _ = Glutton::::set_compute(SystemOrigin::Root.into(), One::one()); - let _ = Glutton::::set_storage(SystemOrigin::Root.into(), One::one()); - }: { - let weight = Glutton::::on_idle(System::::block_number(), Weight::from_parts(WEIGHT_REF_TIME_PER_MILLIS * 100, WEIGHT_PROOF_SIZE_PER_MB * 5)); + let _ = Pallet::::set_compute(RawOrigin::Root.into(), One::one()); + let _ = Pallet::::set_storage(RawOrigin::Root.into(), One::one()); + + #[block] + { + Pallet::::on_idle( + frame_system::Pallet::::block_number(), + Weight::from_parts(WEIGHT_REF_TIME_PER_MILLIS * 100, WEIGHT_PROOF_SIZE_PER_MB * 5), + ); + } } // For manual verification only. - on_idle_low_proof_waste { + #[benchmark] + fn on_idle_low_proof_waste() { (0..5000).for_each(|i| TrashData::::insert(i, [i as u8; 1024])); - let _ = Glutton::::set_compute(SystemOrigin::Root.into(), One::one()); - let _ = Glutton::::set_storage(SystemOrigin::Root.into(), One::one()); - }: { - let weight = Glutton::::on_idle(System::::block_number(), Weight::from_parts(WEIGHT_REF_TIME_PER_MILLIS * 100, WEIGHT_PROOF_SIZE_PER_KB * 20)); + let _ = Pallet::::set_compute(RawOrigin::Root.into(), One::one()); + let _ = Pallet::::set_storage(RawOrigin::Root.into(), One::one()); + + #[block] + { + Pallet::::on_idle( + frame_system::Pallet::::block_number(), + Weight::from_parts(WEIGHT_REF_TIME_PER_MILLIS * 100, WEIGHT_PROOF_SIZE_PER_KB * 20), + ); + } } - empty_on_idle { - }: { + #[benchmark] + fn empty_on_idle() { // Enough weight to do nothing. - Glutton::::on_idle(System::::block_number(), T::WeightInfo::empty_on_idle()); + #[block] + { + Pallet::::on_idle( + frame_system::Pallet::::block_number(), + T::WeightInfo::empty_on_idle(), + ); + } } - set_compute { - }: _(SystemOrigin::Root, FixedU64::from_perbill(Perbill::from_percent(50))) + #[benchmark] + fn set_compute() { + #[extrinsic_call] + _(RawOrigin::Root, FixedU64::from_perbill(Perbill::from_percent(50))); + } - set_storage { - }: _(SystemOrigin::Root, FixedU64::from_perbill(Perbill::from_percent(50))) + #[benchmark] + fn set_storage() { + #[extrinsic_call] + _(RawOrigin::Root, FixedU64::from_perbill(Perbill::from_percent(50))); + } - impl_benchmark_test_suite!(Glutton, crate::mock::new_test_ext(), crate::mock::Test); + impl_benchmark_test_suite! { + Pallet, + mock::new_test_ext(), + mock::Test + } } diff --git a/substrate/frame/identity/src/lib.rs b/substrate/frame/identity/src/lib.rs index 11b43f958c4ebf3632f4a345cce5b546fbc2ee05..6a71e831cca1131d35053deb4277c59bcf4a95d2 100644 --- a/substrate/frame/identity/src/lib.rs +++ b/substrate/frame/identity/src/lib.rs @@ -421,6 +421,10 @@ pub mod pallet { RegistrarAdded { registrar_index: RegistrarIndex }, /// A sub-identity was added to an identity and the deposit paid. SubIdentityAdded { sub: T::AccountId, main: T::AccountId, deposit: BalanceOf }, + /// An account's sub-identities were set (in bulk). + SubIdentitiesSet { main: T::AccountId, number_of_subs: u32, new_deposit: BalanceOf }, + /// A given sub-account's associated name was changed by its super-identity. + SubIdentityRenamed { sub: T::AccountId, main: T::AccountId }, /// A sub-identity was removed from an identity and the deposit freed. SubIdentityRemoved { sub: T::AccountId, main: T::AccountId, deposit: BalanceOf }, /// A sub-identity was cleared, and the given deposit repatriated from the @@ -591,6 +595,12 @@ pub mod pallet { SubsOf::::insert(&sender, (new_deposit, ids)); } + Self::deposit_event(Event::SubIdentitiesSet { + main: sender, + number_of_subs: new_subs as u32, + new_deposit, + }); + Ok(Some( T::WeightInfo::set_subs_old(old_ids.len() as u32) // P: Real number of old accounts removed. // S: New subs added @@ -992,7 +1002,9 @@ pub mod pallet { let sub = T::Lookup::lookup(sub)?; ensure!(IdentityOf::::contains_key(&sender), Error::::NoIdentity); ensure!(SuperOf::::get(&sub).map_or(false, |x| x.0 == sender), Error::::NotOwned); - SuperOf::::insert(&sub, (sender, data)); + SuperOf::::insert(&sub, (&sender, data)); + + Self::deposit_event(Event::SubIdentityRenamed { main: sender, sub }); Ok(()) } diff --git a/substrate/frame/identity/src/tests.rs b/substrate/frame/identity/src/tests.rs index a095085a81882075567b3d123fb8c7bf378c3af3..7bf5b2a727607e910bc7a2470c383aa236a18e35 100644 --- a/substrate/frame/identity/src/tests.rs +++ b/substrate/frame/identity/src/tests.rs @@ -273,6 +273,10 @@ fn editing_subaccounts_should_work() { // rename first sub account assert_ok!(Identity::rename_sub(RuntimeOrigin::signed(ten.clone()), one.clone(), data(11))); + System::assert_last_event(tests::RuntimeEvent::Identity(Event::SubIdentityRenamed { + main: ten.clone(), + sub: one.clone(), + })); assert_eq!(SuperOf::::get(one.clone()), Some((ten.clone(), data(11)))); assert_eq!(SuperOf::::get(two.clone()), Some((ten.clone(), data(2)))); assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - 2 * sub_deposit); @@ -546,6 +550,13 @@ fn setting_subaccounts_should_work() { assert_ok!(Identity::set_identity(RuntimeOrigin::signed(ten.clone()), Box::new(ten_info))); assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit); assert_ok!(Identity::set_subs(RuntimeOrigin::signed(ten.clone()), subs.clone())); + + System::assert_last_event(tests::RuntimeEvent::Identity(Event::SubIdentitiesSet { + main: ten.clone(), + number_of_subs: 1, + new_deposit: sub_deposit, + })); + assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - sub_deposit); assert_eq!( SubsOf::::get(ten.clone()), diff --git a/substrate/frame/im-online/src/benchmarking.rs b/substrate/frame/im-online/src/benchmarking.rs index d8170d4817e3e5f6b53db44076f7896089d6ee57..439720bcab38aadd93dc5f7395d7fc033029701b 100644 --- a/substrate/frame/im-online/src/benchmarking.rs +++ b/substrate/frame/im-online/src/benchmarking.rs @@ -19,9 +19,7 @@ #![cfg(feature = "runtime-benchmarks")] -use super::*; - -use frame_benchmarking::v1::benchmarks; +use frame_benchmarking::v2::*; use frame_support::{traits::UnfilteredDispatchable, WeakBoundedVec}; use frame_system::RawOrigin; use sp_runtime::{ @@ -29,7 +27,7 @@ use sp_runtime::{ transaction_validity::TransactionSource, }; -use crate::Pallet as ImOnline; +use crate::*; const MAX_KEYS: u32 = 1000; @@ -64,34 +62,55 @@ pub fn create_heartbeat( Ok((input_heartbeat, signature)) } -benchmarks! { - #[extra] - heartbeat { - let k in 1 .. MAX_KEYS; +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark(extra)] + fn heartbeat(k: Linear<1, MAX_KEYS>) -> Result<(), BenchmarkError> { let (input_heartbeat, signature) = create_heartbeat::(k)?; - }: _(RawOrigin::None, input_heartbeat, signature) - #[extra] - validate_unsigned { - let k in 1 .. MAX_KEYS; + #[extrinsic_call] + _(RawOrigin::None, input_heartbeat, signature); + + Ok(()) + } + + #[benchmark(extra)] + fn validate_unsigned(k: Linear<1, MAX_KEYS>) -> Result<(), BenchmarkError> { let (input_heartbeat, signature) = create_heartbeat::(k)?; let call = Call::heartbeat { heartbeat: input_heartbeat, signature }; - }: { - ImOnline::::validate_unsigned(TransactionSource::InBlock, &call) - .map_err(<&str>::from)?; + + #[block] + { + Pallet::::validate_unsigned(TransactionSource::InBlock, &call) + .map_err(<&str>::from)?; + } + + Ok(()) } - validate_unsigned_and_then_heartbeat { - let k in 1 .. MAX_KEYS; + #[benchmark] + fn validate_unsigned_and_then_heartbeat(k: Linear<1, MAX_KEYS>) -> Result<(), BenchmarkError> { let (input_heartbeat, signature) = create_heartbeat::(k)?; let call = Call::heartbeat { heartbeat: input_heartbeat, signature }; let call_enc = call.encode(); - }: { - ImOnline::::validate_unsigned(TransactionSource::InBlock, &call).map_err(<&str>::from)?; - as Decode>::decode(&mut &*call_enc) - .expect("call is encoded above, encoding must be correct") - .dispatch_bypass_filter(RawOrigin::None.into())?; + + #[block] + { + Pallet::::validate_unsigned(TransactionSource::InBlock, &call) + .map_err(<&str>::from)?; + as Decode>::decode(&mut &*call_enc) + .expect("call is encoded above, encoding must be correct") + .dispatch_bypass_filter(RawOrigin::None.into())?; + } + + Ok(()) } - impl_benchmark_test_suite!(ImOnline, crate::mock::new_test_ext(), crate::mock::Runtime); + impl_benchmark_test_suite! { + Pallet, + mock::new_test_ext(), + mock::Runtime + } } diff --git a/substrate/frame/indices/src/benchmarking.rs b/substrate/frame/indices/src/benchmarking.rs index bd173815cb347b9259c2972f1ffb1ad7c3af63a4..28f5e3bf5cf08be62e76ba8ccf1206ff22d20783 100644 --- a/substrate/frame/indices/src/benchmarking.rs +++ b/substrate/frame/indices/src/benchmarking.rs @@ -19,26 +19,31 @@ #![cfg(feature = "runtime-benchmarks")] -use super::*; -use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; +use crate::*; +use frame_benchmarking::v2::*; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; -use crate::Pallet as Indices; - const SEED: u32 = 0; -benchmarks! { - claim { +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn claim() { let account_index = T::AccountIndex::from(SEED); let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - }: _(RawOrigin::Signed(caller.clone()), account_index) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), account_index); + assert_eq!(Accounts::::get(account_index).unwrap().0, caller); } - transfer { + #[benchmark] + fn transfer() -> Result<(), BenchmarkError> { let account_index = T::AccountIndex::from(SEED); // Setup accounts let caller: T::AccountId = whitelisted_caller(); @@ -47,25 +52,33 @@ benchmarks! { let recipient_lookup = T::Lookup::unlookup(recipient.clone()); T::Currency::make_free_balance_be(&recipient, BalanceOf::::max_value()); // Claim the index - Indices::::claim(RawOrigin::Signed(caller.clone()).into(), account_index)?; - }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, account_index) - verify { + Pallet::::claim(RawOrigin::Signed(caller.clone()).into(), account_index)?; + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), recipient_lookup, account_index); + assert_eq!(Accounts::::get(account_index).unwrap().0, recipient); + Ok(()) } - free { + #[benchmark] + fn free() -> Result<(), BenchmarkError> { let account_index = T::AccountIndex::from(SEED); // Setup accounts let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Claim the index - Indices::::claim(RawOrigin::Signed(caller.clone()).into(), account_index)?; - }: _(RawOrigin::Signed(caller.clone()), account_index) - verify { + Pallet::::claim(RawOrigin::Signed(caller.clone()).into(), account_index)?; + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), account_index); + assert_eq!(Accounts::::get(account_index), None); + Ok(()) } - force_transfer { + #[benchmark] + fn force_transfer() -> Result<(), BenchmarkError> { let account_index = T::AccountIndex::from(SEED); // Setup accounts let original: T::AccountId = account("original", 0, SEED); @@ -74,25 +87,32 @@ benchmarks! { let recipient_lookup = T::Lookup::unlookup(recipient.clone()); T::Currency::make_free_balance_be(&recipient, BalanceOf::::max_value()); // Claim the index - Indices::::claim(RawOrigin::Signed(original).into(), account_index)?; - }: _(RawOrigin::Root, recipient_lookup, account_index, false) - verify { + Pallet::::claim(RawOrigin::Signed(original).into(), account_index)?; + + #[extrinsic_call] + _(RawOrigin::Root, recipient_lookup, account_index, false); + assert_eq!(Accounts::::get(account_index).unwrap().0, recipient); + Ok(()) } - freeze { + #[benchmark] + fn freeze() -> Result<(), BenchmarkError> { let account_index = T::AccountIndex::from(SEED); // Setup accounts let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Claim the index - Indices::::claim(RawOrigin::Signed(caller.clone()).into(), account_index)?; - }: _(RawOrigin::Signed(caller.clone()), account_index) - verify { + Pallet::::claim(RawOrigin::Signed(caller.clone()).into(), account_index)?; + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), account_index); + assert_eq!(Accounts::::get(account_index).unwrap().2, true); + Ok(()) } // TODO in another PR: lookup and unlookup trait weights (not critical) - impl_benchmark_test_suite!(Indices, crate::mock::new_test_ext(), crate::mock::Test); + impl_benchmark_test_suite!(Pallet, mock::new_test_ext(), mock::Test); } diff --git a/substrate/frame/nomination-pools/runtime-api/src/lib.rs b/substrate/frame/nomination-pools/runtime-api/src/lib.rs index d81ad1dd4954e003680d5e467e5796526bc3edf0..4138dd22d898793f001bc010f338bf5d898c37c0 100644 --- a/substrate/frame/nomination-pools/runtime-api/src/lib.rs +++ b/substrate/frame/nomination-pools/runtime-api/src/lib.rs @@ -69,5 +69,8 @@ sp_api::decl_runtime_apis! { /// Total balance contributed to the pool. fn pool_balance(pool_id: PoolId) -> Balance; + + /// Returns the bonded account and reward account associated with the pool_id. + fn pool_accounts(pool_id: PoolId) -> (AccountId, AccountId); } } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 177c5da74d4ff63defd576322ed5097c90ef4a28..201b0af1d608034a1537a12a20321e670931df49 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -4020,6 +4020,13 @@ impl Pallet { T::StakeAdapter::total_balance(Pool::from(Self::generate_bonded_account(pool_id))) .unwrap_or_default() } + + /// Returns the bonded account and reward account associated with the pool_id. + pub fn api_pool_accounts(pool_id: PoolId) -> (T::AccountId, T::AccountId) { + let bonded_account = Self::generate_bonded_account(pool_id); + let reward_account = Self::generate_reward_account(pool_id); + (bonded_account, reward_account) + } } impl sp_staking::OnStakingUpdate> for Pallet { diff --git a/substrate/frame/recovery/src/benchmarking.rs b/substrate/frame/recovery/src/benchmarking.rs index b7639742a620271172460ef272a36d12aa902f9b..ee97cb77d301c6aca98eddafe5ca61148682d054 100644 --- a/substrate/frame/recovery/src/benchmarking.rs +++ b/substrate/frame/recovery/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use crate::Pallet; use alloc::{boxed::Box, vec, vec::Vec}; -use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::v2::*; use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; @@ -103,56 +103,55 @@ fn insert_recovery_account(caller: &T::AccountId, account: &T::Accoun >::insert(&account, recovery_config); } -benchmarks! { - as_recovered { +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn as_recovered() { let caller: T::AccountId = whitelisted_caller(); let recovered_account: T::AccountId = account("recovered_account", 0, SEED); let recovered_account_lookup = T::Lookup::unlookup(recovered_account.clone()); - let call: ::RuntimeCall = frame_system::Call::::remark { remark: vec![] }.into(); + let call: ::RuntimeCall = + frame_system::Call::::remark { remark: vec![] }.into(); Proxy::::insert(&caller, &recovered_account); - }: _( - RawOrigin::Signed(caller), - recovered_account_lookup, - Box::new(call) - ) - set_recovered { + #[extrinsic_call] + _(RawOrigin::Signed(caller), recovered_account_lookup, Box::new(call)) + } + + #[benchmark] + fn set_recovered() { let lost: T::AccountId = whitelisted_caller(); let lost_lookup = T::Lookup::unlookup(lost.clone()); let rescuer: T::AccountId = whitelisted_caller(); let rescuer_lookup = T::Lookup::unlookup(rescuer.clone()); - }: _( - RawOrigin::Root, - lost_lookup, - rescuer_lookup - ) verify { + + #[extrinsic_call] + _(RawOrigin::Root, lost_lookup, rescuer_lookup); + assert_last_event::( - Event::AccountRecovered { - lost_account: lost, - rescuer_account: rescuer, - }.into() + Event::AccountRecovered { lost_account: lost, rescuer_account: rescuer }.into(), ); } - create_recovery { - let n in 1 .. T::MaxFriends::get(); - + #[benchmark] + fn create_recovery(n: Linear<1, { T::MaxFriends::get() }>) { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Create friends let friends = generate_friends::(n); - }: _( - RawOrigin::Signed(caller.clone()), - friends, - n as u16, - DEFAULT_DELAY.into() - ) verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), friends, n as u16, DEFAULT_DELAY.into()); + assert_last_event::(Event::RecoveryCreated { account: caller }.into()); } - initiate_recovery { + #[benchmark] + fn initiate_recovery() { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -160,28 +159,23 @@ benchmarks! { let lost_account_lookup = T::Lookup::unlookup(lost_account.clone()); insert_recovery_account::(&caller, &lost_account); - }: _( - RawOrigin::Signed(caller.clone()), - lost_account_lookup - ) verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), lost_account_lookup); + assert_last_event::( - Event::RecoveryInitiated { - lost_account: lost_account, - rescuer_account: caller, - }.into() + Event::RecoveryInitiated { lost_account, rescuer_account: caller }.into(), ); } - vouch_recovery { - let n in 1 .. T::MaxFriends::get(); - + #[benchmark] + fn vouch_recovery(n: Linear<1, { T::MaxFriends::get() }>) { let caller: T::AccountId = whitelisted_caller(); let lost_account: T::AccountId = account("lost_account", 0, SEED); let lost_account_lookup = T::Lookup::unlookup(lost_account.clone()); let rescuer_account: T::AccountId = account("rescuer_account", 0, SEED); let rescuer_account_lookup = T::Lookup::unlookup(rescuer_account.clone()); - // Create friends let friends = add_caller_and_generate_friends::(caller.clone(), n); let bounded_friends: FriendsOf = friends.try_into().unwrap(); @@ -212,23 +206,15 @@ benchmarks! { // Create the active recovery storage item >::insert(&lost_account, &rescuer_account, recovery_status); - }: _( - RawOrigin::Signed(caller.clone()), - lost_account_lookup, - rescuer_account_lookup - ) verify { + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), lost_account_lookup, rescuer_account_lookup); assert_last_event::( - Event::RecoveryVouched { - lost_account: lost_account, - rescuer_account: rescuer_account, - sender: caller, - }.into() + Event::RecoveryVouched { lost_account, rescuer_account, sender: caller }.into(), ); } - claim_recovery { - let n in 1 .. T::MaxFriends::get(); - + #[benchmark] + fn claim_recovery(n: Linear<1, { T::MaxFriends::get() }>) { let caller: T::AccountId = whitelisted_caller(); let lost_account: T::AccountId = account("lost_account", 0, SEED); let lost_account_lookup = T::Lookup::unlookup(lost_account.clone()); @@ -264,25 +250,20 @@ benchmarks! { // Create the active recovery storage item >::insert(&lost_account, &caller, recovery_status); - }: _( - RawOrigin::Signed(caller.clone()), - lost_account_lookup - ) verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), lost_account_lookup); assert_last_event::( - Event::AccountRecovered { - lost_account: lost_account, - rescuer_account: caller, - }.into() + Event::AccountRecovered { lost_account, rescuer_account: caller }.into(), ); } - close_recovery { + #[benchmark] + fn close_recovery(n: Linear<1, { T::MaxFriends::get() }>) { let caller: T::AccountId = whitelisted_caller(); let rescuer_account: T::AccountId = account("rescuer_account", 0, SEED); let rescuer_account_lookup = T::Lookup::unlookup(rescuer_account.clone()); - let n in 1 .. T::MaxFriends::get(); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); T::Currency::make_free_balance_be(&rescuer_account, BalanceOf::::max_value()); @@ -315,21 +296,16 @@ benchmarks! { // Create the active recovery storage item >::insert(&caller, &rescuer_account, recovery_status); - }: _( - RawOrigin::Signed(caller.clone()), - rescuer_account_lookup - ) verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), rescuer_account_lookup); assert_last_event::( - Event::RecoveryClosed { - lost_account: caller, - rescuer_account: rescuer_account, - }.into() + Event::RecoveryClosed { lost_account: caller, rescuer_account }.into(), ); } - remove_recovery { - let n in 1 .. T::MaxFriends::get(); - + #[benchmark] + fn remove_recovery(n: Linear<1, { T::MaxFriends::get() }>) { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -353,17 +329,14 @@ benchmarks! { // Reserve deposit for recovery T::Currency::reserve(&caller, total_deposit).unwrap(); - }: _( - RawOrigin::Signed(caller.clone()) - ) verify { - assert_last_event::( - Event::RecoveryRemoved { - lost_account: caller - }.into() - ); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone())); + assert_last_event::(Event::RecoveryRemoved { lost_account: caller }.into()); } - cancel_recovered { + #[benchmark] + fn cancel_recovered() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account: T::AccountId = account("account", 0, SEED); let account_lookup = T::Lookup::unlookup(account.clone()); @@ -373,10 +346,12 @@ benchmarks! { frame_system::Pallet::::inc_consumers(&caller)?; Proxy::::insert(&caller, &account); - }: _( - RawOrigin::Signed(caller), - account_lookup - ) + + #[extrinsic_call] + _(RawOrigin::Signed(caller), account_lookup); + + Ok(()) + } impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/substrate/frame/remark/src/benchmarking.rs b/substrate/frame/remark/src/benchmarking.rs index 15b72b4748dd406da7bcb551ffe3be2a29e4fca6..41d49c3b930bbd49de7c8a29ef79047782efa696 100644 --- a/substrate/frame/remark/src/benchmarking.rs +++ b/substrate/frame/remark/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use alloc::vec; -use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v2::*; use frame_system::{EventRecord, Pallet as System, RawOrigin}; #[cfg(test)] @@ -34,13 +34,24 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { assert_eq!(event, &system_event); } -benchmarks! { - store { - let l in 1 .. 1024*1024; +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn store(l: Linear<1, { 1024 * 1024 }>) { let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller.clone()), vec![0u8; l as usize]) - verify { - assert_last_event::(Event::Stored { sender: caller, content_hash: sp_io::hashing::blake2_256(&vec![0u8; l as usize]).into() }.into()); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), vec![0u8; l as usize]); + + assert_last_event::( + Event::Stored { + sender: caller, + content_hash: sp_io::hashing::blake2_256(&vec![0u8; l as usize]).into(), + } + .into(), + ); } impl_benchmark_test_suite!(Remark, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index 67bc1809cad7482fc3b7649a7e906680a605a3cc..81fbbc8cf38e1a384aac69e1676788f13c4a416f 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -40,7 +40,7 @@ frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } pallet-balances = { optional = true, workspace = true } -pallet-revive-fixtures = { workspace = true, default-features = false } +pallet-revive-fixtures = { workspace = true, default-features = false, optional = true } pallet-revive-uapi = { workspace = true, default-features = true } pallet-revive-proc-macro = { workspace = true, default-features = true } pallet-transaction-payment = { workspace = true } @@ -91,7 +91,7 @@ std = [ "log/std", "pallet-balances?/std", "pallet-proxy/std", - "pallet-revive-fixtures/std", + "pallet-revive-fixtures?/std", "pallet-timestamp/std", "pallet-transaction-payment/std", "pallet-utility/std", @@ -121,6 +121,7 @@ runtime-benchmarks = [ "pallet-balances/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", + "pallet-revive-fixtures", "pallet-timestamp/runtime-benchmarks", "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index bbd986d9d44c0e807cd0a2cd334b0fc98478da1b..a5b23e58c0d62887b9738283700a628ba8575da8 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -120,6 +120,7 @@ fn invoke_build(target: &Path, current_dir: &Path) -> Result<()> { .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) .env("RUSTC_BOOTSTRAP", "1") .env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default()) + .env("RUSTUP_TOOLCHAIN", env::var("RUSTUP_TOOLCHAIN").unwrap_or_default()) .args([ "build", "--release", @@ -147,8 +148,8 @@ fn invoke_build(target: &Path, current_dir: &Path) -> Result<()> { /// Post-process the compiled code. fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { - let strip = std::env::var(OVERRIDE_STRIP_ENV_VAR).map_or(false, |value| value == "1"); - let optimize = std::env::var(OVERRIDE_OPTIMIZE_ENV_VAR).map_or(true, |value| value == "1"); + let strip = env::var(OVERRIDE_STRIP_ENV_VAR).map_or(false, |value| value == "1"); + let optimize = env::var(OVERRIDE_OPTIMIZE_ENV_VAR).map_or(true, |value| value == "1"); let mut config = polkavm_linker::Config::default(); config.set_strip(strip); diff --git a/substrate/frame/revive/fixtures/contracts/drain.rs b/substrate/frame/revive/fixtures/contracts/drain.rs index 0d644a4238c4c44d9f70d9379d6249bebfd483f3..6e3e708a6b3d82917e7b2cf09b195fd4c80c1743 100644 --- a/substrate/frame/revive/fixtures/contracts/drain.rs +++ b/substrate/frame/revive/fixtures/contracts/drain.rs @@ -36,6 +36,15 @@ pub extern "C" fn call() { // Try to self-destruct by sending more balance to the 0 address. // The call will fail because a contract transfer has a keep alive requirement. - let res = api::transfer(&[0u8; 20], &u256_bytes(balance)); + let res = api::call( + uapi::CallFlags::empty(), + &[0u8; 20], + 0, + 0, + None, + &u256_bytes(balance), + &[], + None, + ); assert!(matches!(res, Err(uapi::ReturnErrorCode::TransferFailed))); } diff --git a/substrate/frame/revive/fixtures/contracts/return_data_api.rs b/substrate/frame/revive/fixtures/contracts/return_data_api.rs index 846396b0944d303479d98b6ff227c938b086ec62..2a390296a419035f5ff840c18c744918b1f347ea 100644 --- a/substrate/frame/revive/fixtures/contracts/return_data_api.rs +++ b/substrate/frame/revive/fixtures/contracts/return_data_api.rs @@ -35,11 +35,11 @@ static INPUT_DATA: [u8; INPUT_BUF_SIZE] = [0xFF; INPUT_BUF_SIZE]; const OUTPUT_BUF_SIZE: usize = INPUT_BUF_SIZE - 4; static OUTPUT_DATA: [u8; OUTPUT_BUF_SIZE] = [0xEE; OUTPUT_BUF_SIZE]; +/// Assert correct return data after calls and finally reset the return data. fn assert_return_data_after_call(input: &[u8]) { assert_return_data_size_of(OUTPUT_BUF_SIZE as u64); - assert_plain_transfer_does_not_reset(OUTPUT_BUF_SIZE as u64); assert_return_data_copy(&input[4..]); - reset_return_data(); + assert_balance_transfer_does_reset(); } /// Assert that what we get from [api::return_data_copy] matches `whole_return_data`, @@ -73,22 +73,6 @@ fn recursion_guard() -> [u8; 20] { own_address } -/// Call ourselves recursively, which panics the callee and thus resets the return data. -fn reset_return_data() { - api::call( - uapi::CallFlags::ALLOW_REENTRY, - &recursion_guard(), - 0u64, - 0u64, - None, - &[0u8; 32], - &[0u8; 32], - None, - ) - .unwrap_err(); - assert_return_data_size_of(0); -} - /// Assert [api::return_data_size] to match the `expected` value. fn assert_return_data_size_of(expected: u64) { let mut return_data_size = [0xff; 32]; @@ -96,11 +80,11 @@ fn assert_return_data_size_of(expected: u64) { assert_eq!(return_data_size, u256_bytes(expected)); } -/// Assert [api::return_data_size] to match the `expected` value after a plain transfer -/// (plain transfers don't issue a call and so should not reset the return data) -fn assert_plain_transfer_does_not_reset(expected: u64) { - api::transfer(&[0; 20], &u256_bytes(128)).unwrap(); - assert_return_data_size_of(expected); +/// Assert the return data to be reset after a balance transfer. +fn assert_balance_transfer_does_reset() { + api::call(uapi::CallFlags::empty(), &[0u8; 20], 0, 0, None, &u256_bytes(128), &[], None) + .unwrap(); + assert_return_data_size_of(0); } #[no_mangle] diff --git a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs index bfeca9b8b4a42d0276285cf7332ad6a9ddb77e09..09d45d0a8411a3a9fca37a8034aa880317ad1834 100644 --- a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs @@ -28,7 +28,16 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - let ret_code = match api::transfer(&[0u8; 20], &u256_bytes(100u64)) { + let ret_code = match api::call( + uapi::CallFlags::empty(), + &[0u8; 20], + 0, + 0, + None, + &u256_bytes(100u64), + &[], + None, + ) { Ok(_) => 0u32, Err(code) => code as u32, }; diff --git a/substrate/frame/revive/rpc/Cargo.toml b/substrate/frame/revive/rpc/Cargo.toml index e6d8c38c04f06dbaf368753abfb502f8ca3e0c3d..8bf9302402400c102c3ec122257f23c11e9c1be0 100644 --- a/substrate/frame/revive/rpc/Cargo.toml +++ b/substrate/frame/revive/rpc/Cargo.toml @@ -56,6 +56,7 @@ sp-core = { workspace = true, default-features = true } sp-weights = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } sc-rpc = { workspace = true, default-features = true } +sc-rpc-api = { workspace = true, default-features = true } sc-cli = { workspace = true, default-features = true } sc-service = { workspace = true, default-features = true } prometheus-endpoint = { workspace = true, default-features = true } @@ -77,3 +78,4 @@ example = ["hex", "hex-literal", "rlp", "secp256k1", "subxt-signer"] hex-literal = { workspace = true } pallet-revive-fixtures = { workspace = true } substrate-cli-test-utils = { workspace = true } +subxt-signer = { workspace = true, features = ["unstable-eth"] } diff --git a/substrate/frame/revive/rpc/examples/rust/transfer.rs b/substrate/frame/revive/rpc/examples/rust/transfer.rs index 185ad808e78738505c911ee595f3f6b6effbf0f1..b99d48a2f78e11ff24b0cccba8241b46d50ef62d 100644 --- a/substrate/frame/revive/rpc/examples/rust/transfer.rs +++ b/substrate/frame/revive/rpc/examples/rust/transfer.rs @@ -26,14 +26,14 @@ async fn main() -> anyhow::Result<()> { let alith = Account::default(); let client = HttpClientBuilder::default().build("http://localhost:8545")?; - let baltathar = Account::from(subxt_signer::eth::dev::baltathar()); - let value = 1_000_000_000_000_000_000u128.into(); // 1 ETH + let ethan = Account::from(subxt_signer::eth::dev::ethan()); + let value = 1_000_000_000_000_000_000_000u128.into(); let print_balance = || async { let balance = client.get_balance(alith.address(), BlockTag::Latest.into()).await?; println!("Alith {:?} balance: {balance:?}", alith.address()); - let balance = client.get_balance(baltathar.address(), BlockTag::Latest.into()).await?; - println!("Baltathar {:?} balance: {balance:?}", baltathar.address()); + let balance = client.get_balance(ethan.address(), BlockTag::Latest.into()).await?; + println!("ethan {:?} balance: {balance:?}", ethan.address()); anyhow::Result::<()>::Ok(()) }; @@ -41,14 +41,15 @@ async fn main() -> anyhow::Result<()> { println!("\n\n=== Transferring ===\n\n"); let hash = - send_transaction(&alith, &client, value, Bytes::default(), Some(baltathar.address())) - .await?; + send_transaction(&alith, &client, value, Bytes::default(), Some(ethan.address())).await?; println!("Transaction hash: {hash:?}"); - let ReceiptInfo { block_number, gas_used, .. } = wait_for_receipt(&client, hash).await?; + let ReceiptInfo { block_number, gas_used, status, .. } = + wait_for_receipt(&client, hash).await?; println!("Receipt: "); println!("- Block number: {block_number}"); println!("- Gas used: {gas_used}"); + println!("- Success: {status:?}"); print_balance().await?; Ok(()) diff --git a/substrate/frame/revive/rpc/revive_chain.metadata b/substrate/frame/revive/rpc/revive_chain.metadata index eef6dd2a0aeaa5b55ef9519aafb2301bd8cd51dc..305d079f9bd86786d1cd00fadac152eb856b26ee 100644 Binary files a/substrate/frame/revive/rpc/revive_chain.metadata and b/substrate/frame/revive/rpc/revive_chain.metadata differ diff --git a/substrate/frame/revive/rpc/src/cli.rs b/substrate/frame/revive/rpc/src/cli.rs index b95fa78bf3ee327c46aa935fe0da54dc5b6e4988..c0f81fcafd771377a31e7ae75fc29a75ceec60b8 100644 --- a/substrate/frame/revive/rpc/src/cli.rs +++ b/substrate/frame/revive/rpc/src/cli.rs @@ -15,7 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. //! The Ethereum JSON-RPC server. -use crate::{client::Client, EthRpcServer, EthRpcServerImpl}; +use crate::{ + client::Client, EthRpcServer, EthRpcServerImpl, SystemHealthRpcServer, + SystemHealthRpcServerImpl, +}; use clap::Parser; use futures::{pin_mut, FutureExt}; use jsonrpsee::server::RpcModule; @@ -109,23 +112,26 @@ pub fn run(cmd: CliCommand) -> anyhow::Result<()> { let tokio_handle = tokio_runtime.handle(); let signals = tokio_runtime.block_on(async { Signals::capture() })?; let mut task_manager = TaskManager::new(tokio_handle.clone(), prometheus_registry)?; - let spawn_handle = task_manager.spawn_handle(); + let essential_spawn_handle = task_manager.spawn_essential_handle(); let gen_rpc_module = || { let signals = tokio_runtime.block_on(async { Signals::capture() })?; - let fut = Client::from_url(&node_rpc_url, &spawn_handle).fuse(); + let fut = Client::from_url(&node_rpc_url, &essential_spawn_handle).fuse(); pin_mut!(fut); match tokio_handle.block_on(signals.try_until_signal(fut)) { Ok(Ok(client)) => rpc_module(is_dev, client), - Ok(Err(err)) => Err(sc_service::Error::Application(err.into())), + Ok(Err(err)) => { + log::error!("Error connecting to the node at {node_rpc_url}: {err}"); + Err(sc_service::Error::Application(err.into())) + }, Err(_) => Err(sc_service::Error::Application("Client connection interrupted".into())), } }; // Prometheus metrics. if let Some(PrometheusConfig { port, registry }) = prometheus_config.clone() { - spawn_handle.spawn( + task_manager.spawn_handle().spawn( "prometheus-endpoint", None, prometheus_endpoint::init_prometheus(port, registry).map(drop), @@ -142,11 +148,14 @@ pub fn run(cmd: CliCommand) -> anyhow::Result<()> { /// Create the JSON-RPC module. fn rpc_module(is_dev: bool, client: Client) -> Result, sc_service::Error> { - let eth_api = EthRpcServerImpl::new(client) + let eth_api = EthRpcServerImpl::new(client.clone()) .with_accounts(if is_dev { vec![crate::Account::default()] } else { vec![] }) .into_rpc(); + let health_api = SystemHealthRpcServerImpl::new(client).into_rpc(); + let mut module = RpcModule::new(()); module.merge(eth_api).map_err(|e| sc_service::Error::Application(e.into()))?; + module.merge(health_api).map_err(|e| sc_service::Error::Application(e.into()))?; Ok(module) } diff --git a/substrate/frame/revive/rpc/src/client.rs b/substrate/frame/revive/rpc/src/client.rs index 64f7f2a6161787c18364ab287747b61bf56261de..a0552189f4433bb91a2bb9daf0f659b2cf4ff674 100644 --- a/substrate/frame/revive/rpc/src/client.rs +++ b/substrate/frame/revive/rpc/src/client.rs @@ -35,7 +35,6 @@ use pallet_revive::{ }, EthContractResult, }; -use sc_service::SpawnTaskHandle; use sp_runtime::traits::{BlakeTwo256, Hash}; use sp_weights::Weight; use std::{ @@ -45,7 +44,7 @@ use std::{ }; use subxt::{ backend::{ - legacy::LegacyRpcMethods, + legacy::{rpc_methods::SystemHealth, LegacyRpcMethods}, rpc::{ reconnecting_rpc_client::{Client as ReconnectingRpcClient, ExponentialBackoff}, RpcClient, @@ -145,9 +144,6 @@ pub enum ClientError { /// The transaction fee could not be found #[error("TransactionFeePaid event not found")] TxFeeNotFound, - /// The token decimals property was not found - #[error("tokenDecimals not found in properties")] - TokenDecimalsNotFound, /// The cache is empty. #[error("Cache is empty")] CacheEmpty, @@ -165,7 +161,7 @@ impl From for ErrorObjectOwned { /// The number of recent blocks maintained by the cache. /// For each block in the cache, we also store the EVM transaction receipts. -pub const CACHE_SIZE: usize = 10; +pub const CACHE_SIZE: usize = 256; impl BlockCache { fn latest_block(&self) -> Option<&Arc> { @@ -196,6 +192,7 @@ impl BlockCache { } /// A client connect to a node and maintains a cache of the last `CACHE_SIZE` blocks. +#[derive(Clone)] pub struct Client { /// The inner state of the client. inner: Arc, @@ -228,7 +225,7 @@ impl ClientInner { let rpc = LegacyRpcMethods::::new(RpcClient::new(rpc_client.clone())); let (native_to_evm_ratio, chain_id, max_block_weight) = - tokio::try_join!(native_to_evm_ratio(&rpc), chain_id(&api), max_block_weight(&api))?; + tokio::try_join!(native_to_evm_ratio(&api), chain_id(&api), max_block_weight(&api))?; Ok(Self { api, rpc_client, rpc, cache, chain_id, max_block_weight, native_to_evm_ratio }) } @@ -238,6 +235,11 @@ impl ClientInner { value.saturating_mul(self.native_to_evm_ratio) } + /// Convert an evm balance to a native balance. + fn evm_to_native_decimals(&self, value: U256) -> U256 { + value / self.native_to_evm_ratio + } + /// Get the receipt infos from the extrinsics in a block. async fn receipt_infos( &self, @@ -274,7 +276,7 @@ impl ClientInner { .checked_div(gas_price.as_u128()) .unwrap_or_default(); - let success = events.find_first::().is_ok(); + let success = events.has::()?; let transaction_index = ext.index(); let transaction_hash = BlakeTwo256::hash(&Vec::from(ext.bytes()).encode()); let block_hash = block.hash(); @@ -319,16 +321,10 @@ async fn max_block_weight(api: &OnlineClient) -> Result) -> Result { - let props = rpc.system_properties().await?; - let eth_decimals = U256::from(18u32); - let native_decimals: U256 = props - .get("tokenDecimals") - .and_then(|v| v.as_number()?.as_u64()) - .ok_or(ClientError::TokenDecimalsNotFound)? - .into(); - - Ok(U256::from(10u32).pow(eth_decimals - native_decimals)) +async fn native_to_evm_ratio(api: &OnlineClient) -> Result { + let query = subxt_client::constants().revive().native_to_eth_ratio(); + let ratio = api.constants().at(&query)?; + Ok(U256::from(ratio)) } /// Extract the block timestamp. @@ -344,7 +340,10 @@ async fn extract_block_timestamp(block: &SubstrateBlock) -> Option { impl Client { /// Create a new client instance. /// The client will subscribe to new blocks and maintain a cache of [`CACHE_SIZE`] blocks. - pub async fn from_url(url: &str, spawn_handle: &SpawnTaskHandle) -> Result { + pub async fn from_url( + url: &str, + spawn_handle: &sc_service::SpawnEssentialTaskHandle, + ) -> Result { log::info!(target: LOG_TARGET, "Connecting to node at: {url} ..."); let inner: Arc = Arc::new(ClientInner::from_url(url).await?); log::info!(target: LOG_TARGET, "Connected to node at: {url}"); @@ -557,6 +556,12 @@ impl Client { cache.tx_hashes_by_block_and_index.get(block_hash).map(|v| v.len()) } + /// Get the system health. + pub async fn system_health(&self) -> Result { + let health = self.inner.rpc.system_health().await?; + Ok(health) + } + /// Get the balance of the given address. pub async fn balance( &self, @@ -619,9 +624,10 @@ impl Client { block: BlockNumberOrTagOrHash, ) -> Result, ClientError> { let runtime_api = self.runtime_api(&block).await?; - let value = tx - .value - .unwrap_or_default() + + let value = self + .inner + .evm_to_native_decimals(tx.value.unwrap_or_default()) .try_into() .map_err(|_| ClientError::ConversionFailed)?; diff --git a/substrate/frame/revive/rpc/src/lib.rs b/substrate/frame/revive/rpc/src/lib.rs index a6d47063ef9087a70878c4b44b4b2ccc3d189973..88a3cb641784b0606dc41e7cc0fe75fb5ae6942f 100644 --- a/substrate/frame/revive/rpc/src/lib.rs +++ b/substrate/frame/revive/rpc/src/lib.rs @@ -35,6 +35,9 @@ pub mod subxt_client; #[cfg(test)] mod tests; +mod rpc_health; +pub use rpc_health::*; + mod rpc_methods_gen; pub use rpc_methods_gen::*; diff --git a/substrate/frame/revive/rpc/src/rpc_health.rs b/substrate/frame/revive/rpc/src/rpc_health.rs new file mode 100644 index 0000000000000000000000000000000000000000..f94d4b82a80fbbb5df7f29f4963f7dfa4e9daa6a --- /dev/null +++ b/substrate/frame/revive/rpc/src/rpc_health.rs @@ -0,0 +1,50 @@ +// 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. +//! Heatlh JSON-RPC methods. + +use super::*; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use sc_rpc_api::system::helpers::Health; + +#[rpc(server, client)] +pub trait SystemHealthRpc { + /// Proxy the substrate chain system_health RPC call. + #[method(name = "system_health")] + async fn system_health(&self) -> RpcResult; +} + +pub struct SystemHealthRpcServerImpl { + client: client::Client, +} + +impl SystemHealthRpcServerImpl { + pub fn new(client: client::Client) -> Self { + Self { client } + } +} + +#[async_trait] +impl SystemHealthRpcServer for SystemHealthRpcServerImpl { + async fn system_health(&self) -> RpcResult { + let health = self.client.system_health().await?; + Ok(Health { + peers: health.peers, + is_syncing: health.is_syncing, + should_have_peers: health.should_have_peers, + }) + } +} diff --git a/substrate/frame/revive/rpc/src/tests.rs b/substrate/frame/revive/rpc/src/tests.rs index 5d84e06e9e044dc46de9f85621058bbfb077a50d..01fcb6ae3bd2f9a68d1b8ad395faa9ba1df07075 100644 --- a/substrate/frame/revive/rpc/src/tests.rs +++ b/substrate/frame/revive/rpc/src/tests.rs @@ -50,16 +50,14 @@ async fn ws_client_with_retry(url: &str) -> WsClient { async fn test_jsonrpsee_server() -> anyhow::Result<()> { // Start the node. let _ = thread::spawn(move || { - match start_node_inline(vec![ + if let Err(e) = start_node_inline(vec![ "--dev", "--rpc-port=45789", "--no-telemetry", "--no-prometheus", + "-lerror,evm=debug,sc_rpc_server=info,runtime::revive=debug", ]) { - Ok(_) => {}, - Err(e) => { - panic!("Node exited with error: {}", e); - }, + panic!("Node exited with error: {e:?}"); } }); @@ -69,17 +67,36 @@ async fn test_jsonrpsee_server() -> anyhow::Result<()> { "--rpc-port=45788", "--node-rpc-url=ws://localhost:45789", "--no-prometheus", + "-linfo,eth-rpc=debug", ]); - let _ = thread::spawn(move || match cli::run(args) { - Ok(_) => {}, - Err(e) => { - panic!("eth-rpc exited with error: {}", e); - }, + let _ = thread::spawn(move || { + if let Err(e) = cli::run(args) { + panic!("eth-rpc exited with error: {e:?}"); + } }); let client = ws_client_with_retry("ws://localhost:45788").await; let account = Account::default(); + // Balance transfer + let ethan = Account::from(subxt_signer::eth::dev::ethan()); + let ethan_balance = client.get_balance(ethan.address(), BlockTag::Latest.into()).await?; + assert_eq!(U256::zero(), ethan_balance); + + let value = 1_000_000_000_000_000_000_000u128.into(); + let hash = + send_transaction(&account, &client, value, Bytes::default(), Some(ethan.address())).await?; + + let receipt = wait_for_receipt(&client, hash).await?; + assert_eq!( + Some(ethan.address()), + receipt.to, + "Receipt should have the correct contract address." + ); + + let ethan_balance = client.get_balance(ethan.address(), BlockTag::Latest.into()).await?; + assert_eq!(value, ethan_balance, "ethan's balance should be the same as the value sent."); + // Deploy contract let data = b"hello world".to_vec(); let value = U256::from(5_000_000_000_000u128); @@ -96,11 +113,7 @@ async fn test_jsonrpsee_server() -> anyhow::Result<()> { ); let balance = client.get_balance(contract_address, BlockTag::Latest.into()).await?; - assert_eq!( - value * 1_000_000, - balance, - "Contract balance should be the same as the value sent." - ); + assert_eq!(value, balance, "Contract balance should be the same as the value sent."); // Call contract let hash = diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 3d1d7d2a224a247311116cc275dd46dfbc4e4432..593c16cbb2d8d67290f6de6d0b2888a9fae5e042 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -169,7 +169,7 @@ where }; if key == &key_new { - continue + continue; } child::put_raw(&child_trie_info, &key_new, &value); } @@ -1507,36 +1507,6 @@ mod benchmarks { Ok(()) } - // We transfer to unique accounts. - #[benchmark(pov_mode = Measured)] - fn seal_transfer() { - let account = account::("receiver", 0, 0); - let value = Pallet::::min_balance(); - assert!(value > 0u32.into()); - - let mut setup = CallSetup::::default(); - setup.set_balance(value); - let (mut ext, _) = setup.ext(); - let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); - - let account_bytes = account.encode(); - let account_len = account_bytes.len() as u32; - let value_bytes = Into::::into(value).encode(); - let mut memory = memory!(account_bytes, value_bytes,); - - let result; - #[block] - { - result = runtime.bench_transfer( - memory.as_mut_slice(), - 0, // account_ptr - account_len, // value_ptr - ); - } - - assert_ok!(result); - } - // t: with or without some value to transfer // i: size of the input data #[benchmark(pov_mode = Measured)] diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index 3acd67b32aabc0a40411c08b547d18fecb42f72f..d4c3440a3ea780949dbb671fd875c3c51416a063 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -190,7 +190,7 @@ impl<'a, Address: Decode, Signature: Decode, E: EthExtra> serde::Deserialize<'a> { let r = sp_core::bytes::deserialize(de)?; Decode::decode(&mut &r[..]) - .map_err(|e| serde::de::Error::custom(sp_runtime::format!("Decode error: {}", e))) + .map_err(|e| serde::de::Error::custom(alloc::format!("Decode error: {}", e))) } } @@ -313,10 +313,14 @@ pub trait EthExtra { return Err(InvalidTransaction::Call); } + let value = (value / U256::from(::NativeToEthRatio::get())) + .try_into() + .map_err(|_| InvalidTransaction::Call)?; + let call = if let Some(dest) = to { crate::Call::call:: { dest, - value: value.try_into().map_err(|_| InvalidTransaction::Call)?, + value, gas_limit, storage_deposit_limit, data: input.0, @@ -336,7 +340,7 @@ pub trait EthExtra { }; crate::Call::instantiate_with_code:: { - value: value.try_into().map_err(|_| InvalidTransaction::Call)?, + value, gas_limit, storage_deposit_limit, code: code.to_vec(), @@ -370,7 +374,7 @@ pub trait EthExtra { Default::default(), ) .into(); - log::debug!(target: LOG_TARGET, "try_into_checked_extrinsic: encoded_len: {encoded_len:?} actual_fee: {actual_fee:?} eth_fee: {eth_fee:?}"); + log::trace!(target: LOG_TARGET, "try_into_checked_extrinsic: encoded_len: {encoded_len:?} actual_fee: {actual_fee:?} eth_fee: {eth_fee:?}"); if eth_fee < actual_fee { log::debug!(target: LOG_TARGET, "fees {eth_fee:?} too low for the extrinsic {actual_fee:?}"); @@ -381,10 +385,10 @@ pub trait EthExtra { let max = actual_fee.max(eth_fee_no_tip); let diff = Percent::from_rational(max - min, min); if diff > Percent::from_percent(10) { - log::debug!(target: LOG_TARGET, "Difference between the extrinsic fees {actual_fee:?} and the Ethereum gas fees {eth_fee_no_tip:?} should be no more than 10% got {diff:?}"); + log::trace!(target: LOG_TARGET, "Difference between the extrinsic fees {actual_fee:?} and the Ethereum gas fees {eth_fee_no_tip:?} should be no more than 10% got {diff:?}"); return Err(InvalidTransaction::Call.into()) } else { - log::debug!(target: LOG_TARGET, "Difference between the extrinsic fees {actual_fee:?} and the Ethereum gas fees {eth_fee_no_tip:?}: {diff:?}"); + log::trace!(target: LOG_TARGET, "Difference between the extrinsic fees {actual_fee:?} and the Ethereum gas fees {eth_fee_no_tip:?}: {diff:?}"); } let tip = eth_fee.saturating_sub(eth_fee_no_tip); diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 8629a21c4fda70e170f3f61e751fc758dc05dcae..4f90b41b0de549b4103edc7eea8868b6b51dc853 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -236,9 +236,6 @@ pub trait Ext: sealing::Sealed { /// call stack. fn terminate(&mut self, beneficiary: &H160) -> DispatchResult; - /// Transfer some amount of funds into the specified account. - fn transfer(&mut self, to: &H160, value: U256) -> DispatchResult; - /// Returns the storage entry of the executing account by the given `key`. /// /// Returns `None` if the `key` wasn't previously set by `set_storage` or @@ -775,7 +772,7 @@ where )? { stack.run(executable, input_data).map(|_| stack.first_frame.last_frame_output) } else { - Self::transfer_from_origin(&origin, &dest, value) + Self::transfer_from_origin(&origin, &origin, &dest, value) } } @@ -1069,7 +1066,12 @@ where // If it is a delegate call, then we've already transferred tokens in the // last non-delegate frame. if delegated_code_hash.is_none() { - Self::transfer_from_origin(&caller, &frame.account_id, frame.value_transferred)?; + Self::transfer_from_origin( + &self.origin, + &caller, + &frame.account_id, + frame.value_transferred, + )?; } let contract_address = T::AddressMapper::to_address(&top_frame!(self).account_id); @@ -1265,17 +1267,48 @@ where } /// Transfer some funds from `from` to `to`. - fn transfer(from: &T::AccountId, to: &T::AccountId, value: BalanceOf) -> ExecResult { - // this avoids events to be emitted for zero balance transfers - if !value.is_zero() { - T::Currency::transfer(from, to, value, Preservation::Preserve) - .map_err(|_| Error::::TransferFailed)?; + /// + /// This is a no-op for zero `value`, avoiding events to be emitted for zero balance transfers. + /// + /// If the destination account does not exist, it is pulled into existence by transferring the + /// ED from `origin` to the new account. The total amount transferred to `to` will be ED + + /// `value`. This makes the ED fully transparent for contracts. + /// The ED transfer is executed atomically with the actual transfer, avoiding the possibility of + /// the ED transfer succeeding but the actual transfer failing. In other words, if the `to` does + /// not exist, the transfer does fail and nothing will be sent to `to` if either `origin` can + /// not provide the ED or transferring `value` from `from` to `to` fails. + /// Note: This will also fail if `origin` is root. + fn transfer( + origin: &Origin, + from: &T::AccountId, + to: &T::AccountId, + value: BalanceOf, + ) -> ExecResult { + if value.is_zero() { + return Ok(Default::default()); + } + + if >::account_exists(to) { + return T::Currency::transfer(from, to, value, Preservation::Preserve) + .map(|_| Default::default()) + .map_err(|_| Error::::TransferFailed.into()); } - Ok(Default::default()) + + let origin = origin.account_id()?; + let ed = ::Currency::minimum_balance(); + with_transaction(|| -> TransactionOutcome { + match T::Currency::transfer(origin, to, ed, Preservation::Preserve) + .and_then(|_| T::Currency::transfer(from, to, value, Preservation::Preserve)) + { + Ok(_) => TransactionOutcome::Commit(Ok(Default::default())), + Err(_) => TransactionOutcome::Rollback(Err(Error::::TransferFailed.into())), + } + }) } /// Same as `transfer` but `from` is an `Origin`. fn transfer_from_origin( + origin: &Origin, from: &Origin, to: &T::AccountId, value: BalanceOf, @@ -1287,7 +1320,7 @@ where Origin::Root if value.is_zero() => return Ok(Default::default()), Origin::Root => return Err(DispatchError::RootNotAllowed.into()), }; - Self::transfer(from, to, value) + Self::transfer(origin, from, to, value) } /// Reference to the current (top) frame. @@ -1411,7 +1444,13 @@ where )? { self.run(executable, input_data) } else { - Self::transfer(&self.account_id(), &dest, value).map(|_| ()) + Self::transfer_from_origin( + &self.origin, + &Origin::from_account_id(self.account_id().clone()), + &dest, + value, + )?; + Ok(()) } }; @@ -1509,16 +1548,6 @@ where Ok(()) } - fn transfer(&mut self, to: &H160, value: U256) -> DispatchResult { - Self::transfer( - &self.top_frame().account_id, - &T::AddressMapper::to_account_id(to), - value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, - ) - .map(|_| ()) - .map_err(|error| error.error) - } - fn get_storage(&mut self, key: &Key) -> Option> { self.top_frame_mut().contract_info().read(key) } @@ -2056,10 +2085,50 @@ mod tests { set_balance(&ALICE, 100); set_balance(&BOB, 0); - MockStack::transfer(&ALICE, &BOB, 55).unwrap(); + let origin = Origin::from_account_id(ALICE); + MockStack::transfer(&origin, &ALICE, &BOB, 55).unwrap(); - assert_eq!(get_balance(&ALICE), 45); - assert_eq!(get_balance(&BOB), 55); + let min_balance = ::Currency::minimum_balance(); + assert_eq!(get_balance(&ALICE), 45 - min_balance); + assert_eq!(get_balance(&BOB), 55 + min_balance); + }); + } + + #[test] + fn transfer_to_nonexistent_account_works() { + // This test verifies that a contract is able to transfer + // some funds to a nonexistant account and that those transfers + // are not able to reap accounts. + ExtBuilder::default().build().execute_with(|| { + let ed = ::Currency::minimum_balance(); + let value = 1024; + + // Transfers to nonexistant accounts should work + set_balance(&ALICE, ed * 2); + set_balance(&BOB, ed + value); + + assert_ok!(MockStack::transfer(&Origin::from_account_id(ALICE), &BOB, &CHARLIE, value)); + assert_eq!(get_balance(&ALICE), ed); + assert_eq!(get_balance(&BOB), ed); + assert_eq!(get_balance(&CHARLIE), ed + value); + + // Do not reap the origin account + set_balance(&ALICE, ed); + set_balance(&BOB, ed + value); + assert_err!( + MockStack::transfer(&Origin::from_account_id(ALICE), &BOB, &DJANGO, value), + >::TransferFailed + ); + + // Do not reap the sender account + set_balance(&ALICE, ed * 2); + set_balance(&BOB, value); + assert_err!( + MockStack::transfer(&Origin::from_account_id(ALICE), &BOB, &EVE, value), + >::TransferFailed + ); + // The ED transfer would work. But it should only be executed with the actual transfer + assert!(!System::account_exists(&EVE)); }); } @@ -2170,16 +2239,17 @@ mod tests { fn balance_too_low() { // This test verifies that a contract can't send value if it's // balance is too low. - let origin = ALICE; + let from = ALICE; + let origin = Origin::from_account_id(ALICE); let dest = BOB; ExtBuilder::default().build().execute_with(|| { - set_balance(&origin, 0); + set_balance(&from, 0); - let result = MockStack::transfer(&origin, &dest, 100); + let result = MockStack::transfer(&origin, &from, &dest, 100); assert_eq!(result, Err(Error::::TransferFailed.into())); - assert_eq!(get_balance(&origin), 0); + assert_eq!(get_balance(&from), 0); assert_eq!(get_balance(&dest), 0); }); } @@ -4383,12 +4453,19 @@ mod tests { &ExecReturnValue { flags: ReturnFlags::empty(), data: vec![127] } ); - // Plain transfers should not set the output - ctx.ext.transfer(&address, U256::from(1)).unwrap(); - assert_eq!( - ctx.ext.last_frame_output(), - &ExecReturnValue { flags: ReturnFlags::empty(), data: vec![127] } - ); + // Balance transfers should reset the output + ctx.ext + .call( + Weight::zero(), + U256::zero(), + &address, + U256::from(1), + vec![], + true, + false, + ) + .unwrap(); + assert_eq!(ctx.ext.last_frame_output(), &Default::default()); // Reverted instantiation should set the output ctx.ext diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 51e9a8fa3f9032eb3fac7a4ff12567e31d80da53..5038ae44afadb32bbcff581d2278cef6fccbfb4b 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -303,6 +303,10 @@ pub mod pallet { /// preventing replay attacks. #[pallet::constant] type ChainId: Get; + + /// The ratio between the decimal representation of the native token and the ETH token. + #[pallet::constant] + type NativeToEthRatio: Get; } /// Container for different types that implement [`DefaultConfig`]` of this pallet. @@ -374,7 +378,8 @@ pub mod pallet { type Xcm = (); type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; - type ChainId = ConstU64<{ 0 }>; + type ChainId = ConstU64<0>; + type NativeToEthRatio = ConstU32<1_000_000>; } } @@ -1239,6 +1244,7 @@ where T::Nonce: Into, T::Hash: frame_support::traits::IsType, { + log::debug!(target: LOG_TARGET, "bare_eth_transact: dest: {dest:?} value: {value:?} gas_limit: {gas_limit:?} storage_deposit_limit: {storage_deposit_limit:?}"); // Get the nonce to encode in the tx. let nonce: T::Nonce = >::account_nonce(&origin); @@ -1264,7 +1270,7 @@ where // Get the encoded size of the transaction. let tx = TransactionLegacyUnsigned { - value: value.into(), + value: value.into().saturating_mul(T::NativeToEthRatio::get().into()), input: input.into(), nonce: nonce.into(), chain_id: Some(T::ChainId::get().into()), @@ -1300,7 +1306,7 @@ where ) .into(); - log::debug!(target: LOG_TARGET, "bare_eth_call: len: {encoded_len:?} fee: {fee:?}"); + log::trace!(target: LOG_TARGET, "bare_eth_call: len: {encoded_len:?} fee: {fee:?}"); EthContractResult { gas_required: result.gas_required, storage_deposit: result.storage_deposit.charge_or_zero(), @@ -1339,7 +1345,7 @@ where let tx = TransactionLegacyUnsigned { gas: max_gas_fee.into(), nonce: nonce.into(), - value: value.into(), + value: value.into().saturating_mul(T::NativeToEthRatio::get().into()), input: input.clone().into(), gas_price: GAS_PRICE.into(), chain_id: Some(T::ChainId::get().into()), @@ -1373,7 +1379,7 @@ where ) .into(); - log::debug!(target: LOG_TARGET, "Call dry run Result: dispatch_info: {dispatch_info:?} len: {encoded_len:?} fee: {fee:?}"); + log::trace!(target: LOG_TARGET, "bare_eth_call: len: {encoded_len:?} fee: {fee:?}"); EthContractResult { gas_required: result.gas_required, storage_deposit: result.storage_deposit.charge_or_zero(), diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 2d9cae16c441c8f9846db503eaa6945900be7f08..a35e4d908601d74a39d0be1704456976d9bd3b8b 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -631,7 +631,10 @@ fn calling_plain_account_is_balance_transfer() { assert!(!>::contains_key(BOB_ADDR)); assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 0); let result = builder::bare_call(BOB_ADDR).value(42).build_and_unwrap_result(); - assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 42); + assert_eq!( + test_utils::get_balance(&BOB_FALLBACK), + 42 + ::Currency::minimum_balance() + ); assert_eq!(result, Default::default()); }); } @@ -1508,20 +1511,8 @@ fn call_return_code() { .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); - // Sending less than the minimum balance will also make the transfer fail - let result = builder::bare_call(bob.addr) - .data( - AsRef::<[u8]>::as_ref(&DJANGO_ADDR) - .iter() - .chain(&u256_bytes(42)) - .cloned() - .collect(), - ) - .build_and_unwrap_result(); - assert_return_code!(result, RuntimeReturnCode::TransferFailed); - - // Sending at least the minimum balance should result in success but - // no code called. + // Sending below the minimum balance should result in success. + // The ED is charged from the call origin. assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 0); let result = builder::bare_call(bob.addr) .data( @@ -1533,7 +1524,7 @@ fn call_return_code() { ) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::Success); - assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 55); + assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 55 + min_balance); let django = builder::bare_instantiate(Code::Upload(callee_code)) .origin(RuntimeOrigin::signed(CHARLIE)) diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 95257bee1c6bbd8d0768673f81976795810a583b..8310fe70101372e587b89f799c67ba8cce3f39e6 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -358,8 +358,6 @@ pub enum RuntimeCosts { GetTransientStorage(u32), /// Weight of calling `seal_take_transient_storage` for the given size. TakeTransientStorage(u32), - /// Weight of calling `seal_transfer`. - Transfer, /// Base weight of calling `seal_call`. CallBase, /// Weight of calling `seal_delegate_call` for the given input size. @@ -503,7 +501,6 @@ impl Token for RuntimeCosts { TakeTransientStorage(len) => { cost_storage!(write_transient, seal_take_transient_storage, len) }, - Transfer => T::WeightInfo::seal_transfer(), CallBase => T::WeightInfo::seal_call(0, 0), DelegateCallBase => T::WeightInfo::seal_delegate_call(), CallTransferSurcharge => cost_args!(seal_call, 1, 0), @@ -1235,29 +1232,6 @@ pub mod env { self.take_storage(memory, flags, key_ptr, key_len, out_ptr, out_len_ptr) } - /// Transfer some value to another account. - /// See [`pallet_revive_uapi::HostFn::transfer`]. - #[api_version(0)] - #[mutating] - fn transfer( - &mut self, - memory: &mut M, - address_ptr: u32, - value_ptr: u32, - ) -> Result { - self.charge_gas(RuntimeCosts::Transfer)?; - let callee = memory.read_h160(address_ptr)?; - let value: U256 = memory.read_u256(value_ptr)?; - let result = self.ext.transfer(&callee, value); - match result { - Ok(()) => Ok(ReturnErrorCode::Success), - Err(err) => { - let code = Self::err_into_return_code(err)?; - Ok(code) - }, - } - } - /// Make a call to another contract. /// See [`pallet_revive_uapi::HostFn::call`]. #[api_version(0)] diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index d1b1a63b4db621d9c35273ee589affc168d0450f..3c6a0be6ee75778d3e07bf6ed780dc620aac182e 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -106,7 +106,6 @@ pub trait WeightInfo { fn seal_get_transient_storage(n: u32, ) -> Weight; fn seal_contains_transient_storage(n: u32, ) -> Weight; fn seal_take_transient_storage(n: u32, ) -> Weight; - fn seal_transfer() -> Weight; fn seal_call(t: u32, i: u32, ) -> Weight; fn seal_delegate_call() -> Weight; fn seal_instantiate(i: u32, ) -> Weight; @@ -792,16 +791,6 @@ impl WeightInfo for SubstrateWeight { } /// Storage: `Revive::AddressSuffix` (r:1 w:0) /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) - fn seal_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `315` - // Estimated: `3780` - // Minimum execution time: 14_740_000 picoseconds. - Weight::from_parts(15_320_000, 3780) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } - /// Storage: `Revive::AddressSuffix` (r:1 w:0) - /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -1631,16 +1620,6 @@ impl WeightInfo for () { } /// Storage: `Revive::AddressSuffix` (r:1 w:0) /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) - fn seal_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `315` - // Estimated: `3780` - // Minimum execution time: 14_740_000 picoseconds. - Weight::from_parts(15_320_000, 3780) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - } - /// Storage: `Revive::AddressSuffix` (r:1 w:0) - /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index cf4cdeee0f284a2a51b2d9136b8d68799dab029f..cb52cf93540b1ab6be9ebde107db6c1eabf3ddf3 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -600,18 +600,6 @@ pub trait HostFn: private::Sealed { /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] fn take_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result; - /// Transfer some amount of funds into the specified account. - /// - /// # Parameters - /// - /// - `address`: The address of the account to transfer funds to. - /// - `value`: The U256 value to transfer. - /// - /// # Errors - /// - /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] - fn transfer(address: &[u8; 20], value: &[u8; 32]) -> Result; - /// Remove the calling account and transfer remaining **free** balance. /// /// This function never returns. Either the termination was successful and the diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index fc55bfbde1862ac0746ec4a9bd410a013d69440d..199a0abc3ddc9dd8a6f0cbdaea0a731b51699360 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -58,7 +58,6 @@ mod sys { out_ptr: *mut u8, out_len_ptr: *mut u32, ) -> ReturnCode; - pub fn transfer(address_ptr: *const u8, value_ptr: *const u8) -> ReturnCode; pub fn call(ptr: *const u8) -> ReturnCode; pub fn delegate_call( flags: u32, @@ -332,11 +331,6 @@ impl HostFn for HostFnImpl { ret_code.into() } - fn transfer(address: &[u8; 20], value: &[u8; 32]) -> Result { - let ret_code = unsafe { sys::transfer(address.as_ptr(), value.as_ptr()) }; - ret_code.into() - } - fn deposit_event(topics: &[[u8; 32]], data: &[u8]) { unsafe { sys::deposit_event( diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index a8d2c0b3fc56fe1eb4c5a6e19f8e93046aaf7f81..0ca36ca8545a8cb4fde650421099e5a8ad7384da 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -373,7 +373,10 @@ pub mod runtime { }; /// Types to define your runtime version. - pub use sp_version::{create_runtime_str, runtime_version, RuntimeVersion}; + // TODO: Remove deprecation suppression once + #[allow(deprecated)] + pub use sp_version::create_runtime_str; + pub use sp_version::{runtime_version, RuntimeVersion}; #[cfg(feature = "std")] pub use sp_version::NativeVersion; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 6c4fe8140e8ef2528ba65d6d279c87a7520cd721..d3423d82769dcba1fdd90288dbfe534605f81a62 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -349,6 +349,8 @@ impl Pallet { Self::deposit_event(Event::::PayoutStarted { era_index: era, validator_stash: stash.clone(), + page, + next: EraInfo::::get_next_claimable_page(era, &stash, &ledger), }); let mut total_imbalance = PositiveImbalanceOf::::zero(); @@ -1881,8 +1883,12 @@ impl StakingInterface for Pallet { } /// Whether `who` is a virtual staker whose funds are managed by another pallet. + /// + /// There is an assumption that, this account is keyless and managed by another pallet in the + /// runtime. Hence, it can never sign its own transactions. fn is_virtual_staker(who: &T::AccountId) -> bool { - VirtualStakers::::contains_key(who) + frame_system::Pallet::::account_nonce(who).is_zero() && + VirtualStakers::::contains_key(who) } fn slash_reward_fraction() -> Perbill { @@ -2103,6 +2109,10 @@ impl Pallet { Ledger::::get(stash.clone()).unwrap().stash == stash, "ledger corrupted for virtual staker" ); + ensure!( + frame_system::Pallet::::account_nonce(&stash).is_zero(), + "virtual stakers are keyless and should not have any nonce" + ); let reward_destination = >::get(stash.clone()).unwrap(); if let RewardDestination::Account(payee) = reward_destination { ensure!( diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 28aa4f89b6227e8c58dd832f15a5699838398bdd..5210bef853b29e9b3e02c2d3dd8746c8d9b7d22e 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -851,8 +851,13 @@ pub mod pallet { StakingElectionFailed, /// An account has stopped participating as either a validator or nominator. Chilled { stash: T::AccountId }, - /// The stakers' rewards are getting paid. - PayoutStarted { era_index: EraIndex, validator_stash: T::AccountId }, + /// A Page of stakers rewards are getting paid. `next` is `None` if all pages are claimed. + PayoutStarted { + era_index: EraIndex, + validator_stash: T::AccountId, + page: Page, + next: Option, + }, /// A validator has set their preferences. ValidatorPrefsSet { stash: T::AccountId, prefs: ValidatorPrefs }, /// Voters size limit reached. diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index 639f4096456fb2d59e195aac4377de84a3917d55..d1dc6c3db659ba0189053cfc133c953f1ab0da6f 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -3978,6 +3978,7 @@ fn test_multi_page_payout_stakers_by_page() { assert!(matches!( staking_events_since_last_call().as_slice(), &[ + Event::PayoutStarted { era_index: 1, validator_stash: 11, page: 0, next: Some(1) }, .., Event::Rewarded { stash: 1063, dest: RewardDestination::Stash, amount: 111 }, Event::Rewarded { stash: 1064, dest: RewardDestination::Stash, amount: 111 }, @@ -4001,7 +4002,7 @@ fn test_multi_page_payout_stakers_by_page() { assert!(matches!( events.as_slice(), &[ - Event::PayoutStarted { era_index: 1, validator_stash: 11 }, + Event::PayoutStarted { era_index: 1, validator_stash: 11, page: 1, next: None }, Event::Rewarded { stash: 1065, dest: RewardDestination::Stash, amount: 111 }, Event::Rewarded { stash: 1066, dest: RewardDestination::Stash, amount: 111 }, .. @@ -8011,7 +8012,8 @@ mod ledger_recovery { assert_eq!(asset::staked::(&333), lock_333_before); // OK assert_eq!(Bonded::::get(&333), Some(444)); // OK assert!(Payee::::get(&333).is_some()); // OK - // however, ledger associated with its controller was killed. + + // however, ledger associated with its controller was killed. assert!(Ledger::::get(&444).is_none()); // NOK // side effects on 444 - ledger, bonded, payee, lock should be completely removed. diff --git a/substrate/frame/support/src/generate_genesis_config.rs b/substrate/frame/support/src/generate_genesis_config.rs index fc21e76c74273b3879ccba8edbaf039197cdb951..283840d70c7c45dbd7728616385e04b88d2e4efb 100644 --- a/substrate/frame/support/src/generate_genesis_config.rs +++ b/substrate/frame/support/src/generate_genesis_config.rs @@ -23,41 +23,44 @@ use alloc::{borrow::Cow, format, string::String}; /// Represents the initialization method of a field within a struct. /// -/// This enum provides information about how it was initialized and the field name (as a `String`). +/// This enum provides information about how it was initialized. /// /// Intended to be used in `build_struct_json_patch` macro. #[derive(Debug)] -pub enum InitializedField<'a> { +pub enum InitilizationType { /// The field was partially initialized (e.g., specific fields within the struct were set /// manually). - Partial(Cow<'a, str>), - /// The field was fully initialized (e.g., using `new()` or `default()` like methods). - Full(Cow<'a, str>), + Partial, + /// The field was fully initialized (e.g., using `new()` or `default()` like methods + Full, } +/// This struct provides information about how the struct field was initialized and the field name +/// (as a `&str`). +/// +/// Intended to be used in `build_struct_json_patch` macro. +#[derive(Debug)] +pub struct InitializedField<'a>(InitilizationType, Cow<'a, str>); + impl<'a> InitializedField<'a> { /// Returns a name of the field. pub fn get_name(&'a self) -> &'a str { - match self { - Self::Partial(s) | Self::Full(s) => s, - } + &self.1 } /// Injects a prefix to the field name. pub fn add_prefix(&mut self, prefix: &str) { - match self { - Self::Partial(s) | Self::Full(s) => *s = format!("{prefix}.{s}").into(), - }; + self.1 = format!("{prefix}.{}", self.1).into() } /// Creates new partial field instiance. pub fn partial(s: &'a str) -> Self { - Self::Partial(s.into()) + Self(InitilizationType::Partial, s.into()) } /// Creates new full field instiance. pub fn full(s: &'a str) -> Self { - Self::Full(s.into()) + Self(InitilizationType::Full, s.into()) } } @@ -73,9 +76,15 @@ impl PartialEq for InitializedField<'_> { .map(|c| c.to_ascii_uppercase()) .eq(camel_chars.map(|c| c.to_ascii_uppercase())) } - match self { - InitializedField::Partial(field_name) | InitializedField::Full(field_name) => - field_name == other || compare_keys(field_name.chars(), other.chars()), + *self.1 == *other || compare_keys(self.1.chars(), other.chars()) + } +} + +impl<'a> From<(InitilizationType, &'a str)> for InitializedField<'a> { + fn from(value: (InitilizationType, &'a str)) -> Self { + match value.0 { + InitilizationType::Full => InitializedField::full(value.1), + InitilizationType::Partial => InitializedField::partial(value.1), } } } @@ -104,8 +113,8 @@ pub fn retain_initialized_fields( let current_key = if current_root.is_empty() { key.clone() } else { format!("{current_root}.{key}") }; match keys_to_retain.iter().find(|key| **key == current_key) { - Some(InitializedField::Full(_)) => true, - Some(InitializedField::Partial(_)) => { + Some(InitializedField(InitilizationType::Full, _)) => true, + Some(InitializedField(InitilizationType::Partial, _)) => { retain_initialized_fields(value, keys_to_retain, current_key.clone()); true }, @@ -208,89 +217,154 @@ pub fn retain_initialized_fields( #[macro_export] macro_rules! build_struct_json_patch { ( - $($struct_type:ident)::+ { $($tail:tt)* } + $($struct_type:ident)::+ { $($body:tt)* } ) => { { - let mut keys = $crate::__private::Vec::<$crate::generate_genesis_config::InitializedField>::default(); + let mut __keys = $crate::__private::Vec::<$crate::generate_genesis_config::InitializedField>::default(); #[allow(clippy::needless_update)] - let struct_instance = $crate::build_struct_json_patch!($($struct_type)::+, keys @ { $($tail)* }); - let mut json_value = - $crate::__private::serde_json::to_value(struct_instance).expect("serialization to json should work. qed"); - $crate::generate_genesis_config::retain_initialized_fields(&mut json_value, &keys, Default::default()); - json_value + let __struct_instance = $crate::build_struct_json_patch!($($struct_type)::+, __keys @ { $($body)* }).0; + let mut __json_value = + $crate::__private::serde_json::to_value(__struct_instance).expect("serialization to json should work. qed"); + $crate::generate_genesis_config::retain_initialized_fields(&mut __json_value, &__keys, Default::default()); + __json_value } }; - ($($struct_type:ident)::+, $all_keys:ident @ { $($tail:tt)* }) => { - $($struct_type)::+ { - ..$crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*) + ($($struct_type:ident)::+, $all_keys:ident @ { $($body:tt)* }) => { + { + let __value = $crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($body)*); + ( + $($struct_type)::+ { ..__value.0 }, + __value.1 + ) } }; - ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $($type:ident)::+ { $keyi:ident : $value:tt } ) => { - $($struct_type)::+ { - $key: { - $all_keys.push($crate::generate_genesis_config::InitializedField::partial(stringify!($key))); - $all_keys.push( - $crate::generate_genesis_config::InitializedField::full(concat!(stringify!($key), ".", stringify!($keyi))) - ); - $($type)::+ { - $keyi:$value, - ..Default::default() - } + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $($type:ident)::+ { $($body:tt)* } ) => { + ( + $($struct_type)::+ { + $key: { + let mut __inner_keys = + $crate::__private::Vec::<$crate::generate_genesis_config::InitializedField>::default(); + let __value = $crate::build_struct_json_patch!($($type)::+, __inner_keys @ { $($body)* }); + for i in __inner_keys.iter_mut() { + i.add_prefix(stringify!($key)); + }; + $all_keys.push((__value.1,stringify!($key)).into()); + $all_keys.extend(__inner_keys); + __value.0 + }, + ..Default::default() }, - ..Default::default() + $crate::generate_genesis_config::InitilizationType::Partial + ) + }; + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $($type:ident)::+ { $($body:tt)* }, $($tail:tt)*) => { + { + let mut __initialization_type; + ( + $($struct_type)::+ { + $key : { + let mut __inner_keys = + $crate::__private::Vec::<$crate::generate_genesis_config::InitializedField>::default(); + let __value = $crate::build_struct_json_patch!($($type)::+, __inner_keys @ { $($body)* }); + $all_keys.push((__value.1,stringify!($key)).into()); + + for i in __inner_keys.iter_mut() { + i.add_prefix(stringify!($key)); + }; + $all_keys.extend(__inner_keys); + __value.0 + }, + .. { + let (__value, __tmp) = + $crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*); + __initialization_type = __tmp; + __value + } + }, + __initialization_type + ) } }; - ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $($type:ident)::+ { $($body:tt)* } ) => { - $($struct_type)::+ { - $key: { - $all_keys.push($crate::generate_genesis_config::InitializedField::partial(stringify!($key))); - let mut inner_keys = $crate::__private::Vec::<$crate::generate_genesis_config::InitializedField>::default(); - let value = $crate::build_struct_json_patch!($($type)::+, inner_keys @ { $($body)* }); - for i in inner_keys.iter_mut() { - i.add_prefix(stringify!($key)); - }; - $all_keys.extend(inner_keys); - value - }, - ..Default::default() + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $value:expr, $($tail:tt)* ) => { + { + let mut __initialization_type; + ( + $($struct_type)::+ { + $key: { + $all_keys.push($crate::generate_genesis_config::InitializedField::full( + stringify!($key)) + ); + $value + }, + .. { + let (__value, __tmp) = + $crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*); + __initialization_type = __tmp; + __value + } + }, + __initialization_type + ) } }; - ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $($type:ident)::+ { $($body:tt)* }, $($tail:tt)* ) => { - $($struct_type)::+ { - $key : { - $all_keys.push($crate::generate_genesis_config::InitializedField::partial(stringify!($key))); - let mut inner_keys = $crate::__private::Vec::<$crate::generate_genesis_config::InitializedField>::default(); - let value = $crate::build_struct_json_patch!($($type)::+, inner_keys @ { $($body)* }); - for i in inner_keys.iter_mut() { - i.add_prefix(stringify!($key)); - }; - $all_keys.extend(inner_keys); - value + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $value:expr ) => { + ( + $($struct_type)::+ { + $key: { + $all_keys.push($crate::generate_genesis_config::InitializedField::full(stringify!($key))); + $value + }, + ..Default::default() }, - .. $crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*) + $crate::generate_genesis_config::InitilizationType::Partial + ) + }; + // field init shorthand + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident, $($tail:tt)* ) => { + { + let __update = $crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*); + ( + $($struct_type)::+ { + $key: { + $all_keys.push($crate::generate_genesis_config::InitializedField::full( + stringify!($key)) + ); + $key + }, + ..__update.0 + }, + __update.1 + ) } }; - ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $value:expr, $($tail:tt)* ) => { - $($struct_type)::+ { - $key: { - $all_keys.push($crate::generate_genesis_config::InitializedField::full(stringify!($key))); - $value + ($($struct_type:ident)::+, $all_keys:ident @ $key:ident ) => { + ( + $($struct_type)::+ { + $key: { + $all_keys.push($crate::generate_genesis_config::InitializedField::full(stringify!($key))); + $key + }, + ..Default::default() }, - ..$crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*) - } + $crate::generate_genesis_config::InitilizationType::Partial + ) }; - ($($struct_type:ident)::+, $all_keys:ident @ $key:ident: $value:expr ) => { - $($struct_type)::+ { - $key: { - $all_keys.push($crate::generate_genesis_config::InitializedField::full(stringify!($key))); - $value + // update struct + ($($struct_type:ident)::+, $all_keys:ident @ ..$update:expr ) => { + ( + $($struct_type)::+ { + ..$update }, - ..Default::default() - } + $crate::generate_genesis_config::InitilizationType::Full + ) }; - ($($struct_type:ident)::+, $all_keys:ident @ $(,)?) => { - $($struct_type)::+ { ..Default::default() } + ( + $($struct_type)::+ { + ..Default::default() + }, + $crate::generate_genesis_config::InitilizationType::Partial + ) }; } @@ -401,11 +475,8 @@ mod test { macro_rules! test { ($($struct:ident)::+ { $($v:tt)* }, { $($j:tt)* } ) => {{ - println!("--"); let expected = serde_json::json!({ $($j)* }); - println!("json: {}", serde_json::to_string_pretty(&expected).unwrap()); let value = build_struct_json_patch!($($struct)::+ { $($v)* }); - println!("gc: {}", serde_json::to_string_pretty(&value).unwrap()); assert_eq!(value, expected); }}; } @@ -415,6 +486,7 @@ mod test { let t = 5; const C: u32 = 5; test!(TestStruct { b: 5 }, { "b": 5 }); + test!(TestStruct { b: 5, }, { "b": 5 }); #[allow(unused_braces)] { test!(TestStruct { b: { 4 + 34 } } , { "b": 38 }); @@ -705,6 +777,324 @@ mod test { ); } + #[test] + fn test_generate_config_macro_field_init_shorthand() { + { + let x = 5; + test!(TestStruct { s: S { x } }, { "s": { "x": 5 } }); + } + { + let s = nested_mod::InsideMod { a: 34, b: 8 }; + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { + s, + a: 32, + } + }, + { + "t" : { "a": 32 }, + "u" : { "a": 32, "s": { "a": 34, "b": 8} } + } + ); + } + { + let s = nested_mod::InsideMod { a: 34, b: 8 }; + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { + a: 32, + s, + } + }, + { + "t" : { "a": 32 }, + "u" : { "a": 32, "s": { "a": 34, "b": 8} } + } + ); + } + } + + #[test] + fn test_generate_config_macro_struct_update() { + { + let s = S { x: 5 }; + test!(TestStruct { s: S { ..s } }, { "s": { "x": 5 } }); + } + { + mod nested { + use super::*; + pub fn function() -> S { + S { x: 5 } + } + } + test!(TestStruct { s: S { ..nested::function() } }, { "s": { "x": 5 } }); + } + { + let s = nested_mod::InsideMod { a: 34, b: 8 }; + let s1 = nested_mod::InsideMod { a: 34, b: 8 }; + test!( + TestStruct { + t: nested_mod::InsideMod { ..s1 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { + s, + a: 32, + } + }, + { + "t" : { "a": 34, "b": 8 }, + "u" : { "a": 32, "s": { "a": 34, "b": 8} } + } + ); + } + { + let i3 = nested_mod::nested_mod2::nested_mod3::InsideMod3 { + a: 1, + b: 2, + s: nested_mod::InsideMod { a: 55, b: 88 }, + }; + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { + a: 32, + ..i3 + } + }, + { + "t" : { "a": 32 }, + "u" : { "a": 32, "b": 2, "s": { "a": 55, "b": 88} } + } + ); + } + { + let s = nested_mod::InsideMod { a: 34, b: 8 }; + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { + a: 32, + s: nested_mod::InsideMod { + b: 66, + ..s + } + } + }, + { + "t" : { "a": 32 }, + "u" : { "a": 32, "s": { "a": 34, "b": 66} } + } + ); + } + { + let s = nested_mod::InsideMod { a: 34, b: 8 }; + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { + s: nested_mod::InsideMod { + b: 66, + ..s + }, + a: 32 + } + }, + { + "t" : { "a": 32 }, + "u" : { "a": 32, "s": { "a": 34, "b": 66} } + } + ); + } + } + + #[test] + fn test_generate_config_macro_with_execution_order() { + #[derive(Debug, Default, serde::Serialize, serde::Deserialize, PartialEq)] + struct X { + x: Vec, + x2: Vec, + y2: Y, + } + #[derive(Debug, Default, serde::Serialize, serde::Deserialize, PartialEq)] + struct Y { + y: Vec, + } + #[derive(Debug, Default, serde::Serialize, serde::Deserialize, PartialEq)] + struct Z { + a: u32, + x: X, + y: Y, + } + { + let v = vec![1, 2, 3]; + test!(Z { a: 0, x: X { x: v }, }, { + "a": 0, "x": { "x": [1,2,3] } + }); + } + { + let v = vec![1, 2, 3]; + test!(Z { a: 3, x: X { x: v.clone() }, y: Y { y: v } }, { + "a": 3, "x": { "x": [1,2,3] }, "y": { "y": [1,2,3] } + }); + } + { + let v = vec![1, 2, 3]; + test!(Z { a: 3, x: X { y2: Y { y: v.clone() }, x: v.clone() }, y: Y { y: v } }, { + "a": 3, "x": { "x": [1,2,3], "y2":{ "y":[1,2,3] } }, "y": { "y": [1,2,3] } + }); + } + { + let v = vec![1, 2, 3]; + test!(Z { a: 3, y: Y { y: v.clone() }, x: X { y2: Y { y: v.clone() }, x: v }, }, { + "a": 3, "x": { "x": [1,2,3], "y2":{ "y":[1,2,3] } }, "y": { "y": [1,2,3] } + }); + } + { + let v = vec![1, 2, 3]; + test!( + Z { + y: Y { + y: v.clone() + }, + x: X { + y2: Y { + y: v.clone() + }, + x: v.clone(), + x2: v.clone() + }, + }, + { + "x": { + "x": [1,2,3], + "x2": [1,2,3], + "y2": { + "y":[1,2,3] + } + }, + "y": { + "y": [1,2,3] + } + }); + } + { + let v = vec![1, 2, 3]; + test!( + Z { + y: Y { + y: v.clone() + }, + x: X { + y2: Y { + y: v.clone() + }, + x: v + }, + }, + { + "x": { + "x": [1,2,3], + "y2": { + "y":[1,2,3] + } + }, + "y": { + "y": [1,2,3] + } + }); + } + { + let mut v = vec![0, 1, 2]; + let f = |vec: &mut Vec| -> Vec { + vec.iter_mut().for_each(|x| *x += 1); + vec.clone() + }; + let z = Z { + a: 0, + y: Y { y: f(&mut v) }, + x: X { y2: Y { y: f(&mut v) }, x: f(&mut v), x2: vec![] }, + }; + let z_expected = Z { + a: 0, + y: Y { y: vec![1, 2, 3] }, + x: X { y2: Y { y: vec![2, 3, 4] }, x: vec![3, 4, 5], x2: vec![] }, + }; + assert_eq!(z, z_expected); + v = vec![0, 1, 2]; + println!("{z:?}"); + test!( + Z { + y: Y { + y: f(&mut v) + }, + x: X { + y2: Y { + y: f(&mut v) + }, + x: f(&mut v) + }, + }, + { + "y": { + "y": [1,2,3] + }, + "x": { + "y2": { + "y":[2,3,4] + }, + "x": [3,4,5], + }, + }); + } + { + let mut v = vec![0, 1, 2]; + let f = |vec: &mut Vec| -> Vec { + vec.iter_mut().for_each(|x| *x += 1); + vec.clone() + }; + let z = Z { + a: 0, + y: Y { y: f(&mut v) }, + x: X { y2: Y { y: f(&mut v) }, x: f(&mut v), x2: f(&mut v) }, + }; + let z_expected = Z { + a: 0, + y: Y { y: vec![1, 2, 3] }, + x: X { y2: Y { y: vec![2, 3, 4] }, x: vec![3, 4, 5], x2: vec![4, 5, 6] }, + }; + assert_eq!(z, z_expected); + v = vec![0, 1, 2]; + println!("{z:?}"); + test!( + Z { + y: Y { + y: f(&mut v) + }, + x: X { + y2: Y { + y: f(&mut v) + }, + x: f(&mut v), + x2: f(&mut v) + }, + }, + { + "y": { + "y": [1,2,3] + }, + "x": { + "y2": { + "y":[2,3,4] + }, + "x": [3,4,5], + "x2": [4,5,6], + }, + }); + } + } + #[test] fn test_generate_config_macro_with_nested_mods() { test!( @@ -797,7 +1187,6 @@ mod retain_keys_test { ( $s:literal ) => { let field = InitializedField::full($s); let cc = inflector::cases::camelcase::to_camel_case($s); - println!("field: {:?}, cc: {}", field, cc); assert_eq!(field,cc); } ; ( &[ $f:literal $(, $r:literal)* ]) => { @@ -808,7 +1197,6 @@ mod retain_keys_test { .map(|s| inflector::cases::camelcase::to_camel_case(s)) .collect::>() .join("."); - println!("field: {:?}, cc: {}", field, cc); assert_eq!(field,cc); } ; ); diff --git a/substrate/frame/support/src/genesis_builder_helper.rs b/substrate/frame/support/src/genesis_builder_helper.rs index 662ea2cb1862dd003c1c5a5d3979ececb096f3b1..38b339eb93297bbafbc29b311c9e4ff9e52cc2a5 100644 --- a/substrate/frame/support/src/genesis_builder_helper.rs +++ b/substrate/frame/support/src/genesis_builder_helper.rs @@ -21,16 +21,15 @@ extern crate alloc; -use alloc::vec::Vec; +use alloc::{format, vec::Vec}; use frame_support::traits::BuildGenesisConfig; use sp_genesis_builder::{PresetId, Result as BuildResult}; -use sp_runtime::format_runtime_string; /// Build `GenesisConfig` from a JSON blob not using any defaults and store it in the storage. For /// more info refer to [`sp_genesis_builder::GenesisBuilder::build_state`]. pub fn build_state(json: Vec) -> BuildResult { - let gc = serde_json::from_slice::(&json) - .map_err(|e| format_runtime_string!("Invalid JSON blob: {}", e))?; + let gc = + serde_json::from_slice::(&json).map_err(|e| format!("Invalid JSON blob: {}", e))?; ::build(&gc); Ok(()) } @@ -41,7 +40,7 @@ pub fn build_state(json: Vec) -> BuildResult { /// to [`sp_genesis_builder::GenesisBuilder::get_preset`]. pub fn get_preset( name: &Option, - preset_for_name: impl FnOnce(&sp_genesis_builder::PresetId) -> Option>, + preset_for_name: impl FnOnce(&PresetId) -> Option>, ) -> Option> where GC: BuildGenesisConfig + Default, diff --git a/substrate/frame/support/test/compile_pass/src/lib.rs b/substrate/frame/support/test/compile_pass/src/lib.rs index 677ef4e94c89665bf639a3897cd4617f9ed18e60..31f3126b8dd5938ddc490e374b0039da3fc22ec2 100644 --- a/substrate/frame/support/test/compile_pass/src/lib.rs +++ b/substrate/frame/support/test/compile_pass/src/lib.rs @@ -21,20 +21,22 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{ConstU16, ConstU32, ConstU64, Everything}, }; use sp_core::{sr25519, H256}; use sp_runtime::{ - create_runtime_str, generic, + generic, traits::{BlakeTwo256, IdentityLookup, Verify}, }; use sp_version::RuntimeVersion; pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("frame-support-test-compile-pass"), - impl_name: create_runtime_str!("substrate-frame-support-test-compile-pass-runtime"), + spec_name: alloc::borrow::Cow::Borrowed("frame-support-test-compile-pass"), + impl_name: alloc::borrow::Cow::Borrowed("substrate-frame-support-test-compile-pass-runtime"), authoring_version: 0, spec_version: 0, impl_version: 0, diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr index 55b19ac1a652691ee609aa127a399958b329c0fb..726b09cf54c997b04ffa17a6256cae25e4c17886 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/deprecated_where_block.stderr @@ -751,7 +751,7 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied = help: the trait `Serialize` is implemented for `GenesisConfig` = note: required for `GenesisConfig` to implement `Serialize` note: required by a bound in `frame_support::sp_runtime::serde::ser::SerializeStruct::serialize_field` - --> $CARGO/serde-1.0.210/src/ser/mod.rs + --> $CARGO/serde-1.0.214/src/ser/mod.rs | | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> | --------------- required by a bound in this associated function diff --git a/substrate/frame/support/test/tests/runtime_metadata.rs b/substrate/frame/support/test/tests/runtime_metadata.rs index 81377210eb43f8aa90f9e4ba795e816793002d25..7523a415d458e4f03c704972cca50d03fdf8fb93 100644 --- a/substrate/frame/support/test/tests/runtime_metadata.rs +++ b/substrate/frame/support/test/tests/runtime_metadata.rs @@ -178,7 +178,7 @@ fn runtime_metadata() { RuntimeApiMethodMetadataIR { name: "wild_card", inputs: vec![RuntimeApiMethodParamMetadataIR:: { - name: "_", + name: "__runtime_api_generated_name_0__", ty: meta_type::(), }], output: meta_type::<()>(), diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index 02d61921741c02d3ac3f39f2df48e4af6213e39a..04bedd78cc120b00f028d0d5a699aa2ca746a0a2 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -99,7 +99,7 @@ extern crate alloc; -use alloc::{boxed::Box, vec, vec::Vec}; +use alloc::{borrow::Cow, boxed::Box, vec, vec::Vec}; use core::{fmt::Debug, marker::PhantomData}; use pallet_prelude::{BlockNumberFor, HeaderFor}; #[cfg(feature = "std")] @@ -130,7 +130,8 @@ use frame_support::traits::BuildGenesisConfig; use frame_support::{ dispatch::{ extract_actual_pays_fee, extract_actual_weight, DispatchClass, DispatchInfo, - DispatchResult, DispatchResultWithPostInfo, PerDispatchClass, PostDispatchInfo, + DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo, PerDispatchClass, + PostDispatchInfo, }, ensure, impl_ensure_origin_with_arg_ignoring_arg, migrations::MultiStepMigrator, @@ -507,6 +508,7 @@ pub mod pallet { type RuntimeCall: Parameter + Dispatchable + Debug + + GetDispatchInfo + From>; /// The aggregated `RuntimeTask` type. @@ -1159,24 +1161,24 @@ pub struct AccountInfo { /// Stores the `spec_version` and `spec_name` of when the last runtime upgrade /// happened. -#[derive(sp_runtime::RuntimeDebug, Encode, Decode, TypeInfo)] +#[derive(RuntimeDebug, Encode, Decode, TypeInfo)] #[cfg_attr(feature = "std", derive(PartialEq))] pub struct LastRuntimeUpgradeInfo { pub spec_version: codec::Compact, - pub spec_name: sp_runtime::RuntimeString, + pub spec_name: Cow<'static, str>, } impl LastRuntimeUpgradeInfo { /// Returns if the runtime was upgraded in comparison of `self` and `current`. /// /// Checks if either the `spec_version` increased or the `spec_name` changed. - pub fn was_upgraded(&self, current: &sp_version::RuntimeVersion) -> bool { + pub fn was_upgraded(&self, current: &RuntimeVersion) -> bool { current.spec_version > self.spec_version.0 || current.spec_name != self.spec_name } } -impl From for LastRuntimeUpgradeInfo { - fn from(version: sp_version::RuntimeVersion) -> Self { +impl From for LastRuntimeUpgradeInfo { + fn from(version: RuntimeVersion) -> Self { Self { spec_version: version.spec_version.into(), spec_name: version.spec_name } } } diff --git a/substrate/frame/system/src/mock.rs b/substrate/frame/system/src/mock.rs index f43ffe3c87eed971f1debd0fbd575ec6f3af4658..80bc75973d196e28d8b34389a96d5758ddc96783 100644 --- a/substrate/frame/system/src/mock.rs +++ b/substrate/frame/system/src/mock.rs @@ -33,8 +33,8 @@ const MAX_BLOCK_WEIGHT: Weight = Weight::from_parts(1024, u64::MAX); parameter_types! { pub Version: RuntimeVersion = RuntimeVersion { - spec_name: sp_version::create_runtime_str!("test"), - impl_name: sp_version::create_runtime_str!("system-test"), + spec_name: alloc::borrow::Cow::Borrowed("test"), + impl_name: alloc::borrow::Cow::Borrowed("system-test"), authoring_version: 1, spec_version: 1, impl_version: 1, diff --git a/substrate/frame/tips/src/tests.rs b/substrate/frame/tips/src/tests.rs index f6f130b7e261a714175bcb5f162154b4487995f8..530efb708e41483e8fd339720f9daa81c195d7c2 100644 --- a/substrate/frame/tips/src/tests.rs +++ b/substrate/frame/tips/src/tests.rs @@ -209,6 +209,7 @@ fn last_event() -> TipEvent { } #[test] +#[allow(deprecated)] fn genesis_config_works() { build_and_execute(|| { assert_eq!(Treasury::pot(), 0); diff --git a/substrate/frame/transaction-storage/src/benchmarking.rs b/substrate/frame/transaction-storage/src/benchmarking.rs index f360e9847a1e1f9ef57b12f3aa77f62c13d8d4ca..0b5b0dc994054655502ff13a3f0bad238dd9ba47 100644 --- a/substrate/frame/transaction-storage/src/benchmarking.rs +++ b/substrate/frame/transaction-storage/src/benchmarking.rs @@ -19,16 +19,14 @@ #![cfg(feature = "runtime-benchmarks")] -use super::*; +use crate::*; use alloc::{vec, vec::Vec}; -use frame_benchmarking::v1::{benchmarks, whitelisted_caller}; +use frame_benchmarking::v2::*; use frame_support::traits::{Get, OnFinalize, OnInitialize}; use frame_system::{pallet_prelude::BlockNumberFor, EventRecord, Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, CheckedDiv, One, Zero}; use sp_transaction_storage_proof::TransactionStorageProof; -use crate::Pallet as TransactionStorage; - // Proof generated from max size storage: // ``` // let mut transactions = Vec::new(); @@ -122,39 +120,50 @@ pub fn run_to_block(n: frame_system::pallet_prelude::BlockNumberFor) { let caller: T::AccountId = whitelisted_caller(); let initial_balance = BalanceOf::::max_value().checked_div(&2u32.into()).unwrap(); T::Currency::set_balance(&caller, initial_balance); - }: _(RawOrigin::Signed(caller.clone()), vec![0u8; l as usize]) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), vec![0u8; l as usize]); + assert!(!BlockTransactions::::get().is_empty()); assert_last_event::(Event::Stored { index: 0 }.into()); } - renew { + #[benchmark] + fn renew() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let initial_balance = BalanceOf::::max_value().checked_div(&2u32.into()).unwrap(); T::Currency::set_balance(&caller, initial_balance); - TransactionStorage::::store( + Pallet::::store( RawOrigin::Signed(caller.clone()).into(), vec![0u8; T::MaxTransactionSize::get() as usize], )?; run_to_block::(1u32.into()); - }: _(RawOrigin::Signed(caller.clone()), BlockNumberFor::::zero(), 0) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), BlockNumberFor::::zero(), 0); + assert_last_event::(Event::Renewed { index: 0 }.into()); + + Ok(()) } - check_proof_max { + #[benchmark] + fn check_proof_max() -> Result<(), BenchmarkError> { run_to_block::(1u32.into()); let caller: T::AccountId = whitelisted_caller(); let initial_balance = BalanceOf::::max_value().checked_div(&2u32.into()).unwrap(); T::Currency::set_balance(&caller, initial_balance); - for _ in 0 .. T::MaxBlockTransactions::get() { - TransactionStorage::::store( + for _ in 0..T::MaxBlockTransactions::get() { + Pallet::::store( RawOrigin::Signed(caller.clone()).into(), vec![0u8; T::MaxTransactionSize::get() as usize], )?; @@ -162,10 +171,14 @@ benchmarks! { run_to_block::(StoragePeriod::::get() + BlockNumberFor::::one()); let encoded_proof = proof(); let proof = TransactionStorageProof::decode(&mut &*encoded_proof).unwrap(); - }: check_proof(RawOrigin::None, proof) - verify { + + #[extrinsic_call] + check_proof(RawOrigin::None, proof); + assert_last_event::(Event::ProofChecked.into()); + + Ok(()) } - impl_benchmark_test_suite!(TransactionStorage, crate::mock::new_test_ext(), crate::mock::Test); + impl_benchmark_test_suite!(Pallet, mock::new_test_ext(), mock::Test); } diff --git a/substrate/frame/treasury/src/benchmarking.rs b/substrate/frame/treasury/src/benchmarking.rs index 650e5376fa4b285f3a289271b922a9e3b47a4819..a03ee149db9b129f827084f07aebb328cb1881f1 100644 --- a/substrate/frame/treasury/src/benchmarking.rs +++ b/substrate/frame/treasury/src/benchmarking.rs @@ -78,6 +78,7 @@ fn create_approved_proposals, I: 'static>(n: u32) -> Result<(), &'s for i in 0..n { let (_, value, lookup) = setup_proposal::(i); + #[allow(deprecated)] if let Ok(origin) = &spender { Treasury::::spend_local(origin.clone(), value, lookup)?; } @@ -136,6 +137,7 @@ mod benchmarks { let (spend_exists, proposal_id) = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { let (_, value, beneficiary_lookup) = setup_proposal::(SEED); + #[allow(deprecated)] Treasury::::spend_local(origin, value, beneficiary_lookup)?; let proposal_id = ProposalCount::::get() - 1; @@ -149,8 +151,8 @@ mod benchmarks { #[block] { - let res = - Treasury::::remove_approval(reject_origin as T::RuntimeOrigin, proposal_id); + #[allow(deprecated)] + let res = Treasury::::remove_approval(reject_origin as T::RuntimeOrigin, proposal_id); if spend_exists { assert_ok!(res); diff --git a/substrate/frame/treasury/src/lib.rs b/substrate/frame/treasury/src/lib.rs index b21a36949357bbde0a6cef96e1968983d8e67b24..faacda1c07832ce10f0d2857870d6a7d9bd42328 100644 --- a/substrate/frame/treasury/src/lib.rs +++ b/substrate/frame/treasury/src/lib.rs @@ -240,6 +240,9 @@ pub mod pallet { /// Runtime hooks to external pallet using treasury to compute spend funds. type SpendFunds: SpendFunds; + /// DEPRECATED: associated with `spend_local` call and will be removed in May 2025. + /// Refer to for migration to `spend`. + /// /// The maximum number of approvals that can wait in the spending queue. /// /// NOTE: This parameter is also used within the Bounties Pallet extension if enabled. @@ -284,10 +287,16 @@ pub mod pallet { type BlockNumberProvider: BlockNumberProvider>; } + /// DEPRECATED: associated with `spend_local` call and will be removed in May 2025. + /// Refer to for migration to `spend`. + /// /// Number of proposals that have been made. #[pallet::storage] pub type ProposalCount = StorageValue<_, ProposalIndex, ValueQuery>; + /// DEPRECATED: associated with `spend_local` call and will be removed in May 2025. + /// Refer to for migration to `spend`. + /// /// Proposals that have been made. #[pallet::storage] pub type Proposals, I: 'static = ()> = StorageMap< @@ -303,6 +312,9 @@ pub mod pallet { pub type Deactivated, I: 'static = ()> = StorageValue<_, BalanceOf, ValueQuery>; + /// DEPRECATED: associated with `spend_local` call and will be removed in May 2025. + /// Refer to for migration to `spend`. + /// /// Proposal indices that have been approved but not yet awarded. #[pallet::storage] pub type Approvals, I: 'static = ()> = @@ -494,6 +506,10 @@ pub mod pallet { /// Emits [`Event::SpendApproved`] if successful. #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::spend_local())] + #[deprecated( + note = "The `spend_local` call will be removed by May 2025. Migrate to the new flow and use the `spend` call." + )] + #[allow(deprecated)] pub fn spend_local( origin: OriginFor, #[pallet::compact] amount: BalanceOf, @@ -523,7 +539,9 @@ pub mod pallet { .unwrap_or(Ok(()))?; let beneficiary = T::Lookup::lookup(beneficiary)?; + #[allow(deprecated)] let proposal_index = ProposalCount::::get(); + #[allow(deprecated)] Approvals::::try_append(proposal_index) .map_err(|_| Error::::TooManyApprovals)?; let proposal = Proposal { @@ -532,7 +550,9 @@ pub mod pallet { beneficiary: beneficiary.clone(), bond: Default::default(), }; + #[allow(deprecated)] Proposals::::insert(proposal_index, proposal); + #[allow(deprecated)] ProposalCount::::put(proposal_index + 1); Self::deposit_event(Event::SpendApproved { proposal_index, amount, beneficiary }); @@ -562,12 +582,17 @@ pub mod pallet { /// in the first place. #[pallet::call_index(4)] #[pallet::weight((T::WeightInfo::remove_approval(), DispatchClass::Operational))] + #[deprecated( + note = "The `remove_approval` call will be removed by May 2025. It associated with the deprecated `spend_local` call." + )] + #[allow(deprecated)] pub fn remove_approval( origin: OriginFor, #[pallet::compact] proposal_id: ProposalIndex, ) -> DispatchResult { T::RejectOrigin::ensure_origin(origin)?; + #[allow(deprecated)] Approvals::::try_mutate(|v| -> DispatchResult { if let Some(index) = v.iter().position(|x| x == &proposal_id) { v.remove(index); @@ -836,16 +861,28 @@ impl, I: 'static> Pallet { } /// Public function to proposal_count storage. + #[deprecated( + note = "This function will be removed by May 2025. Configure pallet to use PayFromAccount for Paymaster type instead" + )] pub fn proposal_count() -> ProposalIndex { + #[allow(deprecated)] ProposalCount::::get() } /// Public function to proposals storage. + #[deprecated( + note = "This function will be removed by May 2025. Configure pallet to use PayFromAccount for Paymaster type instead" + )] pub fn proposals(index: ProposalIndex) -> Option>> { + #[allow(deprecated)] Proposals::::get(index) } /// Public function to approvals storage. + #[deprecated( + note = "This function will be removed by May 2025. Configure pallet to use PayFromAccount for Paymaster type instead" + )] + #[allow(deprecated)] pub fn approvals() -> BoundedVec { Approvals::::get() } @@ -864,6 +901,7 @@ impl, I: 'static> Pallet { let mut missed_any = false; let mut imbalance = PositiveImbalanceOf::::zero(); + #[allow(deprecated)] let proposals_len = Approvals::::mutate(|v| { let proposals_approvals_len = v.len() as u32; v.retain(|&index| { diff --git a/substrate/frame/treasury/src/migration.rs b/substrate/frame/treasury/src/migration.rs index c0de4ce43108840b22096bb09c3338bdf1d9afd3..7c8c587f1664142eb01ea1b64587a85d6938c92d 100644 --- a/substrate/frame/treasury/src/migration.rs +++ b/substrate/frame/treasury/src/migration.rs @@ -43,11 +43,13 @@ pub mod cleanup_proposals { { fn on_runtime_upgrade() -> frame_support::weights::Weight { let mut approval_index = BTreeSet::new(); + #[allow(deprecated)] for approval in Approvals::::get().iter() { approval_index.insert(*approval); } let mut proposals_processed = 0; + #[allow(deprecated)] for (proposal_index, p) in Proposals::::iter() { if !approval_index.contains(&proposal_index) { let err_amount = T::Currency::unreserve(&p.proposer, p.bond); diff --git a/substrate/frame/treasury/src/tests.rs b/substrate/frame/treasury/src/tests.rs index a99dd0dd4449fee33fc7ca8d561c58f936e67ee1..e9efb7c0956f196a53239b2ff6eec2a3a705fa57 100644 --- a/substrate/frame/treasury/src/tests.rs +++ b/substrate/frame/treasury/src/tests.rs @@ -249,6 +249,7 @@ fn genesis_config_works() { #[test] fn spend_local_origin_permissioning_works() { + #[allow(deprecated)] ExtBuilder::default().build().execute_with(|| { assert_noop!(Treasury::spend_local(RuntimeOrigin::signed(1), 1, 1), BadOrigin); assert_noop!( @@ -273,6 +274,7 @@ fn spend_local_origin_permissioning_works() { #[docify::export] #[test] fn spend_local_origin_works() { + #[allow(deprecated)] ExtBuilder::default().build().execute_with(|| { // Check that accumulate works when we have Some value in Dummy already. Balances::make_free_balance_be(&Treasury::account_id(), 102); @@ -309,7 +311,10 @@ fn accepted_spend_proposal_ignored_outside_spend_period() { ExtBuilder::default().build().execute_with(|| { Balances::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 100, 3)); + #[allow(deprecated)] + { + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 100, 3)); + } go_to_block(1); assert_eq!(Balances::free_balance(3), 0); @@ -336,7 +341,10 @@ fn accepted_spend_proposal_enacted_on_spend_period() { Balances::make_free_balance_be(&Treasury::account_id(), 101); assert_eq!(Treasury::pot(), 100); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 100, 3)); + #[allow(deprecated)] + { + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 100, 3)); + } go_to_block(2); assert_eq!(Balances::free_balance(3), 100); @@ -350,7 +358,10 @@ fn pot_underflow_should_not_diminish() { Balances::make_free_balance_be(&Treasury::account_id(), 101); assert_eq!(Treasury::pot(), 100); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 150, 3)); + #[allow(deprecated)] + { + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 150, 3)); + } go_to_block(2); assert_eq!(Treasury::pot(), 100); // Pot hasn't changed @@ -370,13 +381,19 @@ fn treasury_account_doesnt_get_deleted() { Balances::make_free_balance_be(&Treasury::account_id(), 101); assert_eq!(Treasury::pot(), 100); let treasury_balance = Balances::free_balance(&Treasury::account_id()); + #[allow(deprecated)] + { + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), treasury_balance, 3)); + >::on_initialize(2); + assert_eq!(Treasury::pot(), 100); // Pot hasn't changed - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), treasury_balance, 3)); + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), treasury_balance, 3)); - go_to_block(2); - assert_eq!(Treasury::pot(), 100); // Pot hasn't changed + go_to_block(2); + assert_eq!(Treasury::pot(), 100); // Pot hasn't changed - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), Treasury::pot(), 3)); + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), Treasury::pot(), 3)); + } go_to_block(4); assert_eq!(Treasury::pot(), 0); // Pot is emptied @@ -399,8 +416,11 @@ fn inexistent_account_works() { assert_eq!(Balances::free_balance(Treasury::account_id()), 0); // Account does not exist assert_eq!(Treasury::pot(), 0); // Pot is empty - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 99, 3)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 1, 3)); + #[allow(deprecated)] + { + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 99, 3)); + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 1, 3)); + } go_to_block(2); @@ -439,6 +459,7 @@ fn genesis_funding_works() { #[test] fn max_approvals_limited() { + #[allow(deprecated)] ExtBuilder::default().build().execute_with(|| { Balances::make_free_balance_be(&Treasury::account_id(), u64::MAX); Balances::make_free_balance_be(&0, u64::MAX); @@ -457,6 +478,7 @@ fn max_approvals_limited() { #[test] fn remove_already_removed_approval_fails() { + #[allow(deprecated)] ExtBuilder::default().build().execute_with(|| { Balances::make_free_balance_be(&Treasury::account_id(), 101); @@ -796,7 +818,10 @@ fn try_state_proposals_invariant_1_works() { ExtBuilder::default().build().execute_with(|| { use frame_support::pallet_prelude::DispatchError::Other; // Add a proposal and approve using `spend_local` - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 1, 3)); + #[allow(deprecated)] + { + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 1, 3)); + } assert_eq!(Proposals::::iter().count(), 1); assert_eq!(ProposalCount::::get(), 1); @@ -816,8 +841,11 @@ fn try_state_proposals_invariant_1_works() { fn try_state_proposals_invariant_2_works() { ExtBuilder::default().build().execute_with(|| { use frame_support::pallet_prelude::DispatchError::Other; - // Add a proposal and approve using `spend_local` - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 1, 3)); + #[allow(deprecated)] + { + // Add a proposal and approve using `spend_local` + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 1, 3)); + } assert_eq!(Proposals::::iter().count(), 1); assert_eq!(Approvals::::get().len(), 1); @@ -846,7 +874,10 @@ fn try_state_proposals_invariant_3_works() { ExtBuilder::default().build().execute_with(|| { use frame_support::pallet_prelude::DispatchError::Other; // Add a proposal and approve using `spend_local` - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 10, 3)); + #[allow(deprecated)] + { + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(14), 10, 3)); + } assert_eq!(Proposals::::iter().count(), 1); assert_eq!(Approvals::::get().len(), 1); @@ -952,13 +983,16 @@ fn multiple_spend_periods_work() { // 100 will be spent, 1024 will be the burn amount, 1 for ED Balances::make_free_balance_be(&Treasury::account_id(), 100 + 1024 + 1); // approve spend of total amount 100 to beneficiary `6`. - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(11), 10, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(12), 20, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(13), 50, 6)); + #[allow(deprecated)] + { + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(11), 10, 6)); + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(12), 20, 6)); + assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(13), 50, 6)); + } // free balance of `6` is zero, spend period has not passed. go_to_block(1); assert_eq!(Balances::free_balance(6), 0); diff --git a/substrate/primitives/api/proc-macro/Cargo.toml b/substrate/primitives/api/proc-macro/Cargo.toml index 659307e7b0f8989d8f2e873186a4b35e423e4191..191578f432adb84f2570669dbfa26ae68dda5dbf 100644 --- a/substrate/primitives/api/proc-macro/Cargo.toml +++ b/substrate/primitives/api/proc-macro/Cargo.toml @@ -20,7 +20,7 @@ proc-macro = true [dependencies] quote = { workspace = true } -syn = { features = ["extra-traits", "fold", "full", "visit"], workspace = true } +syn = { features = ["extra-traits", "fold", "full", "visit", "visit-mut"], workspace = true } proc-macro2 = { workspace = true } blake2 = { workspace = true } proc-macro-crate = { workspace = true } diff --git a/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs index cb213f2fd627b628f2b763ff7e3f132eb849bed4..ddca1095a1920027ae6740282951026b146edfb2 100644 --- a/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -32,6 +32,7 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; +use std::collections::{BTreeMap, HashMap}; use syn::{ fold::{self, Fold}, parse::{Error, Parse, ParseStream, Result}, @@ -43,8 +44,6 @@ use syn::{ TraitItem, TraitItemFn, }; -use std::collections::{BTreeMap, HashMap}; - /// The structure used for parsing the runtime api declarations. struct RuntimeApiDecls { decls: Vec, @@ -133,7 +132,7 @@ fn remove_supported_attributes(attrs: &mut Vec) -> HashMap<&'static s /// ``` fn generate_versioned_api_traits( api: ItemTrait, - methods: BTreeMap>, + methods: BTreeMap>, ) -> Vec { let mut result = Vec::::new(); for (version, _) in &methods { @@ -189,15 +188,12 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result { extend_generics_with_block(&mut decl.generics); let mod_name = generate_runtime_mod_name_for_trait(&decl.ident); let found_attributes = remove_supported_attributes(&mut decl.attrs); - let api_version = - get_api_version(&found_attributes).map(|v| generate_runtime_api_version(v as u32))?; + let api_version = get_api_version(&found_attributes).map(generate_runtime_api_version)?; let id = generate_runtime_api_id(&decl.ident.to_string()); - let metadata = crate::runtime_metadata::generate_decl_runtime_metadata(&decl); - let trait_api_version = get_api_version(&found_attributes)?; - let mut methods_by_version: BTreeMap> = BTreeMap::new(); + let mut methods_by_version: BTreeMap> = BTreeMap::new(); // Process the items in the declaration. The filter_map function below does a lot of stuff // because the method attributes are stripped at this point @@ -255,6 +251,12 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result { _ => (), }); + let versioned_methods_iter = methods_by_version + .iter() + .flat_map(|(&version, methods)| methods.iter().map(move |method| (method, version))); + let metadata = + crate::runtime_metadata::generate_decl_runtime_metadata(&decl, versioned_methods_iter); + let versioned_api_traits = generate_versioned_api_traits(decl.clone(), methods_by_version); let main_api_ident = decl.ident.clone(); @@ -505,7 +507,7 @@ fn generate_runtime_api_version(version: u32) -> TokenStream { } /// Generates the implementation of `RuntimeApiInfo` for the given trait. -fn generate_runtime_info_impl(trait_: &ItemTrait, version: u64) -> TokenStream { +fn generate_runtime_info_impl(trait_: &ItemTrait, version: u32) -> TokenStream { let trait_name = &trait_.ident; let crate_ = generate_crate_access(); let id = generate_runtime_api_id(&trait_name.to_string()); @@ -537,7 +539,7 @@ fn generate_runtime_info_impl(trait_: &ItemTrait, version: u64) -> TokenStream { } /// Get changed in version from the user given attribute or `Ok(None)`, if no attribute was given. -fn get_changed_in(found_attributes: &HashMap<&'static str, Attribute>) -> Result> { +fn get_changed_in(found_attributes: &HashMap<&'static str, Attribute>) -> Result> { found_attributes .get(&CHANGED_IN_ATTRIBUTE) .map(|v| parse_runtime_api_version(v).map(Some)) @@ -545,7 +547,7 @@ fn get_changed_in(found_attributes: &HashMap<&'static str, Attribute>) -> Result } /// Get the api version from the user given attribute or `Ok(1)`, if no attribute was given. -fn get_api_version(found_attributes: &HashMap<&'static str, Attribute>) -> Result { +fn get_api_version(found_attributes: &HashMap<&'static str, Attribute>) -> Result { found_attributes .get(&API_VERSION_ATTRIBUTE) .map(parse_runtime_api_version) @@ -610,7 +612,7 @@ impl CheckTraitDecl { /// /// Any error is stored in `self.errors`. fn check_method_declarations<'a>(&mut self, methods: impl Iterator) { - let mut method_to_signature_changed = HashMap::>>::new(); + let mut method_to_signature_changed = HashMap::>>::new(); methods.into_iter().for_each(|method| { let attributes = remove_supported_attributes(&mut method.attrs.clone()); diff --git a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs index de922e3253e47c929ea348f187f3440a0ed1f33a..5c9448da2bc7e4e3bdd96655e5e4cea2953f63d2 100644 --- a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -15,14 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - common::API_VERSION_ATTRIBUTE, - utils::{ - extract_block_type_from_trait_path, extract_impl_trait, - extract_parameter_names_types_and_borrows, generate_crate_access, - generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, - versioned_trait_name, AllowSelfRefInParameters, RequireQualifiedTraitPath, - }, +use crate::utils::{ + extract_api_version, extract_block_type_from_trait_path, extract_impl_trait, + extract_parameter_names_types_and_borrows, generate_crate_access, + generate_runtime_mod_name_for_trait, prefix_function_with_trait, versioned_trait_name, + AllowSelfRefInParameters, ApiVersion, RequireQualifiedTraitPath, }; use proc_macro2::{Span, TokenStream}; @@ -31,11 +28,11 @@ use quote::quote; use syn::{ fold::{self, Fold}, - parenthesized, parse::{Error, Parse, ParseStream, Result}, parse_macro_input, parse_quote, spanned::Spanned, - Attribute, Ident, ImplItem, ItemImpl, LitInt, LitStr, Path, Signature, Type, TypePath, + visit_mut::{self, VisitMut}, + Attribute, Ident, ImplItem, ItemImpl, Path, Signature, Type, TypePath, }; use std::collections::HashMap; @@ -227,34 +224,34 @@ fn generate_wasm_interface(impls: &[ItemImpl]) -> Result { let c = generate_crate_access(); let impl_calls = - generate_impl_calls(impls, &input)? - .into_iter() - .map(|(trait_, fn_name, impl_, attrs)| { - let fn_name = - Ident::new(&prefix_function_with_trait(&trait_, &fn_name), Span::call_site()); - - quote!( - #c::std_disabled! { - #( #attrs )* - #[no_mangle] - #[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), #c::__private::polkavm_export(abi = #c::__private::polkavm_abi))] - pub unsafe extern fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 { - let mut #input = if input_len == 0 { - &[0u8; 0] - } else { - unsafe { - ::core::slice::from_raw_parts(input_data, input_len) - } - }; - - #c::init_runtime_logger(); - - let output = (move || { #impl_ })(); - #c::to_substrate_wasm_fn_return_value(&output) - } - } - ) - }); + generate_impl_calls(impls, &input)? + .into_iter() + .map(|(trait_, fn_name, impl_, attrs)| { + let fn_name = + Ident::new(&prefix_function_with_trait(&trait_, &fn_name), Span::call_site()); + + quote!( + #c::std_disabled! { + #( #attrs )* + #[no_mangle] + #[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), #c::__private::polkavm_export(abi = #c::__private::polkavm_abi))] + pub unsafe extern fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 { + let mut #input = if input_len == 0 { + &[0u8; 0] + } else { + unsafe { + ::core::slice::from_raw_parts(input_data, input_len) + } + }; + + #c::init_runtime_logger(); + + let output = (move || { #impl_ })(); + #c::to_substrate_wasm_fn_return_value(&output) + } + } + ) + }); Ok(quote!( #( #impl_calls )* )) } @@ -396,10 +393,10 @@ fn generate_runtime_api_base_structures() -> Result { impl> RuntimeApiImpl { fn commit_or_rollback_transaction(&self, commit: bool) { let proof = "\ - We only close a transaction when we opened one ourself. - Other parts of the runtime that make use of transactions (state-machine) - also balance their transactions. The runtime cannot close client initiated - transactions; qed"; + We only close a transaction when we opened one ourself. + Other parts of the runtime that make use of transactions (state-machine) + also balance their transactions. The runtime cannot close client initiated + transactions; qed"; let res = if commit { let res = if let Some(recorder) = &self.recorder { @@ -466,7 +463,7 @@ fn extend_with_runtime_decl_path(mut trait_: Path) -> Path { trait_ } -fn extend_with_api_version(mut trait_: Path, version: Option) -> Path { +fn extend_with_api_version(mut trait_: Path, version: Option) -> Path { let version = if let Some(v) = version { v } else { @@ -740,8 +737,8 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { let mut error = Error::new( span, "Two traits with the same name detected! \ - The trait name is used to generate its ID. \ - Please rename one trait at the declaration!", + The trait name is used to generate its ID. \ + Please rename one trait at the declaration!", ); error.combine(Error::new(other_span, "First trait implementation.")); @@ -787,17 +784,50 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { )) } +/// replaces `Self` with explicit `ItemImpl.self_ty`. +struct ReplaceSelfImpl { + self_ty: Box, +} + +impl ReplaceSelfImpl { + /// Replace `Self` with `ItemImpl.self_ty` + fn replace(&mut self, trait_: &mut ItemImpl) { + visit_mut::visit_item_impl_mut(self, trait_) + } +} + +impl VisitMut for ReplaceSelfImpl { + fn visit_type_mut(&mut self, ty: &mut syn::Type) { + match ty { + Type::Path(p) if p.path.is_ident("Self") => { + *ty = *self.self_ty.clone(); + }, + ty => syn::visit_mut::visit_type_mut(self, ty), + } + } +} + +/// Rename `Self` to `ItemImpl.self_ty` in all items. +fn rename_self_in_trait_impls(impls: &mut [ItemImpl]) { + impls.iter_mut().for_each(|i| { + let mut checker = ReplaceSelfImpl { self_ty: i.self_ty.clone() }; + checker.replace(i); + }); +} + /// The implementation of the `impl_runtime_apis!` macro. pub fn impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Parse all impl blocks - let RuntimeApiImpls { impls: api_impls } = parse_macro_input!(input as RuntimeApiImpls); + let RuntimeApiImpls { impls: mut api_impls } = parse_macro_input!(input as RuntimeApiImpls); - impl_runtime_apis_impl_inner(&api_impls) + impl_runtime_apis_impl_inner(&mut api_impls) .unwrap_or_else(|e| e.to_compile_error()) .into() } -fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { +fn impl_runtime_apis_impl_inner(api_impls: &mut [ItemImpl]) -> Result { + rename_self_in_trait_impls(api_impls); + let dispatch_impl = generate_dispatch_function(api_impls)?; let api_impls_for_runtime = generate_api_impl_for_runtime(api_impls)?; let base_runtime_api = generate_runtime_api_base_structures()?; @@ -841,88 +871,6 @@ fn filter_cfg_attrs(attrs: &[Attribute]) -> Vec { attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect() } -/// Parse feature flagged api_version. -/// E.g. `#[cfg_attr(feature = "enable-staging-api", api_version(99))]` -fn extract_cfg_api_version(attrs: &Vec, span: Span) -> Result> { - let cfg_attrs = attrs.iter().filter(|a| a.path().is_ident("cfg_attr")).collect::>(); - - let mut cfg_api_version_attr = Vec::new(); - for cfg_attr in cfg_attrs { - let mut feature_name = None; - let mut api_version = None; - cfg_attr.parse_nested_meta(|m| { - if m.path.is_ident("feature") { - let a = m.value()?; - let b: LitStr = a.parse()?; - feature_name = Some(b.value()); - } else if m.path.is_ident(API_VERSION_ATTRIBUTE) { - let content; - parenthesized!(content in m.input); - let ver: LitInt = content.parse()?; - api_version = Some(ver.base10_parse::()?); - } - Ok(()) - })?; - - // If there is a cfg attribute containing api_version - save if for processing - if let (Some(feature_name), Some(api_version)) = (feature_name, api_version) { - cfg_api_version_attr.push((feature_name, api_version, cfg_attr.span())); - } - } - - if cfg_api_version_attr.len() > 1 { - let mut err = Error::new(span, format!("Found multiple feature gated api versions (cfg attribute with nested `{}` attribute). This is not supported.", API_VERSION_ATTRIBUTE)); - for (_, _, attr_span) in cfg_api_version_attr { - err.combine(Error::new(attr_span, format!("`{}` found here", API_VERSION_ATTRIBUTE))); - } - - return Err(err); - } - - Ok(cfg_api_version_attr - .into_iter() - .next() - .map(|(feature, name, _)| (feature, name))) -} - -/// Represents an API version. -struct ApiVersion { - /// Corresponds to `#[api_version(X)]` attribute. - pub custom: Option, - /// Corresponds to `#[cfg_attr(feature = "enable-staging-api", api_version(99))]` - /// attribute. `String` is the feature name, `u64` the staging api version. - pub feature_gated: Option<(String, u64)>, -} - -// Extracts the value of `API_VERSION_ATTRIBUTE` and handles errors. -// Returns: -// - Err if the version is malformed -// - `ApiVersion` on success. If a version is set or not is determined by the fields of `ApiVersion` -fn extract_api_version(attrs: &Vec, span: Span) -> Result { - // First fetch all `API_VERSION_ATTRIBUTE` values (should be only one) - let api_ver = attrs - .iter() - .filter(|a| a.path().is_ident(API_VERSION_ATTRIBUTE)) - .collect::>(); - - if api_ver.len() > 1 { - return Err(Error::new( - span, - format!( - "Found multiple #[{}] attributes for an API implementation. \ - Each runtime API can have only one version.", - API_VERSION_ATTRIBUTE - ), - )); - } - - // Parse the runtime version if there exists one. - Ok(ApiVersion { - custom: api_ver.first().map(|v| parse_runtime_api_version(v)).transpose()?, - feature_gated: extract_cfg_api_version(attrs, span)?, - }) -} - #[cfg(test)] mod tests { use super::*; @@ -945,4 +893,34 @@ mod tests { assert_eq!(cfg_std, filtered[0]); assert_eq!(cfg_benchmarks, filtered[1]); } + + #[test] + fn impl_trait_rename_self_param() { + let code = quote::quote! { + impl client::Core for Runtime { + fn initialize_block(header: &HeaderFor) -> Output { + let _: HeaderFor = header.clone(); + example_fn::(header) + } + } + }; + let expected = quote::quote! { + impl client::Core for Runtime { + fn initialize_block(header: &HeaderFor) -> Output { + let _: HeaderFor = header.clone(); + example_fn::(header) + } + } + }; + + // Parse the items + let RuntimeApiImpls { impls: mut api_impls } = + syn::parse2::(code).unwrap(); + + // Run the renamer which is being run first in the `impl_runtime_apis!` macro. + rename_self_in_trait_impls(&mut api_impls); + let result: TokenStream = quote::quote! { #(#api_impls)* }; + + assert_eq!(result.to_string(), expected.to_string()); + } } diff --git a/substrate/primitives/api/proc-macro/src/runtime_metadata.rs b/substrate/primitives/api/proc-macro/src/runtime_metadata.rs index 4cba524dbe253562f7641365f31636cc144aeea6..6be396339259866cb5ad8d9d63eab25bf2eb43f0 100644 --- a/substrate/primitives/api/proc-macro/src/runtime_metadata.rs +++ b/substrate/primitives/api/proc-macro/src/runtime_metadata.rs @@ -17,14 +17,11 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::{parse_quote, ItemImpl, ItemTrait, Result}; - -use crate::{ - common::CHANGED_IN_ATTRIBUTE, - utils::{ - extract_impl_trait, filter_cfg_attributes, generate_crate_access, - generate_runtime_mod_name_for_trait, get_doc_literals, RequireQualifiedTraitPath, - }, +use syn::{parse_quote, spanned::Spanned, ItemImpl, ItemTrait, Result}; + +use crate::utils::{ + extract_api_version, extract_impl_trait, filter_cfg_attributes, generate_crate_access, + generate_runtime_mod_name_for_trait, get_doc_literals, RequireQualifiedTraitPath, }; /// Get the type parameter argument without lifetime or mutability @@ -72,7 +69,10 @@ fn collect_docs(attrs: &[syn::Attribute], crate_: &TokenStream2) -> TokenStream2 /// /// The metadata is exposed as a generic function on the hidden module /// of the trait generated by the `decl_runtime_apis`. -pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { +pub fn generate_decl_runtime_metadata<'a>( + decl: &ItemTrait, + versioned_methods_iter: impl Iterator, +) -> TokenStream2 { let crate_ = generate_crate_access(); let mut methods = Vec::new(); @@ -86,17 +86,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { // This restricts the bounds at the metadata level, without needing to modify the `BlockT` // itself, since the concrete implementations are already satisfying `TypeInfo`. let mut where_clause = Vec::new(); - for item in &decl.items { - // Collect metadata for methods only. - let syn::TraitItem::Fn(method) = item else { continue }; - - // Collect metadata only for the latest methods. - let is_changed_in = - method.attrs.iter().any(|attr| attr.path().is_ident(CHANGED_IN_ATTRIBUTE)); - if is_changed_in { - continue; - } - + for (method, version) in versioned_methods_iter { let mut inputs = Vec::new(); let signature = &method.sig; for input in &signature.inputs { @@ -135,14 +125,21 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { Ok(deprecation) => deprecation, Err(e) => return e.into_compile_error(), }; + + // Methods are filtered so that only those whose version is <= the `impl_version` passed to + // `runtime_metadata` are kept in the metadata we hand back. methods.push(quote!( #( #attrs )* - #crate_::metadata_ir::RuntimeApiMethodMetadataIR { - name: #method_name, - inputs: #crate_::vec![ #( #inputs, )* ], - output: #output, - docs: #docs, - deprecation_info: #deprecation, + if #version <= impl_version { + Some(#crate_::metadata_ir::RuntimeApiMethodMetadataIR { + name: #method_name, + inputs: #crate_::vec![ #( #inputs, )* ], + output: #output, + docs: #docs, + deprecation_info: #deprecation, + }) + } else { + None } )); } @@ -176,12 +173,15 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { #crate_::frame_metadata_enabled! { #( #attrs )* #[inline(always)] - pub fn runtime_metadata #impl_generics () -> #crate_::metadata_ir::RuntimeApiMetadataIR + pub fn runtime_metadata #impl_generics (impl_version: u32) -> #crate_::metadata_ir::RuntimeApiMetadataIR #where_clause { #crate_::metadata_ir::RuntimeApiMetadataIR { name: #trait_name, - methods: #crate_::vec![ #( #methods, )* ], + methods: [ #( #methods, )* ] + .into_iter() + .filter_map(|maybe_m| maybe_m) + .collect(), docs: #docs, deprecation_info: #deprecation, } @@ -242,10 +242,43 @@ pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result Result { +pub fn parse_runtime_api_version(version: &Attribute) -> Result { let version = version.parse_args::().map_err(|_| { Error::new( version.span(), @@ -231,7 +231,7 @@ pub fn parse_runtime_api_version(version: &Attribute) -> Result { } /// Each versioned trait is named 'ApiNameVN' where N is the specific version. E.g. ParachainHostV2 -pub fn versioned_trait_name(trait_ident: &Ident, version: u64) -> Ident { +pub fn versioned_trait_name(trait_ident: &Ident, version: u32) -> Ident { format_ident!("{}V{}", trait_ident, version) } @@ -334,6 +334,89 @@ pub fn get_deprecation(crate_: &TokenStream, attrs: &[syn::Attribute]) -> Result .unwrap_or_else(|| Ok(quote! {#crate_::metadata_ir::DeprecationStatusIR::NotDeprecated})) } +/// Represents an API version. +pub struct ApiVersion { + /// Corresponds to `#[api_version(X)]` attribute. + pub custom: Option, + /// Corresponds to `#[cfg_attr(feature = "enable-staging-api", api_version(99))]` + /// attribute. `String` is the feature name, `u32` the staging api version. + pub feature_gated: Option<(String, u32)>, +} + +/// Extracts the value of `API_VERSION_ATTRIBUTE` and handles errors. +/// Returns: +/// - Err if the version is malformed +/// - `ApiVersion` on success. If a version is set or not is determined by the fields of +/// `ApiVersion` +pub fn extract_api_version(attrs: &[Attribute], span: Span) -> Result { + // First fetch all `API_VERSION_ATTRIBUTE` values (should be only one) + let api_ver = attrs + .iter() + .filter(|a| a.path().is_ident(API_VERSION_ATTRIBUTE)) + .collect::>(); + + if api_ver.len() > 1 { + return Err(Error::new( + span, + format!( + "Found multiple #[{}] attributes for an API implementation. \ + Each runtime API can have only one version.", + API_VERSION_ATTRIBUTE + ), + )); + } + + // Parse the runtime version if there exists one. + Ok(ApiVersion { + custom: api_ver.first().map(|v| parse_runtime_api_version(v)).transpose()?, + feature_gated: extract_cfg_api_version(attrs, span)?, + }) +} + +/// Parse feature flagged api_version. +/// E.g. `#[cfg_attr(feature = "enable-staging-api", api_version(99))]` +fn extract_cfg_api_version(attrs: &[Attribute], span: Span) -> Result> { + let cfg_attrs = attrs.iter().filter(|a| a.path().is_ident("cfg_attr")).collect::>(); + + let mut cfg_api_version_attr = Vec::new(); + for cfg_attr in cfg_attrs { + let mut feature_name = None; + let mut api_version = None; + cfg_attr.parse_nested_meta(|m| { + if m.path.is_ident("feature") { + let a = m.value()?; + let b: LitStr = a.parse()?; + feature_name = Some(b.value()); + } else if m.path.is_ident(API_VERSION_ATTRIBUTE) { + let content; + parenthesized!(content in m.input); + let ver: LitInt = content.parse()?; + api_version = Some(ver.base10_parse::()?); + } + Ok(()) + })?; + + // If there is a cfg attribute containing api_version - save if for processing + if let (Some(feature_name), Some(api_version)) = (feature_name, api_version) { + cfg_api_version_attr.push((feature_name, api_version, cfg_attr.span())); + } + } + + if cfg_api_version_attr.len() > 1 { + let mut err = Error::new(span, format!("Found multiple feature gated api versions (cfg attribute with nested `{}` attribute). This is not supported.", API_VERSION_ATTRIBUTE)); + for (_, _, attr_span) in cfg_api_version_attr { + err.combine(Error::new(attr_span, format!("`{}` found here", API_VERSION_ATTRIBUTE))); + } + + return Err(err); + } + + Ok(cfg_api_version_attr + .into_iter() + .next() + .map(|(feature, name, _)| (feature, name))) +} + #[cfg(test)] mod tests { use assert_matches::assert_matches; diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs index 700e212688c8d7bf2666e37a123d4ea5413a1808..b412d4b52fed5208d0d2fe8ace56977822b3b55f 100644 --- a/substrate/primitives/api/src/lib.rs +++ b/substrate/primitives/api/src/lib.rs @@ -105,7 +105,7 @@ pub mod __private { generic::BlockId, traits::{Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT, NumberFor}, transaction_validity::TransactionValidity, - ExtrinsicInclusionMode, RuntimeString, TransactionOutcome, + ExtrinsicInclusionMode, TransactionOutcome, }; pub use sp_version::{create_apis_vec, ApiId, ApisVec, RuntimeVersion}; @@ -286,7 +286,7 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// # Example /// /// ```rust -/// use sp_version::create_runtime_str; +/// extern crate alloc; /// # /// # use sp_runtime::{ExtrinsicInclusionMode, traits::Block as BlockT}; /// # use sp_test_primitives::Block; @@ -338,8 +338,8 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// /// /// Runtime version. This needs to be declared for each runtime. /// pub const VERSION: sp_version::RuntimeVersion = sp_version::RuntimeVersion { -/// spec_name: create_runtime_str!("node"), -/// impl_name: create_runtime_str!("test-node"), +/// spec_name: alloc::borrow::Cow::Borrowed("node"), +/// impl_name: alloc::borrow::Cow::Borrowed("test-node"), /// authoring_version: 1, /// spec_version: 1, /// impl_version: 0, diff --git a/substrate/primitives/api/test/tests/decl_and_impl.rs b/substrate/primitives/api/test/tests/decl_and_impl.rs index 211a08561fd4bcf601d9508d96447fc07f82657f..890cf6eccdbcb13bd23a789f89ccc6c430c18342 100644 --- a/substrate/primitives/api/test/tests/decl_and_impl.rs +++ b/substrate/primitives/api/test/tests/decl_and_impl.rs @@ -24,7 +24,7 @@ use substrate_test_runtime_client::runtime::{Block, Hash}; /// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real /// runtime. -pub enum Runtime {} +pub struct Runtime {} decl_runtime_apis! { pub trait Api { @@ -306,3 +306,62 @@ fn mock_runtime_api_works_with_advanced() { mock.wild_card(Hash::repeat_byte(0x01), 1).unwrap_err().to_string(), ); } + +#[test] +fn runtime_api_metadata_matches_version_implemented() { + let rt = Runtime {}; + let runtime_metadata = rt.runtime_metadata(); + + // Check that the metadata for some runtime API matches expectation. + let assert_has_api_with_methods = |api_name: &str, api_methods: &[&str]| { + let Some(api) = runtime_metadata.iter().find(|api| api.name == api_name) else { + panic!("Can't find runtime API '{api_name}'"); + }; + if api.methods.len() != api_methods.len() { + panic!( + "Wrong number of methods in '{api_name}'; expected {} methods but got {}: {:?}", + api_methods.len(), + api.methods.len(), + api.methods + ); + } + for expected_name in api_methods { + if !api.methods.iter().any(|method| &method.name == expected_name) { + panic!("Can't find API method '{expected_name}' in '{api_name}'"); + } + } + }; + + assert_has_api_with_methods("ApiWithCustomVersion", &["same_name"]); + + assert_has_api_with_methods("ApiWithMultipleVersions", &["stable_one", "new_one"]); + + assert_has_api_with_methods( + "ApiWithStagingMethod", + &[ + "stable_one", + #[cfg(feature = "enable-staging-api")] + "staging_one", + ], + ); + + assert_has_api_with_methods( + "ApiWithStagingAndVersionedMethods", + &[ + "stable_one", + "new_one", + #[cfg(feature = "enable-staging-api")] + "staging_one", + ], + ); + + assert_has_api_with_methods( + "ApiWithStagingAndChangedBase", + &[ + "stable_one", + "new_one", + #[cfg(feature = "enable-staging-api")] + "staging_one", + ], + ); +} diff --git a/substrate/primitives/consensus/babe/src/lib.rs b/substrate/primitives/consensus/babe/src/lib.rs index ee07da6829f52933424a838e36a2c1246bf9aeb7..163fbafa8dd4c6c80a37fe2b6bf820c24c9a6065 100644 --- a/substrate/primitives/consensus/babe/src/lib.rs +++ b/substrate/primitives/consensus/babe/src/lib.rs @@ -134,7 +134,7 @@ pub enum ConsensusLog { } /// Configuration data used by the BABE consensus engine. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct BabeConfigurationV1 { /// The slot duration in milliseconds for BABE. Currently, only /// the value provided by this type at genesis will be used. diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index b04d94e2bf40ddce312ef923b36063defb26ba69..cf24861e233c1c855fd36d9a752699cdac5a42b1 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -17,7 +17,7 @@ //! Cryptographic utilities. -use crate::{ed25519, sr25519}; +use crate::{ed25519, sr25519, U256}; use alloc::{format, str, vec::Vec}; #[cfg(all(not(feature = "std"), feature = "serde"))] use alloc::{string::String, vec}; @@ -1191,7 +1191,7 @@ macro_rules! impl_from_entropy_base { } } -impl_from_entropy_base!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); +impl_from_entropy_base!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, U256); #[cfg(test)] mod tests { diff --git a/substrate/primitives/genesis-builder/src/lib.rs b/substrate/primitives/genesis-builder/src/lib.rs index 4763aa06341e84145087082e15af5aa56c958ef5..9abc278688646737b8bd7513318763f2a2b49cca 100644 --- a/substrate/primitives/genesis-builder/src/lib.rs +++ b/substrate/primitives/genesis-builder/src/lib.rs @@ -73,13 +73,13 @@ //! which is the foundation for genesis block. extern crate alloc; -use alloc::vec::Vec; +use alloc::{string::String, vec::Vec}; /// The result type alias, used in build methods. `Err` contains formatted error message. -pub type Result = core::result::Result<(), sp_runtime::RuntimeString>; +pub type Result = core::result::Result<(), String>; /// The type representing preset ID. -pub type PresetId = sp_runtime::RuntimeString; +pub type PresetId = String; /// The default `development` preset used to communicate with the runtime via /// [`GenesisBuilder`] interface. diff --git a/substrate/primitives/panic-handler/src/lib.rs b/substrate/primitives/panic-handler/src/lib.rs index 81ccaaee828e4f97ade57f03bf07221557f6d4e0..c4a7eb8dc67c7cf8e4defcb5f60746a16d04cc40 100644 --- a/substrate/primitives/panic-handler/src/lib.rs +++ b/substrate/primitives/panic-handler/src/lib.rs @@ -30,7 +30,7 @@ use std::{ cell::Cell, io::{self, Write}, marker::PhantomData, - panic::{self, PanicHookInfo}, + panic::{self, PanicInfo}, sync::LazyLock, thread, }; @@ -149,7 +149,7 @@ fn strip_control_codes(input: &str) -> std::borrow::Cow { } /// Function being called when a panic happens. -fn panic_hook(info: &PanicHookInfo, report_url: &str, version: &str) { +fn panic_hook(info: &PanicInfo, report_url: &str, version: &str) { let location = info.location(); let file = location.as_ref().map(|l| l.file()).unwrap_or(""); let line = location.as_ref().map(|l| l.line()).unwrap_or(0); diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index 6eed57656a6da6d9f20df6a9b7833712914c00f4..f0c8e50f1ba1e66993b98badf980d1f385f8591c 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -49,7 +49,7 @@ extern crate alloc; #[doc(hidden)] -pub use alloc::{format, vec::Vec}; +pub use alloc::vec::Vec; #[doc(hidden)] pub use codec; #[doc(hidden)] @@ -90,15 +90,12 @@ mod multiaddress; pub mod offchain; pub mod proving_trie; pub mod runtime_logger; -mod runtime_string; #[cfg(feature = "std")] pub mod testing; pub mod traits; pub mod transaction_validity; pub mod type_with_default; -pub use crate::runtime_string::*; - // Re-export Multiaddress pub use multiaddress::MultiAddress; @@ -953,7 +950,7 @@ impl<'a> ::serde::Deserialize<'a> for OpaqueExtrinsic { { let r = ::sp_core::bytes::deserialize(de)?; Decode::decode(&mut &r[..]) - .map_err(|e| ::serde::de::Error::custom(format!("Decode error: {}", e))) + .map_err(|e| ::serde::de::Error::custom(alloc::format!("Decode error: {}", e))) } } @@ -1037,6 +1034,23 @@ impl OpaqueValue { } } +// TODO: Remove in future versions and clean up `parse_str_literal` in `sp-version-proc-macro` +/// Deprecated `Cow::Borrowed()` wrapper. +#[macro_export] +#[deprecated = "Use Cow::Borrowed() instead of create_runtime_str!()"] +macro_rules! create_runtime_str { + ( $y:expr ) => {{ + $crate::Cow::Borrowed($y) + }}; +} +// TODO: Re-export for ^ macro `create_runtime_str`, should be removed once macro is gone +#[doc(hidden)] +pub use alloc::borrow::Cow; +// TODO: Remove in future versions +/// Deprecated alias to improve upgrade experience +#[deprecated = "Use String or Cow<'static, str> instead"] +pub type RuntimeString = alloc::string::String; + #[cfg(test)] mod tests { use crate::traits::BlakeTwo256; diff --git a/substrate/primitives/runtime/src/runtime_string.rs b/substrate/primitives/runtime/src/runtime_string.rs deleted file mode 100644 index bb0347badcbbe510d3f3ff35942a373ac1dc19cb..0000000000000000000000000000000000000000 --- a/substrate/primitives/runtime/src/runtime_string.rs +++ /dev/null @@ -1,168 +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 alloc::vec::Vec; -use codec::{Decode, Encode}; -use sp_core::RuntimeDebug; - -/// A string that wraps a `&'static str` in the runtime and `String`/`Vec` on decode. -#[derive(Eq, RuntimeDebug, Clone)] -pub enum RuntimeString { - /// The borrowed mode that wraps a `&'static str`. - Borrowed(&'static str), - /// The owned mode that wraps a `String`. - #[cfg(feature = "std")] - Owned(String), - /// The owned mode that wraps a `Vec`. - #[cfg(not(feature = "std"))] - Owned(Vec), -} - -impl scale_info::TypeInfo for RuntimeString { - type Identity = str; - - fn type_info() -> scale_info::Type { - Self::Identity::type_info() - } -} - -/// Convenience macro to use the format! interface to get a `RuntimeString::Owned` -#[macro_export] -macro_rules! format_runtime_string { - ($($args:tt)*) => {{ - #[cfg(feature = "std")] - { - sp_runtime::RuntimeString::Owned(format!($($args)*)) - } - #[cfg(not(feature = "std"))] - { - sp_runtime::RuntimeString::Owned($crate::format!($($args)*).as_bytes().to_vec()) - } - }}; -} - -impl From<&'static str> for RuntimeString { - fn from(data: &'static str) -> Self { - Self::Borrowed(data) - } -} - -impl<'a> TryFrom<&'a RuntimeString> for &'a str { - type Error = core::str::Utf8Error; - fn try_from(from: &'a RuntimeString) -> core::result::Result<&'a str, Self::Error> { - match from { - #[cfg(feature = "std")] - RuntimeString::Owned(string) => Ok(string.as_str()), - #[cfg(not(feature = "std"))] - RuntimeString::Owned(vec) => core::str::from_utf8(&vec), - RuntimeString::Borrowed(str) => Ok(str), - } - } -} - -#[cfg(feature = "std")] -impl From for String { - fn from(string: RuntimeString) -> Self { - match string { - RuntimeString::Borrowed(data) => data.to_owned(), - RuntimeString::Owned(data) => data, - } - } -} - -impl Default for RuntimeString { - fn default() -> Self { - Self::Borrowed(Default::default()) - } -} - -impl PartialEq for RuntimeString { - fn eq(&self, other: &Self) -> bool { - self.as_ref() == other.as_ref() - } -} - -impl AsRef<[u8]> for RuntimeString { - fn as_ref(&self) -> &[u8] { - match self { - Self::Borrowed(val) => val.as_ref(), - Self::Owned(val) => val.as_ref(), - } - } -} - -#[cfg(feature = "std")] -impl std::ops::Deref for RuntimeString { - type Target = str; - - fn deref(&self) -> &str { - match self { - Self::Borrowed(val) => val, - Self::Owned(val) => val, - } - } -} - -impl Encode for RuntimeString { - fn encode(&self) -> Vec { - match self { - Self::Borrowed(val) => val.encode(), - Self::Owned(val) => val.encode(), - } - } -} - -impl Decode for RuntimeString { - fn decode(value: &mut I) -> Result { - Decode::decode(value).map(Self::Owned) - } -} - -#[cfg(feature = "std")] -impl std::fmt::Display for RuntimeString { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::Borrowed(val) => write!(f, "{}", val), - Self::Owned(val) => write!(f, "{}", val), - } - } -} - -#[cfg(feature = "serde")] -impl serde::Serialize for RuntimeString { - fn serialize(&self, serializer: S) -> Result { - match self { - Self::Borrowed(val) => val.serialize(serializer), - Self::Owned(val) => val.serialize(serializer), - } - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for RuntimeString { - fn deserialize>(de: D) -> Result { - Ok(Self::Owned(serde::Deserialize::deserialize(de)?)) - } -} - -/// Create a const [`RuntimeString`]. -#[macro_export] -macro_rules! create_runtime_str { - ( $y:expr ) => {{ - $crate::RuntimeString::Borrowed($y) - }}; -} diff --git a/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs b/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs index b4f749c90f59204029242b6275c3ff9ba140bc2c..ac6d501b927d7fb2af03ed1617ee494cf99c69c9 100644 --- a/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs +++ b/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs @@ -182,21 +182,47 @@ impl ParseRuntimeVersion { } fn parse_str_literal(expr: &Expr) -> Result { - let mac = match *expr { - Expr::Macro(syn::ExprMacro { ref mac, .. }) => mac, - _ => return Err(Error::new(expr.span(), "a macro expression is expected here")), - }; + match expr { + // TODO: Remove this branch when `sp_runtime::create_runtime_str` is removed + Expr::Macro(syn::ExprMacro { mac, .. }) => { + let lit: ExprLit = mac.parse_body().map_err(|e| { + Error::new( + e.span(), + format!( + "a single literal argument is expected, but parsing is failed: {}", + e + ), + ) + })?; - let lit: ExprLit = mac.parse_body().map_err(|e| { - Error::new( - e.span(), - format!("a single literal argument is expected, but parsing is failed: {}", e), - ) - })?; + match &lit.lit { + Lit::Str(lit) => Ok(lit.value()), + _ => Err(Error::new(lit.span(), "only string literals are supported here")), + } + }, + Expr::Call(call) => { + if call.args.len() != 1 { + return Err(Error::new( + expr.span(), + "a single literal argument is expected, but parsing is failed", + )); + } + let Expr::Lit(lit) = call.args.first().expect("Length checked above; qed") else { + return Err(Error::new( + expr.span(), + "a single literal argument is expected, but parsing is failed", + )); + }; - match lit.lit { - Lit::Str(ref lit) => Ok(lit.value()), - _ => Err(Error::new(lit.span(), "only string literals are supported here")), + match &lit.lit { + Lit::Str(lit) => Ok(lit.value()), + _ => Err(Error::new(lit.span(), "only string literals are supported here")), + } + }, + _ => Err(Error::new( + expr.span(), + format!("a function call is expected here, instead of: {expr:?}"), + )), } } diff --git a/substrate/primitives/version/src/lib.rs b/substrate/primitives/version/src/lib.rs index a9f1c2373069523cc503071626907626a88de745..2e1464646647927592877da3aca196e02129dffa 100644 --- a/substrate/primitives/version/src/lib.rs +++ b/substrate/primitives/version/src/lib.rs @@ -46,7 +46,7 @@ use std::collections::HashSet; pub use alloc::borrow::Cow; use codec::{Decode, Encode, Input}; use scale_info::TypeInfo; -use sp_runtime::RuntimeString; +#[allow(deprecated)] pub use sp_runtime::{create_runtime_str, StateVersion}; #[doc(hidden)] pub use sp_std; @@ -72,12 +72,15 @@ pub mod embed; /// This macro accepts a const item like the following: /// /// ```rust -/// use sp_version::{create_runtime_str, RuntimeVersion}; +/// extern crate alloc; +/// +/// use alloc::borrow::Cow; +/// use sp_version::RuntimeVersion; /// /// #[sp_version::runtime_version] /// pub const VERSION: RuntimeVersion = RuntimeVersion { -/// spec_name: create_runtime_str!("test"), -/// impl_name: create_runtime_str!("test"), +/// spec_name: Cow::Borrowed("test"), +/// impl_name: Cow::Borrowed("test"), /// authoring_version: 10, /// spec_version: 265, /// impl_version: 1, @@ -164,14 +167,14 @@ pub struct RuntimeVersion { /// Identifies the different Substrate runtimes. There'll be at least polkadot and node. /// A different on-chain spec_name to that of the native runtime would normally result /// in node not attempting to sync or author blocks. - pub spec_name: RuntimeString, + pub spec_name: Cow<'static, str>, /// Name of the implementation of the spec. This is of little consequence for the node /// and serves only to differentiate code of different implementation teams. For this /// codebase, it will be parity-polkadot. If there were a non-Rust implementation of the /// Polkadot runtime (e.g. C++), then it would identify itself with an accordingly different /// `impl_name`. - pub impl_name: RuntimeString, + pub impl_name: Cow<'static, str>, /// `authoring_version` is the version of the authorship interface. An authoring node /// will not attempt to author blocks unless this is equal to its native runtime. @@ -472,8 +475,8 @@ impl<'de> serde::Deserialize<'de> for RuntimeVersion { where A: serde::de::MapAccess<'de>, { - let mut spec_name: Option = None; - let mut impl_name: Option = None; + let mut spec_name: Option> = None; + let mut impl_name: Option> = None; let mut authoring_version: Option = None; let mut spec_version: Option = None; let mut impl_version: Option = None; diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index a4a0d348a39019b130e330ced14b7d6ec9c6dfc0..1314d9d6dd4596d6d117c6bfdd0bb675ec009ad6 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -63,7 +63,7 @@ pub use sp_core::hash::H256; use sp_genesis_builder::PresetId; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ - create_runtime_str, impl_opaque_keys, impl_tx_ext_default, + impl_opaque_keys, impl_tx_ext_default, traits::{BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, NumberFor, Verify}, transaction_validity::{ TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, @@ -114,8 +114,8 @@ pub fn wasm_binary_logging_disabled_unwrap() -> &'static [u8] { /// Test runtime version. #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("test"), - impl_name: create_runtime_str!("parity-test"), + spec_name: alloc::borrow::Cow::Borrowed("test"), + impl_name: alloc::borrow::Cow::Borrowed("parity-test"), authoring_version: 1, spec_version: 2, impl_version: 2, @@ -727,8 +727,8 @@ impl_runtime_apis! { fn get_preset(name: &Option) -> Option> { get_preset::(name, |name| { - let patch = match name.try_into() { - Ok("staging") => { + let patch = match name.as_ref() { + "staging" => { let endowed_accounts: Vec = vec![ AccountKeyring::Bob.public().into(), AccountKeyring::Charlie.public().into(), @@ -746,7 +746,7 @@ impl_runtime_apis! { } }) }, - Ok("foobar") => json!({"foo":"bar"}), + "foobar" => json!({"foo":"bar"}), _ => return None, }; Some(serde_json::to_string(&patch) @@ -1392,10 +1392,8 @@ mod tests { let r = BuildResult::decode(&mut &r[..]).unwrap(); log::info!("result: {:#?}", r); assert_eq!(r, Err( - sp_runtime::RuntimeString::Owned( - "Invalid JSON blob: unknown field `renamed_authorities`, expected `authorities` or `epochConfig` at line 4 column 25".to_string(), - )) - ); + "Invalid JSON blob: unknown field `renamed_authorities`, expected `authorities` or `epochConfig` at line 4 column 25".to_string(), + )); } #[test] @@ -1406,10 +1404,8 @@ mod tests { let r = executor_call(&mut t, "GenesisBuilder_build_state", &j.encode()).unwrap(); let r = BuildResult::decode(&mut &r[..]).unwrap(); assert_eq!(r, Err( - sp_runtime::RuntimeString::Owned( - "Invalid JSON blob: unknown field `babex`, expected one of `system`, `babe`, `substrateTest`, `balances` at line 3 column 9".to_string(), - )) - ); + "Invalid JSON blob: unknown field `babex`, expected one of `system`, `babe`, `substrateTest`, `balances` at line 3 column 9".to_string(), + )); } #[test] @@ -1419,14 +1415,11 @@ mod tests { let mut t = BasicExternalities::new_empty(); let r = executor_call(&mut t, "GenesisBuilder_build_state", &j.encode()).unwrap(); - let r = - core::result::Result::<(), sp_runtime::RuntimeString>::decode(&mut &r[..]).unwrap(); + let r = core::result::Result::<(), String>::decode(&mut &r[..]).unwrap(); assert_eq!( r, - Err(sp_runtime::RuntimeString::Owned( - "Invalid JSON blob: missing field `authorities` at line 11 column 3" - .to_string() - )) + Err("Invalid JSON blob: missing field `authorities` at line 11 column 3" + .to_string()) ); } diff --git a/substrate/utils/wasm-builder/src/builder.rs b/substrate/utils/wasm-builder/src/builder.rs index eb761a103d62ecd680747584e077de5025f9955b..a40aafe1d8127d5c250d729ce07ceac210f27757 100644 --- a/substrate/utils/wasm-builder/src/builder.rs +++ b/substrate/utils/wasm-builder/src/builder.rs @@ -303,7 +303,8 @@ fn provide_dummy_wasm_binary_if_not_exist(file_path: &Path) { if !file_path.exists() { crate::write_file_if_changed( file_path, - "pub const WASM_BINARY: Option<&[u8]> = None;\ + "pub const WASM_BINARY_PATH: Option<&str> = None;\ + pub const WASM_BINARY: Option<&[u8]> = None;\ pub const WASM_BINARY_BLOATY: Option<&[u8]> = None;", ); } @@ -378,9 +379,11 @@ fn build_project( file_name, format!( r#" + pub const WASM_BINARY_PATH: Option<&str> = Some("{wasm_binary_path}"); pub const WASM_BINARY: Option<&[u8]> = Some(include_bytes!("{wasm_binary}")); pub const WASM_BINARY_BLOATY: Option<&[u8]> = Some(include_bytes!("{wasm_binary_bloaty}")); "#, + wasm_binary_path = wasm_binary, wasm_binary = wasm_binary, wasm_binary_bloaty = wasm_binary_bloaty, ), diff --git a/substrate/utils/wasm-builder/src/lib.rs b/substrate/utils/wasm-builder/src/lib.rs index e3f2ff5cd733498a01e3d92b9f731d7247c9761f..420ecd63e1dcc2b799d44bc984528346321f9a4b 100644 --- a/substrate/utils/wasm-builder/src/lib.rs +++ b/substrate/utils/wasm-builder/src/lib.rs @@ -48,6 +48,8 @@ //! This will include the generated Wasm binary as two constants `WASM_BINARY` and //! `WASM_BINARY_BLOATY`. The former is a compact Wasm binary and the latter is the Wasm binary as //! being generated by the compiler. Both variables have `Option<&'static [u8]>` as type. +//! Additionally it will create the `WASM_BINARY_PATH` which is the path to the WASM blob on the +//! filesystem. //! //! ### Feature //! diff --git a/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl b/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl index bc587b0447746ce8275fa23bcf5fc816417dc287..cca1f544b3506fc2e6059871e191c1e796050e34 100644 --- a/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl +++ b/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.zndsl @@ -35,7 +35,10 @@ bob: reports block height is at least {{DB_BLOCK_HEIGHT}} within 10 seconds alice: reports substrate_beefy_best_block is at least {{200*180/6}} within 180 seconds bob: reports substrate_beefy_best_block is at least {{200*180/6}} within 180 seconds -alice: count of log lines containing "error" is 0 within 10 seconds +# Validators started without public addresses must emit an error. +# Double check the error is the expected one. +alice: log line matches "No public addresses configured and no global listen addresses found" within 60 seconds +alice: count of log lines containing "error" is 1 within 10 seconds bob: count of log lines containing "verification failed" is 0 within 10 seconds # new blocks were built diff --git a/templates/minimal/Dockerfile b/templates/minimal/Dockerfile index 0c59192208fe66961613f7adaec16284078b9d9f..422f7f726a7e47345d3bc0f3d56dbabc7669ff27 100644 --- a/templates/minimal/Dockerfile +++ b/templates/minimal/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /polkadot COPY . /polkadot RUN cargo fetch -RUN cargo build --locked --release +RUN cargo build --workspace --locked --release FROM docker.io/parity/base-bin:latest diff --git a/templates/minimal/README.md b/templates/minimal/README.md index fe1317a033c7dab4c50aa552ee2a8e14e49d882a..cf43d71d884993c51ba1265ef49d445d4866d1ec 100644 --- a/templates/minimal/README.md +++ b/templates/minimal/README.md @@ -11,30 +11,54 @@ -* ๐Ÿค This template is a minimal (in terms of complexity and the number of components) +## Table of Contents + +- [Intro](#intro) + +- [Template Structure](#template-structure) + +- [Getting Started](#getting-started) + +- [Starting a Minimal Template Chain](#starting-a-minimal-template-chain) + + - [Omni Node](#omni-node) + - [Minimal Template Node](#minimal-template-node) + - [Zombienet with Omni Node](#zombienet-with-omni-node) + - [Zombienet with Minimal Template Node](#zombienet-with-minimal-template-node) + - [Connect with the Polkadot-JS Apps Front-End](#connect-with-the-polkadot-js-apps-front-end) + - [Takeaways](#takeaways) + +- [Contributing](#contributing) + +- [Getting Help](#getting-help) + +## Intro + +- ๐Ÿค This template is a minimal (in terms of complexity and the number of components) template for building a blockchain node. -* ๐Ÿ”ง Its runtime is configured with a single custom pallet as a starting point, and a handful of ready-made pallets +- ๐Ÿ”ง 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). -* ๐Ÿ‘ค The template has no consensus configured - it is best for experimenting with a single node network. +- ๐Ÿ‘ค The template has no consensus configured - it is best for experimenting with a single node network. ## Template Structure A Polkadot SDK based project such as this one consists of: -* ๐Ÿ’ฟ a [Node](./node/README.md) - the binary application. -* ๐Ÿงฎ the [Runtime](./runtime/README.md) - the core logic of the blockchain. -* ๐ŸŽจ the [Pallets](./pallets/README.md) - from which the runtime is constructed. +- ๐Ÿงฎ the [Runtime](./runtime/README.md) - the core logic of the blockchain. +- ๐ŸŽจ the [Pallets](./pallets/README.md) - from which the runtime is constructed. +- ๐Ÿ’ฟ a [Node](./node/README.md) - the binary application (which is not part of the cargo default-members list and is not +compiled unless building the entire workspace). ## Getting Started -* ๐Ÿฆ€ The template is using the Rust language. +- ๐Ÿฆ€ The template is using the Rust language. -* ๐Ÿ‘‰ Check the +- ๐Ÿ‘‰ 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 +- ๐Ÿ› ๏ธ 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 minimal template code: @@ -45,65 +69,152 @@ git clone https://github.com/paritytech/polkadot-sdk-minimal-template.git minima cd minimal-template ``` -### Build +## Starting a Minimal Template Chain + +### Omni Node + +[Omni Node](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html) can +be used to run the minimal 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 installation section on [crates.io/omni-node](https://crates.io/crates/polkadot-omni-node). + +#### Build `minimal-template-runtime` + +```sh +cargo build -p minimal-template-runtime --release +``` + +#### Install `staging-chain-spec-builder` -๐Ÿ”จ Use the following command to build the node without launching it: +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 -cargo build --release +chain-spec-builder create --relay-chain "dev" --para-id 1000 --runtime \ + target/release/wbuild/minimal-template-runtime/minimal_template_runtime.wasm named-preset development ``` -๐Ÿณ Alternatively, build the docker image: +**Note**: the `relay-chain` and `para-id` flags are extra bits of information required to +configure the node for the case of representing a parachain that is connected to a relay chain. +They are not relevant to minimal template business logic, but they are mandatory information for +Omni Node, nonetheless. + +#### Run Omni Node + +Start Omni Node with manual seal (3 seconds block times), minimal template runtime based +chain spec. We'll use `--tmp` flag to start the node with its configurations stored in a +temporary directory, which will be deleted at the end of the process. + +```sh +polkadot-omni-node --chain --dev-block-time 3000 --tmp +``` + +### Minimal Template Node + +#### Build both node & runtime + +```sh +cargo build --workspace --release +``` + +๐Ÿณ Alternatively, build the docker image which builds all the workspace members, +and has as entry point the node binary: ```sh docker build . -t polkadot-sdk-minimal-template ``` -### Single-Node Development Chain +#### Start the `minimal-template-node` -๐Ÿ‘ค The following command starts a single-node development chain: +The `minimal-template-node` has dependency on the `minimal-template-runtime`. It will use +the `minimal_template_runtime::WASM_BINARY` constant (which holds the WASM blob as a byte +array) for chain spec building, while starting. This is in contrast to Omni Node which doesn't +depend on a specific runtime, but asks for the chain spec at startup. ```sh -./target/release/minimal-template-node --dev + --tmp --consensus manual-seal-3000 +# or via docker +docker run --rm polkadot-sdk-minimal-template +``` + +### Zombienet with Omni Node + +#### Install `zombienet` + +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. + +#### Update `zombienet-omni-node.toml` with a valid chain spec path + +Before starting the network with zombienet we must update the network specification +with a valid chain spec path. If we need to generate one, we can look up at the previous +section for chain spec creation [here](#use-chain-spec-builder-to-generate-the-chain_specjson-file). -# docker version: -docker run --rm polkadot-sdk-minimal-template --dev +Then make the changes in the network specification like so: + +```toml +# ... +chain = "dev" +chain_spec_path = "" +default_args = ["--dev-block-time 3000"] +# .. +``` + +#### Start the network + +```sh +zombienet --provider native spawn zombienet-omni-node.toml ``` -Development chains: +### Zombienet with `minimal-template-node` -* ๐Ÿงน Do not persist the state. -* ๐Ÿ’ฐ Are pre-configured with a genesis state that includes several pre-funded development accounts. -* ๐Ÿง‘โ€โš–๏ธ One development account (`ALICE`) is used as `sudo` accounts. +For this one we just need to have `zombienet` installed and run: + +```sh +zombienet --provider native spawn zombienet-multi-node.toml +``` ### Connect with the Polkadot-JS Apps Front-End -* ๐ŸŒ You can interact with your local node using the +- ๐ŸŒ You can interact with your local node using the hosted version of the [Polkadot/Substrate Portal](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9944). -* ๐Ÿช A hosted version is also +- ๐Ÿช 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 +- ๐Ÿง‘โ€๐Ÿ”ง 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 + +Previously minimal template's development chains: + +- โŒ Started in a multi-node setup will produce forks because minimal lacks consensus. +- ๐Ÿงน Do not persist the state. +- ๐Ÿ’ฐ Are pre-configured with a genesis state that includes several pre-funded development accounts. +- ๐Ÿง‘โ€โš–๏ธ One development account (`ALICE`) is used as `sudo` accounts. + ## Contributing -* ๐Ÿ”„ This template is automatically updated after releases in the main [Polkadot SDK monorepo](https://github.com/paritytech/polkadot-sdk). +- ๐Ÿ”„ 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/minimal). +- โžก๏ธ Any pull requests should be directed to this [source](https://github.com/paritytech/polkadot-sdk/tree/master/templates/minimal). -* ๐Ÿ˜‡ Please refer to the monorepo's +- ๐Ÿ˜‡ 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. +- ๐Ÿง‘โ€๐Ÿซ 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 +- ๐Ÿง‘โ€๐Ÿ”ง 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 +- ๐Ÿ‘ฅ Additionally, there are [GitHub issues](https://github.com/paritytech/polkadot-sdk/issues) and [Substrate StackExchange](https://substrate.stackexchange.com/). diff --git a/templates/minimal/node/src/cli.rs b/templates/minimal/node/src/cli.rs index 54107df75a3630d818df881d600e4ce18f886286..f349f8c8da0490fea81275e75f32e12d8b4481fe 100644 --- a/templates/minimal/node/src/cli.rs +++ b/templates/minimal/node/src/cli.rs @@ -21,6 +21,7 @@ use polkadot_sdk::{sc_cli::RunCmd, *}; pub enum Consensus { ManualSeal(u64), InstantSeal, + None, } impl std::str::FromStr for Consensus { @@ -31,6 +32,8 @@ impl std::str::FromStr for Consensus { Consensus::InstantSeal } else if let Some(block_time) = s.strip_prefix("manual-seal-") { Consensus::ManualSeal(block_time.parse().map_err(|_| "invalid block time")?) + } else if s.to_lowercase() == "none" { + Consensus::None } else { return Err("incorrect consensus identifier".into()); }) diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index 169993e2e93ae0eea7ca8ca38aef7a5690e2d082..b4e6fc0b728b60c8930eded042fdc6c03e3be1e8 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -21,9 +21,7 @@ use minimal_template_runtime::{interface::OpaqueBlock as Block, RuntimeApi}; use polkadot_sdk::{ sc_client_api::backend::Backend, sc_executor::WasmExecutor, - sc_service::{ - build_polkadot_syncing_strategy, error::Error as ServiceError, Configuration, TaskManager, - }, + sc_service::{error::Error as ServiceError, Configuration, TaskManager}, sc_telemetry::{Telemetry, TelemetryWorker}, sc_transaction_pool_api::OffchainTransactionPoolFactory, sp_runtime::traits::Block as BlockT, @@ -124,7 +122,7 @@ pub fn new_full::Ha other: mut telemetry, } = new_partial(&config)?; - let mut net_config = sc_network::config::FullNetworkConfiguration::< + let net_config = sc_network::config::FullNetworkConfiguration::< Block, ::Hash, Network, @@ -136,26 +134,16 @@ pub fn new_full::Ha config.prometheus_config.as_ref().map(|cfg| &cfg.registry), ); - let syncing_strategy = build_polkadot_syncing_strategy( - config.protocol_id(), - config.chain_spec.fork_id(), - &mut net_config, - None, - client.clone(), - &task_manager.spawn_handle(), - config.prometheus_config.as_ref().map(|config| &config.registry), - )?; - let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, + net_config, client: client.clone(), transaction_pool: transaction_pool.clone(), spawn_handle: task_manager.spawn_handle(), import_queue, - net_config, block_announce_validator_builder: None, - syncing_strategy, + warp_sync_config: None, block_relay: None, metrics, })?; @@ -273,6 +261,7 @@ pub fn new_full::Ha authorship_future, ); }, + _ => {}, } network_starter.start_network(); diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index 304e50af25088fe93905c25f4d156747277739d1..7b8449f2abe482fe7d77e709ef647bf22e59af5d 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -51,23 +51,20 @@ pub mod genesis_config_presets { /// Returns a development genesis config preset. pub fn development_config_genesis() -> Value { let endowment = >::get().max(1) * 1000; - let config = RuntimeGenesisConfig { + frame_support::build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: AccountKeyring::iter() .map(|a| (a.to_account_id(), endowment)) .collect::>(), }, sudo: SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + }) } /// Get the set of the available genesis config presets. pub fn get_preset(id: &PresetId) -> Option> { - let patch = match id.try_into() { - Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), + let patch = match id.as_ref() { + sp_genesis_builder::DEV_RUNTIME_PRESET => development_config_genesis(), _ => return None, }; Some( @@ -86,8 +83,8 @@ pub mod genesis_config_presets { /// The runtime version. #[runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("minimal-template-runtime"), - impl_name: create_runtime_str!("minimal-template-runtime"), + spec_name: alloc::borrow::Cow::Borrowed("minimal-template-runtime"), + impl_name: alloc::borrow::Cow::Borrowed("minimal-template-runtime"), authoring_version: 1, spec_version: 0, impl_version: 1, diff --git a/templates/minimal/zombienet-omni-node.toml b/templates/minimal/zombienet-omni-node.toml new file mode 100644 index 0000000000000000000000000000000000000000..33b0fceba68c9257a6f5094f252e1830d0fc15e5 --- /dev/null +++ b/templates/minimal/zombienet-omni-node.toml @@ -0,0 +1,9 @@ +[relaychain] +default_command = "polkadot-omni-node" +chain = "dev" +chain_spec_path = "" +default_args = ["--dev-block-time 3000"] + +[[relaychain.nodes]] +name = "alice" +ws_port = 9944 diff --git a/templates/minimal/zombienet.toml b/templates/minimal/zombienet.toml new file mode 100644 index 0000000000000000000000000000000000000000..89df054bf6526ba9e535cef297f9938e613d2f05 --- /dev/null +++ b/templates/minimal/zombienet.toml @@ -0,0 +1,30 @@ +# The setup bellow allows only one node to produce +# blocks and the rest will follow. + +[relaychain] +chain = "dev" +default_command = "minimal-template-node" + +[[relaychain.nodes]] +name = "alice" +args = ["--consensus manual-seal-3000"] +validator = true +ws_port = 9944 + +[[relaychain.nodes]] +name = "bob" +args = ["--consensus None"] +validator = true +ws_port = 9955 + +[[relaychain.nodes]] +name = "charlie" +args = ["--consensus None"] +validator = true +ws_port = 9966 + +[[relaychain.nodes]] +name = "dave" +args = ["--consensus None"] +validator = true +ws_port = 9977 diff --git a/templates/parachain/Dockerfile b/templates/parachain/Dockerfile index 72a8f19fe79ae0ff870f19e90206c9aa7a4309fb..da1353d5fb9c84fec84a3d020a40b7bd17a1c381 100644 --- a/templates/parachain/Dockerfile +++ b/templates/parachain/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /polkadot COPY . /polkadot RUN cargo fetch -RUN cargo build --locked --release +RUN cargo build --workspace --locked --release FROM docker.io/parity/base-bin:latest diff --git a/templates/parachain/README.md b/templates/parachain/README.md index 3de85cbeb4dc77a386fc702f645c0bb4e7ffb96d..65a6979041f2b230cea1afc7017a3050fa3fdd1b 100644 --- a/templates/parachain/README.md +++ b/templates/parachain/README.md @@ -11,32 +11,55 @@ -* โซ This template provides a starting point to build a [parachain](https://wiki.polkadot.network/docs/learn-parachains). +## Table of Contents -* โ˜๏ธ It is based on the +- [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) + +- [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 +- ๐Ÿ”ง 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) +- ๐Ÿ‘‰ 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: -* ๐Ÿ’ฟ a [Node](./node/README.md) - the binary application. -* ๐Ÿงฎ the [Runtime](./runtime/README.md) - the core logic of the parachain. -* ๐ŸŽจ the [Pallets](./pallets/README.md) - from which the runtime is constructed. +- ๐Ÿงฎ 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. +- ๐Ÿฆ€ The template is using the Rust language. -* ๐Ÿ‘‰ Check the +- ๐Ÿ‘‰ 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 +- ๐Ÿ› ๏ธ 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: @@ -47,90 +70,149 @@ git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git para cd parachain-template ``` -### Build +## Starting a Development Chain + +### Omni Node Prerequisites -๐Ÿ”จ Use the following command to build the node without launching it: +[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 ``` -๐Ÿณ Alternatively, build the docker image: +#### 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 -docker build . -t polkadot-sdk-parachain-template +chain-spec-builder create --relay-chain "rococo-local" --para-id 1000 --runtime \ + target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm named-preset development ``` -### Local Development Chain +**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 +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. -๐ŸงŸ This project uses [Zombienet](https://github.com/paritytech/zombienet) to orchestrate the relaychain and parachain nodes. -You can grab a [released binary](https://github.com/paritytech/zombienet/releases/latest) or use an [npm version](https://www.npmjs.com/package/@zombienet/cli). +#### Run Omni Node -This template produces a parachain node. -You can install it in your environment by running: +Start Omni Node with the generated chain spec. We'll start it development mode (without a relay chain config), +with a temporary directory for configuration (given `--tmp`), and block production set to create a block with +every second. + +```bash +polkadot-omni-node --chain --tmp --dev-block-time 1000 -```sh -cargo install --path node ``` -You still need a relaychain node - you can 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/latest). +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. -In addition to the installed parachain node, make sure to bring -`zombienet`, `polkadot`, `polkadot-prepare-worker`, and `polkadot-execute-worker` -into `PATH`, for example: +### 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" +export PATH="$PATH:" ``` -This way, we can conveniently use them in the following steps. +#### Update `zombienet-omni-node.toml` with a valid chain spec path + +```toml +# ... +[[parachains]] +id = 1000 +chain_spec_path = "" +# ... +``` -๐Ÿ‘ฅ The following command starts a local development chain, with a single relay chain node and a single parachain collator: +#### Start the network ```sh -zombienet --provider native spawn ./zombienet.toml +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` -# Alternatively, the npm version: -npx --yes @zombienet/cli --provider native spawn ./zombienet.toml +```sh +cargo install --path node ``` -Development chains: +#### 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). -* ๐Ÿงน 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. +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 +- ๐ŸŒ 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 +- ๐Ÿช 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 +- ๐Ÿง‘โ€๐Ÿ”ง 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. + ## Contributing -* ๐Ÿ”„ This template is automatically updated after releases in the main [Polkadot SDK monorepo](https://github.com/paritytech/polkadot-sdk). +- ๐Ÿ”„ 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). +- โžก๏ธ 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 +- ๐Ÿ˜‡ 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. +- ๐Ÿง‘โ€๐Ÿซ 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 +- ๐Ÿง‘โ€๐Ÿ”ง 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 +- ๐Ÿ‘ฅ Additionally, there are [GitHub issues](https://github.com/paritytech/polkadot-sdk/issues) and [Substrate StackExchange](https://substrate.stackexchange.com/). diff --git a/templates/parachain/runtime/src/apis.rs b/templates/parachain/runtime/src/apis.rs index eba9293a67ba1aa46b7697f7e03d31cca681fdca..05a508ca655fb1fe6be4db53a839b76c8a9c1cec 100644 --- a/templates/parachain/runtime/src/apis.rs +++ b/templates/parachain/runtime/src/apis.rs @@ -261,7 +261,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{BenchmarkError, Benchmarking, BenchmarkBatch}; use super::*; diff --git a/templates/parachain/runtime/src/genesis_config_presets.rs b/templates/parachain/runtime/src/genesis_config_presets.rs index 394bde0be77000d7d06675401eff5ec06a97c36f..aa1ff7895eb82e8a4d8ad20a0a77dca59e00ff1e 100644 --- a/templates/parachain/runtime/src/genesis_config_presets.rs +++ b/templates/parachain/runtime/src/genesis_config_presets.rs @@ -8,6 +8,7 @@ use alloc::{vec, vec::Vec}; use polkadot_sdk::{staging_xcm as xcm, *}; use cumulus_primitives_core::ParaId; +use frame_support::build_struct_json_patch; use parachains_common::AuraId; use serde_json::Value; use sp_genesis_builder::PresetId; @@ -15,6 +16,8 @@ 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 gensis config presets of parachain template. +const PARACHAIN_ID: u32 = 1000; /// Generate the session keys from individual elements. /// @@ -29,7 +32,7 @@ fn testnet_genesis( root: AccountId, id: ParaId, ) -> Value { - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts .iter() @@ -37,11 +40,10 @@ fn testnet_genesis( .map(|k| (k, 1u128 << 60)) .collect::>(), }, - parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + parachain_info: ParachainInfoConfig { parachain_id: id }, collator_selection: CollatorSelectionConfig { invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), candidacy_bond: EXISTENTIAL_DEPOSIT * 16, - ..Default::default() }, session: SessionConfig { keys: invulnerables @@ -54,17 +56,10 @@ fn testnet_genesis( ) }) .collect::>(), - ..Default::default() - }, - polkadot_xcm: PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() }, + polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, sudo: SudoConfig { key: Some(root) }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + }) } fn local_testnet_genesis() -> Value { @@ -76,9 +71,7 @@ fn local_testnet_genesis() -> Value { ], Sr25519Keyring::well_known().map(|k| k.to_account_id()).collect(), Sr25519Keyring::Alice.to_account_id(), - // TODO: this is super opaque, how should one know they should configure this? add to - // README! - 1000.into(), + PARACHAIN_ID.into(), ) } @@ -91,15 +84,15 @@ fn development_config_genesis() -> Value { ], Sr25519Keyring::well_known().map(|k| k.to_account_id()).collect(), Sr25519Keyring::Alice.to_account_id(), - 1000.into(), + PARACHAIN_ID.into(), ) } /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &PresetId) -> Option> { - let patch = match id.try_into() { - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => local_testnet_genesis(), - Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), + let patch = match id.as_ref() { + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => local_testnet_genesis(), + sp_genesis_builder::DEV_RUNTIME_PRESET => development_config_genesis(), _ => return None, }; Some( diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs index 78dc38ef427f7cebea51a81394f876a4dff2444b..43e76dba0591c5284358b18151bedc580d82d7b3 100644 --- a/templates/parachain/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -20,7 +20,7 @@ use smallvec::smallvec; use polkadot_sdk::{staging_parachain_info as parachain_info, *}; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{BlakeTwo256, IdentifyAccount, Verify}, MultiSignature, }; @@ -164,8 +164,8 @@ impl_opaque_keys! { #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("parachain-template-runtime"), - impl_name: create_runtime_str!("parachain-template-runtime"), + spec_name: alloc::borrow::Cow::Borrowed("parachain-template-runtime"), + impl_name: alloc::borrow::Cow::Borrowed("parachain-template-runtime"), authoring_version: 1, spec_version: 1, impl_version: 0, diff --git a/templates/parachain/zombienet-omni-node.toml b/templates/parachain/zombienet-omni-node.toml new file mode 100644 index 0000000000000000000000000000000000000000..29e99cfcd493113c3ee643c3cffb08e2fce498a9 --- /dev/null +++ b/templates/parachain/zombienet-omni-node.toml @@ -0,0 +1,22 @@ +[relaychain] +default_command = "polkadot" +chain = "rococo-local" + +[[relaychain.nodes]] +name = "alice" +validator = true +ws_port = 9944 + +[[relaychain.nodes]] +name = "bob" +validator = true +ws_port = 9955 + +[[parachains]] +id = 1000 +chain_spec_path = "" + +[parachains.collator] +name = "charlie" +ws_port = 9988 +command = "polkadot-omni-node" diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml index 8a3c7d0ac78002590e64d06e72460c7afa6b2270..4c0ab31df95e2b95b0fd1d9fb548c98cf6862d90 100644 --- a/templates/solochain/node/Cargo.toml +++ b/templates/solochain/node/Cargo.toml @@ -30,9 +30,9 @@ sc-telemetry = { workspace = true, default-features = true } sc-transaction-pool = { workspace = true, default-features = true } sc-transaction-pool-api = { workspace = true, default-features = true } sc-offchain = { workspace = true, default-features = true } +sc-consensus = { workspace = true, default-features = true } sc-consensus-aura = { workspace = true, default-features = true } sp-consensus-aura = { workspace = true, default-features = true } -sc-consensus = { workspace = true, default-features = true } sc-consensus-grandpa = { workspace = true, default-features = true } sp-consensus-grandpa = { workspace = true, default-features = true } sp-genesis-builder = { workspace = true, default-features = true } diff --git a/templates/solochain/node/src/service.rs b/templates/solochain/node/src/service.rs index 2524906fd508f54630c51f7bf9b74f6be25d47e6..d6fcebe239f7e7a95e065ab266a3c6149b0c9c5b 100644 --- a/templates/solochain/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -4,10 +4,7 @@ use futures::FutureExt; use sc_client_api::{Backend, BlockBackend}; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; use sc_consensus_grandpa::SharedVoterState; -use sc_service::{ - build_polkadot_syncing_strategy, error::Error as ServiceError, Configuration, TaskManager, - WarpSyncConfig, -}; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncConfig}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use solochain_template_runtime::{self, apis::RuntimeApi, opaque::Block}; @@ -172,16 +169,6 @@ pub fn new_full< Vec::default(), )); - let syncing_strategy = build_polkadot_syncing_strategy( - config.protocol_id(), - config.chain_spec.fork_id(), - &mut net_config, - Some(WarpSyncConfig::WithProvider(warp_sync)), - client.clone(), - &task_manager.spawn_handle(), - config.prometheus_config.as_ref().map(|config| &config.registry), - )?; - let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -191,7 +178,7 @@ pub fn new_full< spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - syncing_strategy, + warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), block_relay: None, metrics, })?; diff --git a/templates/solochain/runtime/src/apis.rs b/templates/solochain/runtime/src/apis.rs index f21eaa3444316af31564c05a19133b35b35e83e8..06c645fa0c53959b209fa4bcfe867eb6c34a5a6d 100644 --- a/templates/solochain/runtime/src/apis.rs +++ b/templates/solochain/runtime/src/apis.rs @@ -237,7 +237,7 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, sp_runtime::RuntimeString> { + ) -> Result, alloc::string::String> { use frame_benchmarking::{baseline, Benchmarking, BenchmarkBatch}; use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; diff --git a/templates/solochain/runtime/src/genesis_config_presets.rs b/templates/solochain/runtime/src/genesis_config_presets.rs index 693ae5c2221f268ce9d04703ae9e6e140d7c4c50..049f4593451b951e55ba2106a5ddc85a88437a64 100644 --- a/templates/solochain/runtime/src/genesis_config_presets.rs +++ b/templates/solochain/runtime/src/genesis_config_presets.rs @@ -17,6 +17,7 @@ use crate::{AccountId, BalancesConfig, RuntimeGenesisConfig, SudoConfig}; use alloc::{vec, vec::Vec}; +use frame_support::build_struct_json_patch; use serde_json::Value; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_grandpa::AuthorityId as GrandpaId; @@ -29,7 +30,7 @@ fn testnet_genesis( endowed_accounts: Vec, root: AccountId, ) -> Value { - let config = RuntimeGenesisConfig { + build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { balances: endowed_accounts .iter() @@ -42,13 +43,9 @@ fn testnet_genesis( }, grandpa: pallet_grandpa::GenesisConfig { authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect::>(), - ..Default::default() }, sudo: SudoConfig { key: Some(root) }, - ..Default::default() - }; - - serde_json::to_value(config).expect("Could not build genesis config.") + }) } /// Return the development genesis config. @@ -91,9 +88,9 @@ pub fn local_config_genesis() -> Value { /// Provides the JSON representation of predefined genesis config for given `id`. pub fn get_preset(id: &PresetId) -> Option> { - let patch = match id.try_into() { - Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), - Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => local_config_genesis(), + let patch = match id.as_ref() { + sp_genesis_builder::DEV_RUNTIME_PRESET => development_config_genesis(), + sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => local_config_genesis(), _ => return None, }; Some( diff --git a/templates/solochain/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs index f2eb49592be958b7ad54df023ed25e072c9c98c7..ae0ea16ae42ef6122d1472677f5a23fe769e465d 100644 --- a/templates/solochain/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -11,7 +11,7 @@ pub mod configs; extern crate alloc; use alloc::vec::Vec; use sp_runtime::{ - create_runtime_str, generic, impl_opaque_keys, + generic, impl_opaque_keys, traits::{BlakeTwo256, IdentifyAccount, Verify}, MultiAddress, MultiSignature, }; @@ -61,8 +61,8 @@ impl_opaque_keys! { // https://docs.substrate.io/main-docs/build/upgrade#runtime-versioning #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("solochain-template-runtime"), - impl_name: create_runtime_str!("solochain-template-runtime"), + spec_name: alloc::borrow::Cow::Borrowed("solochain-template-runtime"), + impl_name: alloc::borrow::Cow::Borrowed("solochain-template-runtime"), authoring_version: 1, // The version of the runtime specification. A full node will not attempt to use its native // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, diff --git a/templates/zombienet/tests/smoke.rs b/templates/zombienet/tests/smoke.rs index ba5f42142f31e2fa60fc4baa2b85f7737e60752e..c0c9646d4e9c978e82158446c19f90a6dd1f49e3 100644 --- a/templates/zombienet/tests/smoke.rs +++ b/templates/zombienet/tests/smoke.rs @@ -7,28 +7,74 @@ //! `cargo build --package minimal-template-node --release` //! `export PATH=/target/release:$PATH //! -//! The you can run the test with -//! `cargo test -p template-zombienet-tests` +//! There are also some tests related to omni node which run basaed on pre-generated chain specs, +//! so to be able to run them you would need to generate the right chain spec (just minimal and +//! parachain tests supported for now). +//! +//! You can run the following command to generate a minimal chainspec, once the runtime wasm file is +//! compiled: +//!`chain-spec-builder create --relay-chain --para-id 1000 -r \ +//! named-preset development` +//! +//! Once the files are generated, you must export an environment variable called +//! `CHAIN_SPECS_DIR` which should point to the absolute path of the directory +//! that holds the generated chain specs. The chain specs file names should be +//! `minimal_chain_spec.json` for minimal and `parachain_chain_spec.json` for parachain +//! templates. +//! +//! To start all tests here we should run: +//! `cargo test -p template-zombienet-tests --features zombienet` #[cfg(feature = "zombienet")] mod smoke { + use std::path::PathBuf; + use anyhow::anyhow; use zombienet_sdk::{NetworkConfig, NetworkConfigBuilder, NetworkConfigExt}; - pub fn get_config(cmd: &str, para_cmd: Option<&str>) -> Result { - let chain = if cmd == "polkadot" { "rococo-local" } else { "dev" }; + const CHAIN_SPECS_DIR_PATH: &str = "CHAIN_SPECS_DIR"; + const PARACHAIN_ID: u32 = 1000; + + #[inline] + fn expect_env_var(var_name: &str) -> String { + std::env::var(var_name) + .unwrap_or_else(|_| panic!("{CHAIN_SPECS_DIR_PATH} environment variable is set. qed.")) + } + + #[derive(Default)] + struct NetworkSpec { + relaychain_cmd: &'static str, + relaychain_spec_path: Option, + // TODO: update the type to something like Option> after + // `zombienet-sdk` exposes `shared::types::Arg`. + relaychain_cmd_args: Option>, + para_cmd: Option<&'static str>, + para_cmd_args: Option>, + } + + fn get_config(network_spec: NetworkSpec) -> Result { + let chain = if network_spec.relaychain_cmd == "polkadot" { "rococo-local" } else { "dev" }; let config = NetworkConfigBuilder::new().with_relaychain(|r| { - r.with_chain(chain) - .with_default_command(cmd) - .with_node(|node| node.with_name("alice")) + let mut r = r.with_chain(chain).with_default_command(network_spec.relaychain_cmd); + if let Some(path) = network_spec.relaychain_spec_path { + r = r.with_chain_spec_path(path); + } + + if let Some(args) = network_spec.relaychain_cmd_args { + r = r.with_default_args(args.into_iter().map(|arg| arg.into()).collect()); + } + + r.with_node(|node| node.with_name("alice")) .with_node(|node| node.with_name("bob")) }); - let config = if let Some(para_cmd) = para_cmd { + let config = if let Some(para_cmd) = network_spec.para_cmd { config.with_parachain(|p| { - p.with_id(1000) - .with_default_command(para_cmd) - .with_collator(|n| n.with_name("collator")) + let mut p = p.with_id(PARACHAIN_ID).with_default_command(para_cmd); + if let Some(args) = network_spec.para_cmd_args { + p = p.with_default_args(args.into_iter().map(|arg| arg.into()).collect()); + } + p.with_collator(|n| n.with_name("collator")) }) } else { config @@ -46,14 +92,18 @@ mod smoke { env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), ); - let config = get_config("polkadot", Some("parachain-template-node"))?; + let config = get_config(NetworkSpec { + relaychain_cmd: "polkadot", + para_cmd: Some("parachain-template-node"), + ..Default::default() + })?; let network = config.spawn_native().await?; // wait 6 blocks of the para let collator = network.get_node("collator")?; assert!(collator - .wait_metric("block_height{status=\"best\"}", |b| b > 5_f64) + .wait_metric("block_height{status=\"finalized\"}", |b| b > 5_f64) .await .is_ok()); @@ -66,13 +116,19 @@ mod smoke { env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), ); - let config = get_config("solochain-template-node", None)?; + let config = get_config(NetworkSpec { + relaychain_cmd: "solochain-template-node", + ..Default::default() + })?; let network = config.spawn_native().await?; // wait 6 blocks let alice = network.get_node("alice")?; - assert!(alice.wait_metric("block_height{status=\"best\"}", |b| b > 5_f64).await.is_ok()); + assert!(alice + .wait_metric("block_height{status=\"finalized\"}", |b| b > 5_f64) + .await + .is_ok()); Ok(()) } @@ -83,13 +139,73 @@ mod smoke { env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), ); - let config = get_config("minimal-template-node", None)?; + let config = get_config(NetworkSpec { + relaychain_cmd: "minimal-template-node", + ..Default::default() + })?; + + let network = config.spawn_native().await?; + + // wait 6 blocks + let alice = network.get_node("alice")?; + assert!(alice + .wait_metric("block_height{status=\"finalized\"}", |b| b > 5_f64) + .await + .is_ok()); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn omni_node_with_minimal_runtime_block_production_test() -> Result<(), anyhow::Error> { + let _ = env_logger::try_init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + let chain_spec_path = expect_env_var(CHAIN_SPECS_DIR_PATH) + "/minimal_chain_spec.json"; + let config = get_config(NetworkSpec { + relaychain_cmd: "polkadot-omni-node", + relaychain_cmd_args: Some(vec![("--dev-block-time", "1000")]), + relaychain_spec_path: Some(chain_spec_path.into()), + ..Default::default() + })?; let network = config.spawn_native().await?; // wait 6 blocks let alice = network.get_node("alice")?; - assert!(alice.wait_metric("block_height{status=\"best\"}", |b| b > 5_f64).await.is_ok()); + assert!(alice + .wait_metric("block_height{status=\"finalized\"}", |b| b > 5_f64) + .await + .is_ok()); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn omni_node_with_parachain_runtime_block_production_test() -> Result<(), anyhow::Error> { + let _ = env_logger::try_init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + + let chain_spec_path = expect_env_var(CHAIN_SPECS_DIR_PATH) + "/parachain_chain_spec.json"; + + let config = get_config(NetworkSpec { + relaychain_cmd: "polkadot", + para_cmd: Some("polkadot-omni-node"), + // Leaking the `String` to be able to use it below as a static str, + // required by the `FromStr` implementation for zombienet-configuration + // `Arg` type, which is not exposed yet through `zombienet-sdk`. + para_cmd_args: Some(vec![("--chain", chain_spec_path.leak())]), + ..Default::default() + })?; + let network = config.spawn_native().await?; + + // wait 6 blocks + let alice = network.get_node("collator")?; + assert!(alice + .wait_metric("block_height{status=\"finalized\"}", |b| b > 5_f64) + .await + .is_ok()); Ok(()) }