From 1cc760b26efafd6d89640cb07e59ee32c8fe64c5 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Thu, 17 Oct 2024 18:23:28 +0200 Subject: [PATCH 01/50] [Release/CI] Adjust release pipelines to support new tags and delete rococo from release (#6108) This PR introduces adjustments to the release pipelines so that it could be used with the new tags in the form of `poilakdot-stableYYMM(-rcX)` in addition to the old form of tags like `vX.XX.X`. Also, the rococo runtime and its parachains are deleted now from the build and release draft as it is decommissioned and not used any more --- .github/scripts/common/lib.sh | 10 +++------ .../workflows/release-10_rc-automation.yml | 4 ++-- .../release-30_publish_release_draft.yml | 14 +++++------- .../workflows/release-50_publish-docker.yml | 6 ++--- .../workflows/release-branchoff-stable.yml | 13 +++++------ .../workflows/release-reusable-rc-buid.yml | 2 +- .github/workflows/release-srtool.yml | 10 ++------- scripts/release/build-changelogs.sh | 22 ++----------------- scripts/release/templates/changelog.md.tera | 2 +- 9 files changed, 25 insertions(+), 58 deletions(-) diff --git a/.github/scripts/common/lib.sh b/.github/scripts/common/lib.sh index d2a4baf12fa..56d0371d678 100755 --- a/.github/scripts/common/lib.sh +++ b/.github/scripts/common/lib.sh @@ -405,14 +405,10 @@ function find_runtimes() { # output: none filter_version_from_input() { version=$1 - regex="(^v[0-9]+\.[0-9]+\.[0-9]+)$|(^v[0-9]+\.[0-9]+\.[0-9]+-rc[0-9]+)$" + regex="^(v)?[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?$" if [[ $version =~ $regex ]]; then - if [ -n "${BASH_REMATCH[1]}" ]; then - echo "${BASH_REMATCH[1]}" - elif [ -n "${BASH_REMATCH[2]}" ]; then - echo "${BASH_REMATCH[2]}" - fi + echo $version else echo "Invalid version: $version" exit 1 @@ -462,7 +458,7 @@ function get_polkadot_node_version_from_code() { validate_stable_tag() { tag="$1" - pattern="^stable[0-9]{4}(-[0-9]+)?(-rc[0-9]+)?$" + pattern="^(polkadot-)?stable[0-9]{4}(-[0-9]+)?(-rc[0-9]+)?$" if [[ $tag =~ $pattern ]]; then echo $tag diff --git a/.github/workflows/release-10_rc-automation.yml b/.github/workflows/release-10_rc-automation.yml index 195c14dbd5a..41783f6cc72 100644 --- a/.github/workflows/release-10_rc-automation.yml +++ b/.github/workflows/release-10_rc-automation.yml @@ -12,7 +12,7 @@ on: workflow_dispatch: inputs: version: - description: Current release/rc version in format vX.X.X + description: Current release/rc version in format polkadot-stableYYMM jobs: tag_rc: @@ -41,7 +41,7 @@ jobs: if [[ -z "${{ inputs.version }}" ]]; then version=v$(get_polkadot_node_version_from_code) else - version=$(filter_version_from_input ${{ inputs.version }}) + version=$(validate_stable_tag ${{ inputs.version }}) fi echo "$version" echo "version=$version" >> $GITHUB_OUTPUT diff --git a/.github/workflows/release-30_publish_release_draft.yml b/.github/workflows/release-30_publish_release_draft.yml index dd6a111d67e..2a5d92ed167 100644 --- a/.github/workflows/release-30_publish_release_draft.yml +++ b/.github/workflows/release-30_publish_release_draft.yml @@ -5,6 +5,7 @@ on: tags: # Catches v1.2.3 and v1.2.3-rc1 - v[0-9]+.[0-9]+.[0-9]+* + # - polkadot-stable[0-9]+* Activate when the release process from release org is setteled workflow_dispatch: inputs: @@ -25,7 +26,7 @@ jobs: build-runtimes: uses: "./.github/workflows/release-srtool.yml" with: - excluded_runtimes: "substrate-test bp cumulus-test kitchensink minimal-template parachain-template penpal polkadot-test seedling shell frame-try sp solochain-template" + excluded_runtimes: "asset-hub-rococo bridge-hub-rococo contracts-rococo coretime-rococo people-rococo rococo rococo-parachain substrate-test bp cumulus-test kitchensink minimal-template parachain-template penpal polkadot-test seedling shell frame-try sp solochain-template" build_opts: "--features on-chain-release-build" build-binaries: @@ -79,30 +80,27 @@ jobs: env: RUSTC_STABLE: ${{ needs.get-rust-versions.outputs.rustc-stable }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ASSET_HUB_ROCOCO_DIGEST: ${{ github.workspace}}/asset-hub-rococo-runtime/asset-hub-rococo-srtool-digest.json ASSET_HUB_WESTEND_DIGEST: ${{ github.workspace}}/asset-hub-westend-runtime/asset-hub-westend-srtool-digest.json - BRIDGE_HUB_ROCOCO_DIGEST: ${{ github.workspace}}/bridge-hub-rococo-runtime/bridge-hub-rococo-srtool-digest.json BRIDGE_HUB_WESTEND_DIGEST: ${{ github.workspace}}/bridge-hub-westend-runtime/bridge-hub-westend-srtool-digest.json COLLECTIVES_WESTEND_DIGEST: ${{ github.workspace}}/collectives-westend-runtime/collectives-westend-srtool-digest.json - CONTRACTS_ROCOCO_DIGEST: ${{ github.workspace}}/contracts-rococo-runtime/contracts-rococo-srtool-digest.json - CORETIME_ROCOCO_DIGEST: ${{ github.workspace}}/coretime-rococo-runtime/coretime-rococo-srtool-digest.json CORETIME_WESTEND_DIGEST: ${{ github.workspace}}/coretime-westend-runtime/coretime-westend-srtool-digest.json GLUTTON_WESTEND_DIGEST: ${{ github.workspace}}/glutton-westend-runtime/glutton-westend-srtool-digest.json - PEOPLE_ROCOCO_DIGEST: ${{ github.workspace}}/people-rococo-runtime/people-rococo-srtool-digest.json PEOPLE_WESTEND_DIGEST: ${{ github.workspace}}/people-westend-runtime/people-westend-srtool-digest.json - ROCOCO_DIGEST: ${{ github.workspace}}/rococo-runtime/rococo-srtool-digest.json WESTEND_DIGEST: ${{ github.workspace}}/westend-runtime/westend-srtool-digest.json + shell: bash run: | . ./.github/scripts/common/lib.sh export REF1=$(get_latest_release_tag) if [[ -z "${{ inputs.version }}" ]]; then export REF2="${{ github.ref_name }}" + echo "REF2: ${REF2}" else export REF2="${{ inputs.version }}" + echo "REF2: ${REF2}" fi echo "REL_TAG=$REF2" >> $GITHUB_ENV - export VERSION=$(echo "$REF2" | sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*$/\1/') + export VERSION=$(echo "$REF2" | sed -E 's/.*(stable[0-9]+).*$/\1/') ./scripts/release/build-changelogs.sh diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index dacfcc5995b..1fe68441a62 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -194,14 +194,12 @@ jobs: run: | . ./.github/scripts/common/lib.sh - release="${{ needs.validate-inputs.outputs.stable_tag }}" && \ - echo "release=${release}" >> $GITHUB_OUTPUT + echo "release=${{ needs.validate-inputs.outputs.stable_tag }}" >> $GITHUB_OUTPUT commit=$(git rev-parse --short HEAD) && \ echo "commit=${commit}" >> $GITHUB_OUTPUT - tag="${{ needs.validate-inputs.outputs.version }}" && \ - echo "tag=${tag}" >> $GITHUB_OUTPUT + echo "tag=${{ needs.validate-inputs.outputs.version }}" >> $GITHUB_OUTPUT - name: Fetch release tags working-directory: release-artifacts diff --git a/.github/workflows/release-branchoff-stable.yml b/.github/workflows/release-branchoff-stable.yml index c4c50f5398e..27a3fdc14ee 100644 --- a/.github/workflows/release-branchoff-stable.yml +++ b/.github/workflows/release-branchoff-stable.yml @@ -9,16 +9,17 @@ on: type: string node_version: - description: Version of the polkadot node in the format vX.XX.X (e.g. 1.15.0) + description: Version of the polkadot node in the format X.XX.X (e.g. 1.15.0) required: true jobs: - # TODO: Activate this job when the pipeline is moved to the fork in the `paritytech-release` org - # check-workflow-can-run: - # uses: paritytech-release/sync-workflows/.github/workflows/check-syncronization.yml@latest + 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 }} @@ -40,9 +41,7 @@ jobs: echo "stable_version=${stable_version}" >> $GITHUB_OUTPUT create-stable-branch: - # needs: [check-workflow-can-run, prepare-tooling] needs: [prepare-tooling] - # if: needs. check-workflow-can-run.outputs.checks_passed == 'true' runs-on: ubuntu-latest env: @@ -100,6 +99,6 @@ jobs: # Set new version for polkadot-parachain binary to match the polkadot node binary # set_polkadot_parachain_binary_version $NODE_VERSION "cumulus/polkadot-parachain/Cargo.toml" - reorder_prdocs $NODE_VERSION + reorder_prdocs $STABLE_BRANCH_NAME git push origin "$STABLE_BRANCH_NAME" diff --git a/.github/workflows/release-reusable-rc-buid.yml b/.github/workflows/release-reusable-rc-buid.yml index 2aee9dc995b..ae6c430b6d3 100644 --- a/.github/workflows/release-reusable-rc-buid.yml +++ b/.github/workflows/release-reusable-rc-buid.yml @@ -183,7 +183,7 @@ jobs: needs: [build-rc] uses: ./.github/workflows/release-reusable-s3-upload.yml with: - package: ${{ inputs.binary }} + package: polkadot-parachain release_tag: ${{ inputs.release_tag }} secrets: AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} diff --git a/.github/workflows/release-srtool.yml b/.github/workflows/release-srtool.yml index 83119dd4ed2..9a29b46d2fc 100644 --- a/.github/workflows/release-srtool.yml +++ b/.github/workflows/release-srtool.yml @@ -39,7 +39,8 @@ jobs: sudo dpkg -i toml.deb toml --version; jq --version - - name: Scan runtimes + - name: Scan and get runtimes list + id: get_runtimes_list env: EXCLUDED_RUNTIMES: ${{ inputs.excluded_runtimes }}:"substrate-test" run: | @@ -51,13 +52,6 @@ jobs: MATRIX=$(find_runtimes | tee runtimes_list.json) echo $MATRIX - - - name: Get runtimes list - id: get_runtimes_list - run: | - ls -al - MATRIX=$(cat runtimes_list.json) - echo $MATRIX echo "runtime=$MATRIX" >> $GITHUB_OUTPUT srtool: diff --git a/scripts/release/build-changelogs.sh b/scripts/release/build-changelogs.sh index d73f06c8cd6..d1bbe136ad4 100755 --- a/scripts/release/build-changelogs.sh +++ b/scripts/release/build-changelogs.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash export PRODUCT=polkadot -export VERSION=${VERSION:-1.5.0} +export VERSION=${VERSION:-stable2409} export ENGINE=${ENGINE:-podman} export REF1=${REF1:-'HEAD'} export REF2=${REF2} @@ -66,33 +66,21 @@ echo "Changelog ready in $OUTPUT/relnote_commits.md" # Show the files tree -s -h -c $OUTPUT/ -ASSET_HUB_ROCOCO_DIGEST=${ASSET_HUB_ROCOCO_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/asset-hub-rococo-srtool-digest.json"} ASSET_HUB_WESTEND_DIGEST=${ASSET_HUB_WESTEND_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/asset-hub-westend-srtool-digest.json"} -BRIDGE_HUB_ROCOCO_DIGEST=${BRIDGE_HUB_ROCOCO_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/bridge-hub-rococo-srtool-digest.json"} BRIDGE_HUB_WESTEND_DIGEST=${BRIDGE_HUB_WESTEND_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/bridge-hub-westend-srtool-digest.json"} COLLECTIVES_WESTEND_DIGEST=${COLLECTIVES_WESTEND_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/collectives-westend-srtool-digest.json"} -CONTRACTS_ROCOCO_DIGEST=${CONTRACTS_ROCOCO_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/contracts-rococo-srtool-digest.json"} -CORETIME_ROCOCO_DIGEST=${CORETIME_ROCOCO_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/coretime-rococo-srtool-digest.json"} CORETIME_WESTEND_DIGEST=${CORETIME_WESTEND_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/coretime-westend-srtool-digest.json"} GLUTTON_WESTEND_DIGEST=${GLUTTON_WESTEND_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/glutton-westend-srtool-digest.json"} -PEOPLE_ROCOCO_DIGEST=${PEOPLE_ROCOCO_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/people-rococo-srtool-digest.json"} PEOPLE_WESTEND_DIGEST=${PEOPLE_WESTEND_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/people-westend-srtool-digest.json"} -ROCOCO_DIGEST=${ROCOCO_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/rococo-srtool-digest.json"} WESTEND_DIGEST=${WESTEND_DIGEST:-"$PROJECT_ROOT/scripts/release/digests/westend-srtool-digest.json"} jq \ - --slurpfile srtool_asset_hub_rococo $ASSET_HUB_ROCOCO_DIGEST \ --slurpfile srtool_asset_hub_westend $ASSET_HUB_WESTEND_DIGEST \ - --slurpfile srtool_bridge_hub_rococo $BRIDGE_HUB_ROCOCO_DIGEST \ --slurpfile srtool_bridge_hub_westend $BRIDGE_HUB_WESTEND_DIGEST \ --slurpfile srtool_collectives_westend $COLLECTIVES_WESTEND_DIGEST \ - --slurpfile srtool_contracts_rococo $CONTRACTS_ROCOCO_DIGEST \ - --slurpfile srtool_coretime_rococo $CORETIME_ROCOCO_DIGEST\ --slurpfile srtool_coretime_westend $CORETIME_WESTEND_DIGEST \ --slurpfile srtool_glutton_westend $GLUTTON_WESTEND_DIGEST \ - --slurpfile srtool_people_rococ $PEOPLE_ROCOCO_DIGEST \ --slurpfile srtool_people_westend $PEOPLE_WESTEND_DIGEST \ - --slurpfile srtool_rococo $ROCOCO_DIGEST \ --slurpfile srtool_westend $WESTEND_DIGEST \ -n '{ srtool: [ @@ -102,13 +90,7 @@ jq \ { order: 13, name: "Westend Collectives", data: $srtool_collectives_westend[0] }, { order: 14, name: "Westend Coretime", data: $srtool_coretime_westend[0] }, { order: 15, name: "Westend Glutton", data: $srtool_glutton_westend[0] }, - { order: 16, name: "Westend People", data: $srtool_people_westend[0] }, - { order: 17, name: "Rococo", data: $srtool_rococo[0] }, - { order: 18, name: "Rococo AssetHub", data: $srtool_asset_hub_rococo[0] }, - { order: 19, name: "Rococo BridgeHub", data: $srtool_bridge_hub_rococo[0] }, - { order: 20, name: "Rococo Contracts", data: $srtool_contracts_rococo[0] }, - { order: 21, name: "Rococo Coretime", data: $srtool_coretime_rococo[0] }, - { order: 22, name: "Rococo People", data: $srtool_people_rococ[0] } + { order: 16, name: "Westend People", data: $srtool_people_westend[0] } ] }' > "$PROJECT_ROOT/scripts/release/context.json" RELEASE_DIR="$PROJECT_ROOT/scripts/release/" diff --git a/scripts/release/templates/changelog.md.tera b/scripts/release/templates/changelog.md.tera index aaba761e8e4..8d17451c8d0 100644 --- a/scripts/release/templates/changelog.md.tera +++ b/scripts/release/templates/changelog.md.tera @@ -1,4 +1,4 @@ -## Changelog for `{{ env.PRODUCT | capitalize }} v{{ env.VERSION }}` +## Changelog for `{{ env.PRODUCT | capitalize }} {{ env.VERSION }}` {% for file in prdoc | sort(attribute="doc_filename.number") -%} {%- set author= file.content.author | default(value="n/a") -%} -- GitLab From d23a1bbd08e69015070f8be5e634820ade8dc3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B3nal=20Murray?= Date: Thu, 17 Oct 2024 19:33:44 +0200 Subject: [PATCH 02/50] Fix generate-prdoc for single audiences (#6103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `prdoc check -n NNNN` (used in CI) enforces that yaml lists only be used for lists with length more than one. Previously this script generated a list no matter how many audiences were provided, which generated a broken PRDoc when that number was 1. Reproducing the issue locally: ``` python .github/scripts/generate-prdoc.py --pr 6022 --bump patch --audience runtime_user --force prdoc check -n 6022 ``` Fails on master but passes with this change Co-authored-by: Bastian Köcher --- .github/scripts/generate-prdoc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/scripts/generate-prdoc.py b/.github/scripts/generate-prdoc.py index edcdb82cd22..780fa001297 100644 --- a/.github/scripts/generate-prdoc.py +++ b/.github/scripts/generate-prdoc.py @@ -122,7 +122,7 @@ def setup_parser(parser=None, pr_required=True): return parser def snake_to_title(s): - return ' '.join(word.capitalize() for word in s.split('_')) + return ' '.join(word.capitalize() for word in s.split('_')) def main(args): print(f"Args: {args}, force: {args.force}") @@ -130,6 +130,8 @@ def main(args): try: # Convert snake_case audience arguments to title case mapped_audiences = [snake_to_title(a) for a in args.audience] + if len(mapped_audiences) == 1: + mapped_audiences = mapped_audiences[0] from_pr_number(args.pr, mapped_audiences, args.bump, args.force) return 0 except Exception as e: -- GitLab From 7240b474b92028a1645de6fdb9e0a8aa28dc86be Mon Sep 17 00:00:00 2001 From: Andrii Date: Thu, 17 Oct 2024 22:24:32 +0300 Subject: [PATCH 03/50] Added Trusted Query API calls (#6039) Implemented is_trusted_reserve and is_trusted_teleporter API methods. Tested them with regular and chopstick tests. Fixes #97 --------- Co-authored-by: Francisco Aguirre --- .../assets/asset-hub-rococo/src/lib.rs | 13 +- .../assets/asset-hub-westend/src/lib.rs | 11 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 9 ++ .../bridge-hubs/bridge-hub-westend/src/lib.rs | 9 ++ .../collectives-westend/src/lib.rs | 9 ++ .../contracts/contracts-rococo/src/lib.rs | 9 ++ .../coretime/coretime-rococo/src/lib.rs | 9 ++ .../coretime/coretime-westend/src/lib.rs | 9 ++ .../runtimes/people/people-rococo/src/lib.rs | 9 ++ .../runtimes/people/people-westend/src/lib.rs | 9 ++ .../runtimes/testing/penpal/src/lib.rs | 11 +- polkadot/xcm/pallet-xcm/src/lib.rs | 52 ++++++ polkadot/xcm/xcm-runtime-apis/src/lib.rs | 4 + .../xcm/xcm-runtime-apis/src/trusted_query.rs | 49 ++++++ polkadot/xcm/xcm-runtime-apis/tests/mock.rs | 11 ++ .../xcm-runtime-apis/tests/trusted_query.rs | 150 ++++++++++++++++++ .../coretime-rococo-local.scale | Bin 0 -> 166046 bytes .../metadata-files/rococo-local.scale | Bin 0 -> 389535 bytes prdoc/pr_6039.prdoc | 54 +++++++ 19 files changed, 424 insertions(+), 3 deletions(-) create mode 100644 polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs create mode 100644 polkadot/xcm/xcm-runtime-apis/tests/trusted_query.rs create mode 100644 polkadot/zombienet-sdk-tests/metadata-files/coretime-rococo-local.scale create mode 100644 polkadot/zombienet-sdk-tests/metadata-files/rococo-local.scale create mode 100644 prdoc/pr_6039.prdoc 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 67f810e1eb6..eb3e26764f6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -103,7 +103,7 @@ use xcm::latest::prelude::{ }; use xcm::{ latest::prelude::{AssetId, BodyId}, - VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, + VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, }; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, @@ -1098,6 +1098,8 @@ pub type Executive = frame_executive::Executive< Migrations, >; +type XcmTrustedQueryResult = Result; + #[cfg(feature = "runtime-benchmarks")] mod benches { frame_benchmarking::define_benchmarks!( @@ -1798,6 +1800,15 @@ impl_runtime_apis! { genesis_config_presets::preset_names() } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> XcmTrustedQueryResult { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> XcmTrustedQueryResult { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { 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 aaeb3919987..74e75ebb489 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -93,7 +93,7 @@ use assets_common::{ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use xcm::{ latest::prelude::AssetId, - prelude::{VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}, + prelude::{VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}, }; #[cfg(feature = "runtime-benchmarks")] @@ -1894,6 +1894,15 @@ impl_runtime_apis! { genesis_config_presets::preset_names() } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { 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 e3435f35f17..cafd2b33fa8 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 @@ -1521,6 +1521,15 @@ impl_runtime_apis! { genesis_config_presets::preset_names() } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } #[cfg(test)] 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 b73d8e5c67f..a18fc8accc9 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 @@ -1337,6 +1337,15 @@ impl_runtime_apis! { genesis_config_presets::preset_names() } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 7f87d4701f9..b516c264e91 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -1165,6 +1165,15 @@ impl_runtime_apis! { genesis_config_presets::preset_names() } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 3377802e91d..6f79780dc17 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -879,6 +879,15 @@ impl_runtime_apis! { vec![] } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index f16dae04f21..d34689deed6 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -1148,6 +1148,15 @@ impl_runtime_apis! { vec![] } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 187856b6c61..c3516df9aa1 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -1141,6 +1141,15 @@ impl_runtime_apis! { vec![] } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index aa04d01cf03..b2883a2bbf9 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -1059,6 +1059,15 @@ impl_runtime_apis! { vec![] } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index c8131c94f15..f4f2c1ac22b 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -1058,6 +1058,15 @@ impl_runtime_apis! { vec![] } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 05773846164..917b3b04a76 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -92,7 +92,7 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::{ latest::prelude::{AssetId as AssetLocationId, BodyId}, - VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, + VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, }; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, @@ -1143,6 +1143,15 @@ impl_runtime_apis! { vec![] } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index d287987a96d..951fb8553d5 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -70,6 +70,7 @@ use xcm_executor::{ use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, fees::Error as XcmPaymentApiError, + trusted_query::Error as TrustedQueryApiError, }; #[cfg(any(feature = "try-runtime", test))] @@ -2603,6 +2604,57 @@ impl Pallet { }) } + /// Given an Asset and a Location, returns if the provided location is a trusted reserve for the + /// given asset. + pub fn is_trusted_reserve( + asset: VersionedAsset, + location: VersionedLocation, + ) -> Result { + let location: Location = location.try_into().map_err(|e| { + tracing::debug!( + target: "xcm::pallet_xcm::is_trusted_reserve", + "Asset version conversion failed with error: {:?}", + e, + ); + TrustedQueryApiError::VersionedLocationConversionFailed + })?; + + let a: Asset = asset.try_into().map_err(|e| { + tracing::debug!( + target: "xcm::pallet_xcm::is_trusted_reserve", + "Location version conversion failed with error: {:?}", + e, + ); + TrustedQueryApiError::VersionedAssetConversionFailed + })?; + + Ok(::IsReserve::contains(&a, &location)) + } + + /// Given an Asset and a Location, returns if the asset can be teleported to provided location. + pub fn is_trusted_teleporter( + asset: VersionedAsset, + location: VersionedLocation, + ) -> Result { + let location: Location = location.try_into().map_err(|e| { + tracing::debug!( + target: "xcm::pallet_xcm::is_trusted_teleporter", + "Asset version conversion failed with error: {:?}", + e, + ); + TrustedQueryApiError::VersionedLocationConversionFailed + })?; + let a: Asset = asset.try_into().map_err(|e| { + tracing::debug!( + target: "xcm::pallet_xcm::is_trusted_teleporter", + "Location version conversion failed with error: {:?}", + e, + ); + TrustedQueryApiError::VersionedAssetConversionFailed + })?; + Ok(::IsTeleporter::contains(&a, &location)) + } + pub fn query_delivery_fees( destination: VersionedLocation, message: VersionedXcm<()>, diff --git a/polkadot/xcm/xcm-runtime-apis/src/lib.rs b/polkadot/xcm/xcm-runtime-apis/src/lib.rs index 44e518e8e7a..f9a857c7c4c 100644 --- a/polkadot/xcm/xcm-runtime-apis/src/lib.rs +++ b/polkadot/xcm/xcm-runtime-apis/src/lib.rs @@ -30,3 +30,7 @@ pub mod dry_run; /// Fee estimation API. /// Given an XCM program, it will return the fees needed to execute it properly or send it. pub mod fees; + +// Exposes runtime API for querying whether a Location is trusted as a reserve or teleporter for a +// given Asset. +pub mod trusted_query; diff --git a/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs b/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs new file mode 100644 index 00000000000..a0c4416c831 --- /dev/null +++ b/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs @@ -0,0 +1,49 @@ +// 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 . + +//! Runtime API definition for checking if given is trusted reserve or teleporter. + +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::TypeInfo; +use xcm::{VersionedAsset, VersionedLocation}; + +sp_api::decl_runtime_apis! { + // API for querying trusted reserves and trusted teleporters. + pub trait TrustedQueryApi { + /// Returns if the location is a trusted reserve for the asset. + /// + /// # Arguments + /// * `asset`: `VersionedAsset`. + /// * `location`: `VersionedLocation`. + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result; + /// Returns if the asset can be teleported to the location. + /// + /// # Arguments + /// * `asset`: `VersionedAsset`. + /// * `location`: `VersionedLocation`. + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result; + } +} + +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +pub enum Error { + /// Converting a versioned Asset structure from one version to another failed. + #[codec(index = 1)] + VersionedAssetConversionFailed, + /// Converting a versioned Location structure from one version to another failed. + #[codec(index = 1)] + VersionedLocationConversionFailed, +} diff --git a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs index 6575feccf8a..3e284c915bf 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs @@ -48,6 +48,7 @@ use xcm_runtime_apis::{ conversions::{Error as LocationToAccountApiError, LocationToAccountApi}, dry_run::{CallDryRunEffects, DryRunApi, Error as XcmDryRunApiError, XcmDryRunEffects}, fees::{Error as XcmPaymentApiError, XcmPaymentApi}, + trusted_query::{Error as TrustedQueryApiError, TrustedQueryApi}, }; construct_runtime! { @@ -414,6 +415,16 @@ impl sp_api::ProvideRuntimeApi for TestClient { } sp_api::mock_impl_runtime_apis! { + impl TrustedQueryApi for RuntimeApi { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_reserve(asset, location) + } + + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_teleporter(asset, location) + } + } + impl LocationToAccountApi for RuntimeApi { fn convert_location(location: VersionedLocation) -> Result { let location = location.try_into().map_err(|_| LocationToAccountApiError::VersionedConversionFailed)?; diff --git a/polkadot/xcm/xcm-runtime-apis/tests/trusted_query.rs b/polkadot/xcm/xcm-runtime-apis/tests/trusted_query.rs new file mode 100644 index 00000000000..5e3a68b9225 --- /dev/null +++ b/polkadot/xcm/xcm-runtime-apis/tests/trusted_query.rs @@ -0,0 +1,150 @@ +// 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 . + +mod mock; + +use frame_support::sp_runtime::testing::H256; +use mock::*; +use sp_api::ProvideRuntimeApi; +use xcm::{prelude::*, v3}; +use xcm_runtime_apis::trusted_query::{Error, TrustedQueryApi}; + +#[test] +fn query_trusted_reserve() { + #[derive(Debug)] + struct TestCase { + name: &'static str, + asset: VersionedAsset, + location: VersionedLocation, + expected: Result, + } + + sp_io::TestExternalities::default().execute_with(|| { + let client = TestClient {}; + let runtime_api = client.runtime_api(); + + let test_cases: Vec = vec![ + TestCase { + // matches!(asset.id.0.unpack(), (1, [])) && matches!(origin.unpack(), (1, + // [Parachain(1000)])) + name: "Valid asset and location", + asset: Asset { id: AssetId(Location::parent()), fun: Fungible(123) }.into(), + location: (Parent, Parachain(1000)).into(), + expected: Ok(true), + }, + TestCase { + name: "Invalid location and valid asset", + asset: Asset { id: AssetId(Location::parent()), fun: Fungible(100) }.into(), + location: (Parent, Parachain(1002)).into(), + expected: Ok(false), + }, + TestCase { + name: "Valid location and invalid asset", + asset: Asset { id: AssetId(Location::new(0, [])), fun: Fungible(100) }.into(), + location: (Parent, Parachain(1000)).into(), + expected: Ok(false), + }, + TestCase { + name: "Invalid asset conversion", + asset: VersionedAsset::V3(v3::MultiAsset { + id: v3::AssetId::Abstract([1; 32]), + fun: v3::Fungibility::Fungible(1), + }), + location: (Parent, Parachain(1000)).into(), + expected: Err(Error::VersionedAssetConversionFailed), + }, + ]; + + for tc in test_cases { + let res = + runtime_api.is_trusted_reserve(H256::zero(), tc.asset.clone(), tc.location.clone()); + let inner_res = res.unwrap_or_else(|e| { + panic!("Test case '{}' failed with ApiError: {:?}", tc.name, e); + }); + + assert_eq!( + tc.expected, inner_res, + "Test case '{}' failed: expected {:?}, got {:?}", + tc.name, tc.expected, inner_res + ); + } + }); +} + +#[test] +fn query_trusted_teleporter() { + #[derive(Debug)] + struct TestCase { + name: &'static str, + asset: VersionedAsset, + location: VersionedLocation, + expected: Result, + } + + sp_io::TestExternalities::default().execute_with(|| { + let client = TestClient {}; + let runtime_api = client.runtime_api(); + + let test_cases: Vec = vec![ + TestCase { + // matches!(asset.id.0.unpack(), (0, [])) && matches!(origin.unpack(), (1, + // [Parachain(1000)])) + name: "Valid asset and location", + asset: Asset { id: AssetId(Location::new(0, [])), fun: Fungible(100) }.into(), + location: (Parent, Parachain(1000)).into(), + expected: Ok(true), + }, + TestCase { + name: "Invalid location and valid asset", + asset: Asset { id: AssetId(Location::new(0, [])), fun: Fungible(100) }.into(), + location: (Parent, Parachain(1002)).into(), + expected: Ok(false), + }, + TestCase { + name: "Valid location and invalid asset", + asset: Asset { id: AssetId(Location::new(1, [])), fun: Fungible(100) }.into(), + location: (Parent, Parachain(1002)).into(), + expected: Ok(false), + }, + TestCase { + name: "Invalid asset conversion", + asset: VersionedAsset::V3(v3::MultiAsset { + id: v3::AssetId::Abstract([1; 32]), + fun: v3::Fungibility::Fungible(1), + }), + location: (Parent, Parachain(1000)).into(), + expected: Err(Error::VersionedAssetConversionFailed), + }, + ]; + + for tc in test_cases { + let res = runtime_api.is_trusted_teleporter( + H256::zero(), + tc.asset.clone(), + tc.location.clone(), + ); + let inner_res = res.unwrap_or_else(|e| { + panic!("Test case '{}' failed with ApiError: {:?}", tc.name, e); + }); + + assert_eq!( + tc.expected, inner_res, + "Test case '{}' failed: expected {:?}, got {:?}", + tc.name, tc.expected, inner_res + ); + } + }); +} diff --git a/polkadot/zombienet-sdk-tests/metadata-files/coretime-rococo-local.scale b/polkadot/zombienet-sdk-tests/metadata-files/coretime-rococo-local.scale new file mode 100644 index 0000000000000000000000000000000000000000..3af6685b91dccb5effc21b5216b850a6127b0650 GIT binary patch literal 166046 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

&dNZaz4ev!FDZD(@YQ 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 literal 0 HcmV?d00001 diff --git a/prdoc/pr_6039.prdoc b/prdoc/pr_6039.prdoc new file mode 100644 index 00000000000..e14ea8f3e17 --- /dev/null +++ b/prdoc/pr_6039.prdoc @@ -0,0 +1,54 @@ +# 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 Trusted Query API calls." + +doc: + - audience: Runtime Dev + description: | + Added is_trusted_reserve and is_trusted_teleporter API calls to all the runtimes. + Given an asset and a location, they return if the chain trusts that location as a reserve or teleporter for that asset respectively. + You can implement them on your runtime by simply calling a helper function on `pallet-xcm`. + ```rust + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } + ``` + + - audience: Runtime User + description: | + There's a new runtime API to check if a chain trust a Location as a reserve or teleporter for a given Asset. + It's implemented in all the relays and system parachains in Westend and Rococo. + +crates: + - name: asset-hub-westend-runtime + bump: minor + - name: bridge-hub-rococo-runtime + bump: minor + - name: bridge-hub-westend-runtime + bump: minor + - name: collectives-westend-runtime + bump: minor + - name: contracts-rococo-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: asset-hub-rococo-runtime + bump: minor + - name: pallet-xcm + bump: minor + - name: xcm-runtime-apis + bump: minor -- GitLab From 09155dbc145a13e1c36e5bef13d52b3c2727b5f8 Mon Sep 17 00:00:00 2001 From: Ermal Kaleci Date: Fri, 18 Oct 2024 14:04:25 +0200 Subject: [PATCH 04/50] pallet-revive: EXTCODEHASH to match EIP-1052 (#6088) # Description Update `ext_code_hash` to match [EIP-1052](https://eips.ethereum.org/EIPS/eip-1052) specs. Since all possible results are written into output pointer then there's no need for a return value. https://github.com/paritytech/revive/pull/77 --- prdoc/pr_6088.prdoc | 14 ++++++ .../revive/fixtures/contracts/code_hash.rs | 40 +++++++++++++++ substrate/frame/revive/src/exec.rs | 50 +++++++++++++------ substrate/frame/revive/src/tests.rs | 42 ++++++++++++++++ substrate/frame/revive/src/wasm/runtime.rs | 26 +++------- substrate/frame/revive/uapi/src/host.rs | 8 +-- .../frame/revive/uapi/src/host/riscv32.rs | 7 ++- 7 files changed, 147 insertions(+), 40 deletions(-) create mode 100644 prdoc/pr_6088.prdoc create mode 100644 substrate/frame/revive/fixtures/contracts/code_hash.rs diff --git a/prdoc/pr_6088.prdoc b/prdoc/pr_6088.prdoc new file mode 100644 index 00000000000..93e435bbd45 --- /dev/null +++ b/prdoc/pr_6088.prdoc @@ -0,0 +1,14 @@ +title: "[pallet-revive] EXTCODEHASH to match EIP-1052" + +doc: + - audience: Runtime Dev + description: | + Update `ext_code_hash` to match [EIP-1052](https://eips.ethereum.org/EIPS/eip-1052) specs. + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: patch + - name: pallet-revive-uapi + bump: major diff --git a/substrate/frame/revive/fixtures/contracts/code_hash.rs b/substrate/frame/revive/fixtures/contracts/code_hash.rs new file mode 100644 index 00000000000..b598a485a8c --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/code_hash.rs @@ -0,0 +1,40 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + address: &[u8; 20], + expected_code_hash: &[u8; 32], + ); + + let mut code_hash = [0u8; 32]; + api::code_hash(address, &mut code_hash); + + assert!(&code_hash == expected_code_hash); +} diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index fffc3e4f483..07dbd096339 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -66,6 +66,10 @@ type VarSizedKey = BoundedVec>; const FRAME_ALWAYS_EXISTS_ON_INSTANTIATE: &str = "The return value is only `None` if no contract exists at the specified address. This cannot happen on instantiate or delegate; qed"; +/// Code hash of existing account without code (keccak256 hash of empty data). +pub const EMPTY_CODE_HASH: H256 = + H256(sp_core::hex2array!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + /// Combined key type for both fixed and variable sized storage keys. pub enum Key { /// Variant for fixed sized keys. @@ -272,9 +276,8 @@ pub trait Ext: sealing::Sealed { fn is_contract(&self, address: &H160) -> bool; /// Returns the code hash of the contract for the given `address`. - /// - /// Returns `None` if the `address` does not belong to a contract. - fn code_hash(&self, address: &H160) -> Option; + /// If not a contract but account exists then `keccak_256([])` is returned, otherwise `zero`. + fn code_hash(&self, address: &H160) -> H256; /// Returns the code hash of the contract being executed. fn own_code_hash(&mut self) -> &H256; @@ -1536,8 +1539,15 @@ where ContractInfoOf::::contains_key(&address) } - fn code_hash(&self, address: &H160) -> Option { - >::get(&address).map(|contract| contract.code_hash) + fn code_hash(&self, address: &H160) -> H256 { + >::get(&address) + .map(|contract| contract.code_hash) + .unwrap_or_else(|| { + if System::::account_exists(&T::AddressMapper::to_account_id(address)) { + return EMPTY_CODE_HASH; + } + H256::zero() + }) } fn own_code_hash(&mut self) -> &H256 { @@ -1817,9 +1827,10 @@ mod tests { }; use assert_matches::assert_matches; use frame_support::{assert_err, assert_ok, parameter_types}; - use frame_system::{EventRecord, Phase}; + use frame_system::{AccountInfo, EventRecord, Phase}; use pallet_revive_uapi::ReturnFlags; use pretty_assertions::assert_eq; + use sp_io::hashing::keccak_256; use sp_runtime::{traits::Hash, DispatchError}; use std::{cell::RefCell, collections::hash_map::HashMap, rc::Rc}; @@ -1870,8 +1881,8 @@ mod tests { f: impl Fn(MockCtx, &MockExecutable) -> ExecResult + 'static, ) -> H256 { Loader::mutate(|loader| { - // Generate code hashes as monotonically increasing values. - let hash = ::Hash::from_low_u64_be(loader.counter); + // Generate code hashes from contract index value. + let hash = H256(keccak_256(&loader.counter.to_le_bytes())); loader.counter += 1; loader.map.insert( hash, @@ -2386,16 +2397,25 @@ mod tests { #[test] fn code_hash_returns_proper_values() { - let code_bob = MockLoader::insert(Call, |ctx, _| { - // ALICE is not a contract and hence they do not have a code_hash - assert!(ctx.ext.code_hash(&ALICE_ADDR).is_none()); - // BOB is a contract and hence it has a code_hash - assert!(ctx.ext.code_hash(&BOB_ADDR).is_some()); + let bob_code_hash = MockLoader::insert(Call, |ctx, _| { + // ALICE is not a contract but account exists so it returns hash of empty data + assert_eq!(ctx.ext.code_hash(&ALICE_ADDR), EMPTY_CODE_HASH); + // BOB is a contract (this function) and hence it has a code_hash. + // `MockLoader` uses contract index to generate the code hash. + assert_eq!(ctx.ext.code_hash(&BOB_ADDR), H256(keccak_256(&0u64.to_le_bytes()))); + // [0xff;20] doesn't exist and returns hash zero + assert!(ctx.ext.code_hash(&H160([0xff; 20])).is_zero()); + exec_success() }); ExtBuilder::default().build().execute_with(|| { - place_contract(&BOB, code_bob); + // add alice account info to test case EOA code hash + frame_system::Account::::insert( + ::AddressMapper::to_account_id(&ALICE_ADDR), + AccountInfo { consumers: 1, providers: 1, ..Default::default() }, + ); + place_contract(&BOB, bob_code_hash); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // ALICE (not contract) -> BOB (contract) @@ -2415,7 +2435,7 @@ mod tests { #[test] fn own_code_hash_returns_proper_values() { let bob_ch = MockLoader::insert(Call, |ctx, _| { - let code_hash = ctx.ext.code_hash(&BOB_ADDR).unwrap(); + let code_hash = ctx.ext.code_hash(&BOB_ADDR); assert_eq!(*ctx.ext.own_code_hash(), code_hash); exec_success() }); diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 4816e65f8f5..e637c5f991c 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -4442,4 +4442,46 @@ mod run_tests { ); }); } + + #[test] + fn code_hash_works() { + let (code_hash_code, self_code_hash) = compile_module("code_hash").unwrap(); + let (dummy_code, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code_hash_code)).build_and_unwrap_contract(); + let Contract { addr: dummy_addr, .. } = + builder::bare_instantiate(Code::Upload(dummy_code)).build_and_unwrap_contract(); + + // code hash of dummy contract + assert_ok!(builder::call(addr).data((dummy_addr, code_hash).encode()).build()); + // code has of itself + assert_ok!(builder::call(addr).data((addr, self_code_hash).encode()).build()); + + // EOA doesn't exists + assert_err!( + builder::bare_call(addr) + .data((BOB_ADDR, crate::exec::EMPTY_CODE_HASH).encode()) + .build() + .result, + Error::::ContractTrapped + ); + // non-existing will return zero + assert_ok!(builder::call(addr).data((BOB_ADDR, H256::zero()).encode()).build()); + + // create EOA + let _ = ::Currency::set_balance( + &::AddressMapper::to_account_id(&BOB_ADDR), + 1_000_000, + ); + + // EOA returns empty code hash + assert_ok!(builder::call(addr) + .data((BOB_ADDR, crate::exec::EMPTY_CODE_HASH).encode()) + .build()); + }); + } } diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 245c91278a7..36cd03e9dd6 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -1406,27 +1406,17 @@ pub mod env { /// Retrieve the code hash for a specified contract address. /// See [`pallet_revive_uapi::HostFn::code_hash`]. #[api_version(0)] - fn code_hash( - &mut self, - memory: &mut M, - addr_ptr: u32, - out_ptr: u32, - ) -> Result { + fn code_hash(&mut self, memory: &mut M, addr_ptr: u32, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::CodeHash)?; let mut address = H160::zero(); memory.read_into_buf(addr_ptr, address.as_bytes_mut())?; - if let Some(value) = self.ext.code_hash(&address) { - self.write_fixed_sandbox_output( - memory, - out_ptr, - &value.as_bytes(), - false, - already_charged, - )?; - Ok(ReturnErrorCode::Success) - } else { - Ok(ReturnErrorCode::KeyNotFound) - } + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &self.ext.code_hash(&address).as_bytes(), + false, + already_charged, + )?) } /// Retrieve the code hash of the currently executing contract. diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index 2106b8fb49b..2663d7c2cf0 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -245,10 +245,12 @@ pub trait HostFn: private::Sealed { /// - `addr`: The address of the contract. /// - `output`: A reference to the output data buffer to write the code hash. /// - /// # Errors + /// # Note /// - /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - fn code_hash(addr: &[u8; 20], output: &mut [u8; 32]) -> Result; + /// If `addr` is not a contract but the account exists then the hash of empty data + /// `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` is written, + /// otherwise `zero`. + fn code_hash(addr: &[u8; 20], output: &mut [u8; 32]); /// Checks whether there is a value stored under the given key. /// diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index 866b0ee8dd1..c2508198c93 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -74,7 +74,7 @@ mod sys { pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); pub fn caller(out_ptr: *mut u8); pub fn is_contract(account_ptr: *const u8) -> ReturnCode; - pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8) -> ReturnCode; + pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8); pub fn own_code_hash(out_ptr: *mut u8); pub fn caller_is_origin() -> ReturnCode; pub fn caller_is_root() -> ReturnCode; @@ -528,9 +528,8 @@ impl HostFn for HostFnImpl { ret_val.into() } - fn code_hash(address: &[u8; 20], output: &mut [u8; 32]) -> Result { - let ret_val = unsafe { sys::code_hash(address.as_ptr(), output.as_mut_ptr()) }; - ret_val.into() + fn code_hash(address: &[u8; 20], output: &mut [u8; 32]) { + unsafe { sys::code_hash(address.as_ptr(), output.as_mut_ptr()) } } fn own_code_hash(output: &mut [u8; 32]) { -- GitLab From a0aefc6b233ace0a82a8631d67b6854e6aeb014b Mon Sep 17 00:00:00 2001 From: Pavlo Khrystenko <45178695+pkhry@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:29:03 +0200 Subject: [PATCH 05/50] rpc v2: backpressure `chainhead_v1_follow` (#6058) # Description closes #5871 > The chainHead_v1_follow is using unbounded channels to send out messages on the JSON-RPC connection which may use lots of memory if the client is slow and can't really keep up with server i.e, substrate may keep lots of message in memory This PR changes the outgoing stream to abort and send a `Stop` event downstream in the case that client doesn't keep up with the producer. ## Integration *In depth notes about how this PR should be integrated by downstream projects. This part is mandatory, and should be reviewed by reviewers, if the PR does NOT have the `R0-Silent` label. In case of a `R0-Silent`, it can be ignored.* ## Review Notes - `rpc::Subscription::pipe_from_stream` - now takes `Self` param by reference, change was made to allow sending events to the `Subscription` after calls to `pipe_from_stream`. - `chainhead_follow::submit_events` - now uses `Abortable` stream to end it early in case - connection was closed by the client - signal received that subscription should stop - error has occured when processing the events - client can't keep up with the events produced - TODO: - make the abort logic less hacky --------- Co-authored-by: Niklas Adolfsson Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> --- prdoc/pr_6058.prdoc | 18 ++++ .../rpc-spec-v2/src/chain_head/chain_head.rs | 8 ++ .../src/chain_head/chain_head_follow.rs | 92 ++++++++----------- .../rpc-spec-v2/src/chain_head/tests.rs | 77 +++++++++++++++- substrate/client/rpc/src/utils.rs | 43 ++++++--- 5 files changed, 169 insertions(+), 69 deletions(-) create mode 100644 prdoc/pr_6058.prdoc diff --git a/prdoc/pr_6058.prdoc b/prdoc/pr_6058.prdoc new file mode 100644 index 00000000000..5b99467b413 --- /dev/null +++ b/prdoc/pr_6058.prdoc @@ -0,0 +1,18 @@ +# 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: backpressure `chainhead_v1_follow` + +doc: + - audience: Node Operator + description: | + The RPC endpoint `chainHead_v1_follow` now relies on backpressure + to determine whether or not the subscription should be closed instead of continuing to send more events + to a consumer which can't keep up. + This should significantly improve memory consumption as substrate will be keeping less messages in memory. + +crates: + - name: sc-rpc-spec-v2 + bump: major + - name: sc-rpc + bump: major diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs index a88e7f2a0b3..61eb47d1b9a 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs @@ -75,6 +75,8 @@ pub struct ChainHeadConfig { pub max_lagging_distance: usize, /// The maximum number of `chainHead_follow` subscriptions per connection. pub max_follow_subscriptions_per_connection: usize, + /// The maximum number of pending messages per subscription. + pub subscription_buffer_cap: usize, } /// Maximum pinned blocks across all connections. @@ -107,6 +109,7 @@ impl Default for ChainHeadConfig { subscription_max_ongoing_operations: MAX_ONGOING_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, } } } @@ -126,6 +129,8 @@ pub struct ChainHead, Block: BlockT, Client> { max_lagging_distance: usize, /// Phantom member to pin the block type. _phantom: PhantomData, + /// The maximum number of pending messages per subscription. + subscription_buffer_cap: usize, } impl, Block: BlockT, Client> ChainHead { @@ -148,6 +153,7 @@ impl, Block: BlockT, Client> ChainHead { backend, ), max_lagging_distance: config.max_lagging_distance, + subscription_buffer_cap: config.subscription_buffer_cap, _phantom: PhantomData, } } @@ -196,6 +202,7 @@ where let backend = self.backend.clone(); let client = self.client.clone(); let max_lagging_distance = self.max_lagging_distance; + let subscription_buffer_cap = self.subscription_buffer_cap; let fut = async move { // Ensure the current connection ID has enough space to accept a new subscription. @@ -231,6 +238,7 @@ where with_runtime, sub_id.clone(), max_lagging_distance, + subscription_buffer_cap, ); let result = chain_head_follow.generate_events(sink, sub_data).await; if let Err(SubscriptionManagementError::BlockDistanceTooLarge) = result { diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index f2326f01567..e9975b36b4a 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -28,9 +28,8 @@ use crate::chain_head::{ }; use futures::{ channel::oneshot, - stream::{self, Stream, StreamExt}, + stream::{self, Stream, StreamExt, TryStreamExt}, }; -use futures_util::future::Either; use log::debug; use sc_client_api::{ Backend, BlockBackend, BlockImportNotification, BlockchainEvents, FinalityNotification, @@ -74,6 +73,8 @@ pub struct ChainHeadFollower, Block: BlockT, Client> { /// Stop all subscriptions if the distance between the leaves and the current finalized /// block is larger than this value. max_lagging_distance: usize, + /// The maximum number of pending messages per subscription. + pub subscription_buffer_cap: usize, } struct AnnouncedBlocks { @@ -148,6 +149,7 @@ impl, Block: BlockT, Client> ChainHeadFollower Self { Self { client, @@ -161,6 +163,7 @@ impl, Block: BlockT, Client> ChainHeadFollower( &mut self, startup_point: &StartupPoint, - mut stream: EventStream, + stream: EventStream, sink: Subscription, rx_stop: oneshot::Receiver<()>, ) -> Result<(), SubscriptionManagementError> where - EventStream: Stream> + Unpin, + EventStream: Stream> + Unpin + Send, { - let mut stream_item = stream.next(); - - // The stop event can be triggered by the chainHead logic when the pinned - // block guarantee cannot be hold. Or when the client is disconnected. - let connection_closed = sink.closed(); - tokio::pin!(connection_closed); - let mut stop_event = futures_util::future::select(rx_stop, connection_closed); - - while let Either::Left((Some(event), next_stop_event)) = - futures_util::future::select(stream_item, stop_event).await - { - let events = match event { - NotificationType::InitialEvents(events) => Ok(events), - NotificationType::NewBlock(notification) => - self.handle_import_blocks(notification, &startup_point), - NotificationType::Finalized(notification) => - self.handle_finalized_blocks(notification, &startup_point), - NotificationType::MethodResponse(notification) => Ok(vec![notification]), - }; + let buffer_cap = self.subscription_buffer_cap; + // create a channel to propagate error messages + let mut handle_events = |event| match event { + NotificationType::InitialEvents(events) => Ok(events), + NotificationType::NewBlock(notification) => + self.handle_import_blocks(notification, &startup_point), + NotificationType::Finalized(notification) => + self.handle_finalized_blocks(notification, &startup_point), + NotificationType::MethodResponse(notification) => Ok(vec![notification]), + }; - let events = match events { - Ok(events) => events, - Err(err) => { - debug!( - target: LOG_TARGET, - "[follow][id={:?}] Failed to handle stream notification {:?}", - self.sub_id, - err - ); - _ = sink.send(&FollowEvent::::Stop).await; - return Err(err) - }, - }; + let stream = stream + .map(|event| handle_events(event)) + .map_ok(|items| stream::iter(items).map(Ok)) + .try_flatten(); + + tokio::pin!(stream); + + let sink_future = + sink.pipe_from_try_stream(stream, sc_rpc::utils::BoundedVecDeque::new(buffer_cap)); - for event in events { - if let Err(err) = sink.send(&event).await { - // Failed to submit event. + let result = tokio::select! { + _ = rx_stop => Ok(()), + result = sink_future => { + if let Err(ref e) = result { debug!( target: LOG_TARGET, - "[follow][id={:?}] Failed to send event {:?}", self.sub_id, err + "[follow][id={:?}] Failed to handle stream notification {:?}", + &self.sub_id, + e ); - - let _ = sink.send(&FollowEvent::::Stop).await; - // No need to propagate this error further, the client disconnected. - return Ok(()) - } + }; + result } - - stream_item = stream.next(); - stop_event = next_stop_event; - } - - // If we got here either: - // - the substrate streams have closed - // - the `Stop` receiver was triggered internally (cannot hold the pinned block guarantee) - // - the client disconnected. + }; let _ = sink.send(&FollowEvent::::Stop).await; - Ok(()) + result } /// Generate the block events for the `chainHead_follow` method. diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index 0c2486157bd..c505566d887 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -44,7 +44,7 @@ use sp_core::{ use sp_runtime::traits::Block as BlockT; use sp_version::RuntimeVersion; use std::{ - collections::{HashMap, HashSet}, + collections::{HashMap, HashSet, VecDeque}, fmt::Debug, sync::Arc, time::Duration, @@ -86,6 +86,7 @@ pub async fn run_server() -> std::net::SocketAddr { subscription_max_ongoing_operations: MAX_OPERATIONS, max_follow_subscriptions_per_connection: 1, max_lagging_distance: MAX_LAGGING_DISTANCE, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -147,6 +148,7 @@ async fn setup_api() -> ( subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -254,6 +256,7 @@ async fn follow_subscription_produces_blocks() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -323,6 +326,7 @@ async fn follow_with_runtime() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -631,6 +635,7 @@ async fn call_runtime_without_flag() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -1290,6 +1295,7 @@ async fn separate_operation_ids_for_subscriptions() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -1376,6 +1382,7 @@ async fn follow_generates_initial_blocks() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -1532,6 +1539,7 @@ async fn follow_exceeding_pinned_blocks() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -1609,6 +1617,7 @@ async fn follow_with_unpin() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -1715,6 +1724,7 @@ async fn unpin_duplicate_hashes() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -1818,6 +1828,7 @@ async fn follow_with_multiple_unpin_hashes() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -1963,6 +1974,7 @@ async fn follow_prune_best_block() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -2149,6 +2161,7 @@ async fn follow_forks_pruned_block() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -2309,6 +2322,7 @@ async fn follow_report_multiple_pruned_block() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -2555,6 +2569,7 @@ async fn pin_block_references() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -2690,6 +2705,7 @@ async fn follow_finalized_before_new_block() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -2805,6 +2821,7 @@ async fn ensure_operation_limits_works() { subscription_max_ongoing_operations: 1, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -2910,6 +2927,7 @@ async fn storage_is_backpressured() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -3047,6 +3065,7 @@ async fn stop_storage_operation() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -3344,6 +3363,7 @@ async fn chain_head_stop_all_subscriptions() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: 5, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -3557,6 +3577,7 @@ async fn chain_head_limit_reached() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: 1, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -3597,6 +3618,7 @@ async fn follow_unique_pruned_blocks() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, max_lagging_distance: MAX_LAGGING_DISTANCE, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -3766,6 +3788,7 @@ async fn follow_report_best_block_of_a_known_block() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -3984,6 +4007,7 @@ async fn follow_event_with_unknown_parent() { subscription_max_ongoing_operations: MAX_OPERATIONS, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, max_lagging_distance: MAX_LAGGING_DISTANCE, + subscription_buffer_cap: MAX_PINNED_BLOCKS, }, ) .into_rpc(); @@ -4033,3 +4057,54 @@ async fn follow_event_with_unknown_parent() { // When importing the block 2, chainHead detects a gap in our blocks and stops. assert_matches!(get_next_event::>(&mut sub).await, FollowEvent::Stop); } + +#[tokio::test] +async fn events_are_backpressured() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + + let api = ChainHead::new( + client.clone(), + backend, + Arc::new(TokioTestExecutor::default()), + ChainHeadConfig { + global_max_pinned_blocks: MAX_PINNED_BLOCKS, + subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), + subscription_max_ongoing_operations: MAX_OPERATIONS, + max_lagging_distance: MAX_LAGGING_DISTANCE, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + subscription_buffer_cap: 10, + }, + ) + .into_rpc(); + + let mut parent_hash = client.chain_info().genesis_hash; + let mut header = VecDeque::new(); + let mut sub = api.subscribe("chainHead_v1_follow", [false], 1).await.unwrap(); + + // insert more events than the user can consume + for i in 0..=5 { + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(parent_hash) + .with_parent_block_number(i) + .build() + .unwrap() + .build() + .unwrap() + .block; + header.push_front(block.header().clone()); + + parent_hash = block.hash(); + client.import(BlockOrigin::Own, block.clone()).await.unwrap(); + } + + let mut events = Vec::new(); + + while let Some(event) = sub.next::>().await { + events.push(event); + } + + assert_eq!(events.len(), 2); + assert_matches!(events.pop().unwrap().map(|x| x.0), Ok(FollowEvent::Stop)); +} diff --git a/substrate/client/rpc/src/utils.rs b/substrate/client/rpc/src/utils.rs index e2ff04c0baf..b94f062cdda 100644 --- a/substrate/client/rpc/src/utils.rs +++ b/substrate/client/rpc/src/utils.rs @@ -21,7 +21,7 @@ use crate::SubscriptionTaskExecutor; use futures::{ future::{self, Either, Fuse, FusedFuture}, - Future, FutureExt, Stream, StreamExt, + Future, FutureExt, Stream, StreamExt, TryStream, TryStreamExt, }; use jsonrpsee::{ types::SubscriptionId, DisconnectError, PendingSubscriptionSink, SubscriptionMessage, @@ -173,14 +173,27 @@ impl From for Subscription { impl Subscription { /// Feed items to the subscription from the underlying stream /// with specified buffer strategy. - pub async fn pipe_from_stream(self, mut stream: S, mut buf: B) + pub async fn pipe_from_stream(&self, stream: S, buf: B) where - S: Stream + Unpin + Send + 'static, - T: Serialize + Send + 'static, + S: Stream + Unpin, + T: Serialize + Send, + B: Buffer, + { + self.pipe_from_try_stream(stream.map(Ok::), buf) + .await + .expect("No Err will be ever encountered.qed"); + } + + /// Feed items to the subscription from the underlying stream + /// with specified buffer strategy. + pub async fn pipe_from_try_stream(&self, mut stream: S, mut buf: B) -> Result<(), E> + where + S: TryStream + Unpin, + T: Serialize + Send, B: Buffer, { let mut next_fut = Box::pin(Fuse::terminated()); - let mut next_item = stream.next(); + let mut next_item = stream.try_next(); let closed = self.0.closed(); futures::pin_mut!(closed); @@ -201,7 +214,7 @@ impl Subscription { next_fut = Box::pin(Fuse::terminated()); }, // New item from the stream - Either::Right((Either::Right((Some(v), n)), c)) => { + Either::Right((Either::Right((Ok(Some(v)), n)), c)) => { if buf.push(v).is_err() { log::debug!( target: "rpc", @@ -209,31 +222,35 @@ impl Subscription { self.0.method_name(), self.0.connection_id().0 ); - return + return Ok(()); } next_fut = n; closed = c; - next_item = stream.next(); + next_item = stream.try_next(); }, + // Error occured while processing the stream. + // + // terminate the stream. + Either::Right((Either::Right((Err(e), _)), _)) => return Err(e), // Stream "finished". // // Process remaining items and terminate. - Either::Right((Either::Right((None, pending_fut)), _)) => { + Either::Right((Either::Right((Ok(None), pending_fut)), _)) => { if !pending_fut.is_terminated() && pending_fut.await.is_err() { - return; + return Ok(()); } while let Some(v) = buf.pop() { if self.send(&v).await.is_err() { - return; + return Ok(()); } } - return; + return Ok(()); }, // Subscription was closed. - Either::Left(_) => return, + Either::Left(_) => return Ok(()), } } } -- GitLab From b48a6fa5fce566aca4711abf291996b68b430556 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Fri, 18 Oct 2024 15:17:41 +0200 Subject: [PATCH 06/50] [CI] Fix branch-off pipeline (#6120) A tiny fix for the `Release - Branch off stable branch` pipeline, that adds an environment to be able to access secrets --- .github/workflows/release-branchoff-stable.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-branchoff-stable.yml b/.github/workflows/release-branchoff-stable.yml index 27a3fdc14ee..3086c0d21f4 100644 --- a/.github/workflows/release-branchoff-stable.yml +++ b/.github/workflows/release-branchoff-stable.yml @@ -43,7 +43,7 @@ jobs: create-stable-branch: needs: [prepare-tooling] runs-on: ubuntu-latest - + environment: release env: PGP_KMS_KEY: ${{ secrets.PGP_KMS_KEY }} PGP_KMS_HASH: ${{ secrets.PGP_KMS_HASH }} -- GitLab From a83f0fe83c7fb4833379344647920e99d20f83fa Mon Sep 17 00:00:00 2001 From: Giuseppe Re Date: Fri, 18 Oct 2024 16:40:02 +0200 Subject: [PATCH 07/50] Adding migration instruction from benchmarking v1 to v2 (#6093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Adding instruction to migrate benchmarking from v1 to v2 Even if the documentation for benchmarking v1 and v2 is clear and detailed, I feel that adding a migration guide from v1 to v2 would help doing it quicker. ## Integration This change only affects documentation, so it does not cause integration issues. ## Review Notes I followed the migration procedure I applied in PR https://github.com/paritytech/polkadot-sdk/pull/6018 . I added everything from there, but I may be missing some extra steps that are needed in specific case, so in case you notice something please let me know. --------- Co-authored-by: Dónal Murray --- substrate/frame/benchmarking/src/lib.rs | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/substrate/frame/benchmarking/src/lib.rs b/substrate/frame/benchmarking/src/lib.rs index 625da2a24bd..6e21356e9d4 100644 --- a/substrate/frame/benchmarking/src/lib.rs +++ b/substrate/frame/benchmarking/src/lib.rs @@ -311,6 +311,83 @@ pub use v1::*; /// } /// } /// ``` +/// +/// ## Migrate from v1 to v2 +/// +/// To migrate your code from benchmarking v1 to benchmarking v2, you may follow these +/// steps: +/// 1. Change the import from `frame_benchmarking::v1::` to `frame_benchmarking::v2::*`, or +/// `frame::benchmarking::prelude::*` under the umbrella crate; +/// 2. Move the code inside the v1 `benchmarks! { ... }` block to the v2 benchmarks module `mod +/// benchmarks { ... }` under the benchmarks macro (`#[benchmarks]` for a regular module, or +/// `#[instance_benchmarks]` to set up the module in instance benchmarking mode); +/// 3. Turn each v1 benchmark into a function inside the v2 benchmarks module with the same name, +/// having either a blank return type or a return type compatible with `Result<(), +/// BenchmarkError>`. For instance, `foo { ... }` can become `fn foo() -> Result<(), +/// BenchmarkError>`. More in detail: +/// 1. Move all the v1 complexity parameters as [ParamRange](`v2::ParamRange`) arguments to the +/// v2 function, and their setup code to the body of the function. For instance, `let y in 0 +/// .. 10 => setup(y)?;` from v1 will give a `y: Linear<0, 10>` argument to the corresponding +/// function in v2, while `setup(y)?;` will be moved to the body of the function; +/// 2. Move all the v1 setup code to the body of the v2 function; +/// 3. Move the benchmarked code to the body of the v2 function under the appropriate macro +/// attribute: `#[extrinsic_call]` for extrinsic pallet calls and `#[block]` for blocks of +/// code; +/// 4. Move the v1 verify code block to the body of the v2 function, after the +/// `#[extrinsic_call]` or `#[block]` attribute. +/// 5. If the function returns a `Result<(), BenchmarkError>`, end with `Ok(())`. +/// +/// As for tests, the code is the same as v1 (see [Benchmark Tests](#benchmark-tests)). +/// +/// As an example migration, the following v1 code +/// +/// ```ignore +/// #![cfg(feature = "runtime-benchmarks")] +/// +/// use frame_benchmarking::v1::*; +/// +/// benchmarks! { +/// +/// // first dispatchable: this is a user dispatchable and operates on a `u8` vector of +/// // size `l` +/// foo { +/// let caller = funded_account::(b"caller", 0); +/// let l in 1 .. 10_000 => initialize_l(l); +/// }: { +/// _(RuntimeOrigin::Signed(caller), vec![0u8; l]) +/// } verify { +/// assert_last_event::(Event::FooExecuted { result: Ok(()) }.into()); +/// } +/// } +/// ``` +/// +/// would become the following v2 code: +/// +/// ```ignore +/// #![cfg(feature = "runtime-benchmarks")] +/// +/// use frame_benchmarking::v2::*; +/// +/// #[benchmarks] +/// mod benchmarks { +/// use super::*; +/// +/// // first dispatchable: foo; this is a user dispatchable and operates on a `u8` vector of +/// // size `l` +/// #[benchmark] +/// fn foo(l: Linear<1 .. 10_000>) -> Result<(), BenchmarkError> { +/// let caller = funded_account::(b"caller", 0); +/// initialize_l(l); +/// +/// #[extrinsic_call] +/// _(RuntimeOrigin::Signed(caller), vec![0u8; l]); +/// +/// // Everything onwards will be treated as test. +/// assert_last_event::(Event::FooExecuted { result: Ok(()) }.into()); +/// Ok(()) +/// } +/// } +/// ``` pub mod v2 { pub use super::*; pub use frame_support_procedural::{ -- GitLab From b76e91acc953e682b3ddcfc45ecacaaf26c694a1 Mon Sep 17 00:00:00 2001 From: georgepisaltu <52418509+georgepisaltu@users.noreply.github.com> Date: Fri, 18 Oct 2024 20:37:32 +0300 Subject: [PATCH 08/50] FRAME: Reintroduce `TransactionExtension` as a replacement for `SignedExtension` (#3685) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original PR https://github.com/paritytech/polkadot-sdk/pull/2280 reverted in https://github.com/paritytech/polkadot-sdk/pull/3665 This PR reintroduces the reverted functionality with additional changes, related effort [here](https://github.com/paritytech/polkadot-sdk/pull/3623). Description is copied over from the original PR First part of [Extrinsic Horizon](https://github.com/paritytech/polkadot-sdk/issues/2415) Introduces a new trait `TransactionExtension` to replace `SignedExtension`. Introduce the idea of transactions which obey the runtime's extensions and have according Extension data (né Extra data) yet do not have hard-coded signatures. Deprecate the terminology of "Unsigned" when used for transactions/extrinsics owing to there now being "proper" unsigned transactions which obey the extension framework and "old-style" unsigned which do not. Instead we have __*General*__ for the former and __*Bare*__ for the latter. (Ultimately, the latter will be phased out as a type of transaction, and Bare will only be used for Inherents.) Types of extrinsic are now therefore: - Bare (no hardcoded signature, no Extra data; used to be known as "Unsigned") - Bare transactions (deprecated): Gossiped, validated with `ValidateUnsigned` (deprecated) and the `_bare_compat` bits of `TransactionExtension` (deprecated). - Inherents: Not gossiped, validated with `ProvideInherent`. - Extended (Extra data): Gossiped, validated via `TransactionExtension`. - Signed transactions (with a hardcoded signature) in extrinsic v4. - General transactions (without a hardcoded signature) in extrinsic v5. `TransactionExtension` differs from `SignedExtension` because: - A signature on the underlying transaction may validly not be present. - It may alter the origin during validation. - `pre_dispatch` is renamed to `prepare` and need not contain the checks present in `validate`. - `validate` and `prepare` is passed an `Origin` rather than a `AccountId`. - `validate` may pass arbitrary information into `prepare` via a new user-specifiable type `Val`. - `AdditionalSigned`/`additional_signed` is renamed to `Implicit`/`implicit`. It is encoded *for the entire transaction* and passed in to each extension as a new argument to `validate`. This facilitates the ability of extensions to acts as underlying crypto. There is a new `DispatchTransaction` trait which contains only default function impls and is impl'ed for any `TransactionExtension` impler. It provides several utility functions which reduce some of the tedium from using `TransactionExtension` (indeed, none of its regular functions should now need to be called directly). Three transaction version discriminator ("versions") are now permissible (RFC [here](https://github.com/polkadot-fellows/RFCs/pull/84)) in extrinsic version 5: - 0b00000100 or 0b00000101: Bare (used to be called "Unsigned"): contains Signature or Extra (extension data). After bare transactions are no longer supported, this will strictly identify an Inherents only. Available in both extrinsic versions 4 and 5. - 0b10000100: Old-school "Signed" Transaction: contains Signature, Extra (extension data) and an extension version byte, introduced as part of [RFC99](https://github.com/polkadot-fellows/RFCs/blob/main/text/0099-transaction-extension-version.md). Still available as part of extrinsic v4. - 0b01000101: New-school "General" Transaction: contains Extra (extension data) and an extension version byte, as per RFC99, but no Signature. Only available in extrinsic v5. For the New-school General Transaction, it becomes trivial for authors to publish extensions to the mechanism for authorizing an Origin, e.g. through new kinds of key-signing schemes, ZK proofs, pallet state, mutations over pre-authenticated origins or any combination of the above. `UncheckedExtrinsic` still maintains encode/decode backwards compatibility with extrinsic version 4, where the first byte was encoded as: - 0b00000100 - Unsigned transactions - 0b10000100 - Old-school Signed transactions, without the extension version byte Now, `UncheckedExtrinsic` contains a `Preamble` and the actual call. The `Preamble` describes the type of extrinsic as follows: ```rust /// A "header" for extrinsics leading up to the call itself. Determines the type of extrinsic and /// holds any necessary specialized data. #[derive(Eq, PartialEq, Clone)] pub enum Preamble { /// An extrinsic without a signature or any extension. This means it's either an inherent or /// an old-school "Unsigned" (we don't use that terminology any more since it's confusable with /// the general transaction which is without a signature but does have an extension). /// /// NOTE: In the future, once we remove `ValidateUnsigned`, this will only serve Inherent /// extrinsics and thus can be renamed to `Inherent`. Bare(ExtrinsicVersion), /// An old-school transaction extrinsic which includes a signature of some hard-coded crypto. /// Available only on extrinsic version 4. Signed(Address, Signature, ExtensionVersion, Extension), /// A new-school transaction extrinsic which does not include a signature by default. The /// origin authorization, through signatures or other means, is performed by the transaction /// extension in this extrinsic. Available starting with extrinsic version 5. General(ExtensionVersion, Extension), } ``` ## Code Migration ### NOW: Getting it to build Wrap your `SignedExtension`s in `AsTransactionExtension`. This should be accompanied by renaming your aggregate type in line with the new terminology. E.g. Before: ```rust /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( /* snip */ MySpecialSignedExtension, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; ``` After: ```rust /// The extension to the basic transaction logic. pub type TxExtension = ( /* snip */ AsTransactionExtension, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; ``` You'll also need to alter any transaction building logic to add a `.into()` to make the conversion happen. E.g. Before: ```rust fn construct_extrinsic( /* snip */ ) -> UncheckedExtrinsic { let extra: SignedExtra = ( /* snip */ MySpecialSignedExtension::new(/* snip */), ); let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); UncheckedExtrinsic::new_signed( /* snip */ Signature::Sr25519(signature), extra, ) } ``` After: ```rust fn construct_extrinsic( /* snip */ ) -> UncheckedExtrinsic { let tx_ext: TxExtension = ( /* snip */ MySpecialSignedExtension::new(/* snip */).into(), ); let payload = SignedPayload::new(call.clone(), tx_ext.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); UncheckedExtrinsic::new_signed( /* snip */ Signature::Sr25519(signature), tx_ext, ) } ``` ### SOON: Migrating to `TransactionExtension` Most `SignedExtension`s can be trivially converted to become a `TransactionExtension`. There are a few things to know. - Instead of a single trait like `SignedExtension`, you should now implement two traits individually: `TransactionExtensionBase` and `TransactionExtension`. - Weights are now a thing and must be provided via the new function `fn weight`. #### `TransactionExtensionBase` This trait takes care of anything which is not dependent on types specific to your runtime, most notably `Call`. - `AdditionalSigned`/`additional_signed` is renamed to `Implicit`/`implicit`. - Weight must be returned by implementing the `weight` function. If your extension is associated with a pallet, you'll probably want to do this via the pallet's existing benchmarking infrastructure. #### `TransactionExtension` Generally: - `pre_dispatch` is now `prepare` and you *should not reexecute the `validate` functionality in there*! - You don't get an account ID any more; you get an origin instead. If you need to presume an account ID, then you can use the trait function `AsSystemOriginSigner::as_system_origin_signer`. - You get an additional ticket, similar to `Pre`, called `Val`. This defines data which is passed from `validate` into `prepare`. This is important since you should not be duplicating logic from `validate` to `prepare`, you need a way of passing your working from the former into the latter. This is it. - This trait takes a `Call` type parameter. `Call` is the runtime call type which used to be an associated type; you can just move it to become a type parameter for your trait impl. - There's no `AccountId` associated type any more. Just remove it. Regarding `validate`: - You get three new parameters in `validate`; all can be ignored when migrating from `SignedExtension`. - `validate` returns a tuple on success; the second item in the tuple is the new ticket type `Self::Val` which gets passed in to `prepare`. If you use any information extracted during `validate` (off-chain and on-chain, non-mutating) in `prepare` (on-chain, mutating) then you can pass it through with this. For the tuple's last item, just return the `origin` argument. Regarding `prepare`: - This is renamed from `pre_dispatch`, but there is one change: - FUNCTIONALITY TO VALIDATE THE TRANSACTION NEED NOT BE DUPLICATED FROM `validate`!! - (This is different to `SignedExtension` which was required to run the same checks in `pre_dispatch` as in `validate`.) Regarding `post_dispatch`: - Since there are no unsigned transactions handled by `TransactionExtension`, `Pre` is always defined, so the first parameter is `Self::Pre` rather than `Option`. If you make use of `SignedExtension::validate_unsigned` or `SignedExtension::pre_dispatch_unsigned`, then: - Just use the regular versions of these functions instead. - Have your logic execute in the case that the `origin` is `None`. - Ensure your transaction creation logic creates a General Transaction rather than a Bare Transaction; this means having to include all `TransactionExtension`s' data. - `ValidateUnsigned` can still be used (for now) if you need to be able to construct transactions which contain none of the extension data, however these will be phased out in stage 2 of the Transactions Horizon, so you should consider moving to an extension-centric design. --------- Signed-off-by: georgepisaltu Co-authored-by: Guillaume Thiolliere Co-authored-by: Branislav Kontur --- Cargo.lock | 77 +- Cargo.toml | 4 + bridges/bin/runtime-common/Cargo.toml | 3 + bridges/bin/runtime-common/src/extensions.rs | 168 ++-- bridges/bin/runtime-common/src/mock.rs | 1 - .../chain-bridge-hub-cumulus/src/lib.rs | 4 +- .../chains/chain-bridge-hub-rococo/src/lib.rs | 4 +- .../chain-bridge-hub-westend/src/lib.rs | 4 +- bridges/chains/chain-kusama/src/lib.rs | 4 +- .../chains/chain-polkadot-bulletin/src/lib.rs | 47 +- bridges/chains/chain-polkadot/src/lib.rs | 4 +- bridges/chains/chain-rococo/src/lib.rs | 4 +- bridges/chains/chain-westend/src/lib.rs | 4 +- bridges/modules/relayers/Cargo.toml | 1 + bridges/modules/relayers/src/extension/mod.rs | 229 +++-- .../relayers/src/extension/priority.rs | 36 +- bridges/primitives/polkadot-core/src/lib.rs | 37 +- bridges/primitives/relayers/src/extension.rs | 2 +- bridges/primitives/runtime/src/extensions.rs | 128 ++- .../lib-substrate-relay/src/messages/mod.rs | 4 +- .../pallets/ethereum-client/Cargo.toml | 8 +- .../ethereum-client/fixtures/Cargo.toml | 4 +- .../pallets/inbound-queue/fixtures/Cargo.toml | 4 +- .../outbound-queue/merkle-tree/Cargo.toml | 7 +- .../src/validate_block/implementation.rs | 11 +- .../assets/asset-hub-rococo/Cargo.toml | 2 + .../assets/asset-hub-rococo/src/lib.rs | 81 +- .../src/weights/frame_system_extensions.rs | 132 +++ .../asset-hub-rococo/src/weights/mod.rs | 3 + .../pallet_asset_conversion_tx_payment.rs | 92 ++ .../src/weights/pallet_transaction_payment.rs | 67 ++ .../assets/asset-hub-westend/Cargo.toml | 2 + .../assets/asset-hub-westend/src/lib.rs | 86 +- .../src/weights/frame_system_extensions.rs | 132 +++ .../asset-hub-westend/src/weights/mod.rs | 3 + .../pallet_asset_conversion_tx_payment.rs | 92 ++ .../src/weights/pallet_transaction_payment.rs | 67 ++ .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 1 + .../src/bridge_to_bulletin_config.rs | 8 +- .../src/bridge_to_westend_config.rs | 7 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 31 +- .../src/weights/frame_system_extensions.rs | 132 +++ .../bridge-hub-rococo/src/weights/mod.rs | 2 + .../src/weights/pallet_transaction_payment.rs | 67 ++ .../bridge-hub-rococo/tests/snowbridge.rs | 10 +- .../bridge-hub-rococo/tests/tests.rs | 34 +- .../bridge-hubs/bridge-hub-westend/Cargo.toml | 1 + .../src/bridge_to_rococo_config.rs | 7 +- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 31 +- .../src/weights/frame_system_extensions.rs | 132 +++ .../bridge-hub-westend/src/weights/mod.rs | 2 + .../src/weights/pallet_transaction_payment.rs | 67 ++ .../bridge-hub-westend/tests/snowbridge.rs | 6 +- .../bridge-hub-westend/tests/tests.rs | 13 +- .../test-utils/src/test_cases/mod.rs | 8 +- .../collectives-westend/Cargo.toml | 1 + .../collectives-westend/src/lib.rs | 12 +- .../src/weights/frame_system_extensions.rs | 132 +++ .../collectives-westend/src/weights/mod.rs | 2 + .../src/weights/pallet_transaction_payment.rs | 67 ++ .../contracts/contracts-rococo/Cargo.toml | 1 + .../contracts/contracts-rococo/src/lib.rs | 10 +- .../coretime/coretime-rococo/Cargo.toml | 1 + .../coretime/coretime-rococo/src/lib.rs | 9 +- .../src/weights/frame_system_extensions.rs | 132 +++ .../coretime-rococo/src/weights/mod.rs | 2 + .../src/weights/pallet_transaction_payment.rs | 67 ++ .../coretime/coretime-westend/Cargo.toml | 1 + .../coretime/coretime-westend/src/lib.rs | 9 +- .../src/weights/frame_system_extensions.rs | 132 +++ .../coretime-westend/src/weights/mod.rs | 2 + .../src/weights/pallet_transaction_payment.rs | 67 ++ .../glutton/glutton-westend/src/lib.rs | 9 +- .../src/weights/frame_system_extensions.rs | 130 +++ .../runtimes/people/people-rococo/Cargo.toml | 1 + .../runtimes/people/people-rococo/src/lib.rs | 9 +- .../src/weights/frame_system_extensions.rs | 132 +++ .../people/people-rococo/src/weights/mod.rs | 2 + .../src/weights/pallet_transaction_payment.rs | 67 ++ .../runtimes/people/people-westend/Cargo.toml | 1 + .../runtimes/people/people-westend/src/lib.rs | 8 +- .../src/weights/frame_system_extensions.rs | 132 +++ .../people/people-westend/src/weights/mod.rs | 2 + .../src/weights/pallet_transaction_payment.rs | 67 ++ .../parachains/runtimes/test-utils/src/lib.rs | 2 +- .../runtimes/testing/penpal/Cargo.toml | 1 + .../runtimes/testing/penpal/src/lib.rs | 26 +- .../testing/rococo-parachain/Cargo.toml | 1 + .../testing/rococo-parachain/src/lib.rs | 7 +- cumulus/polkadot-omni-node/lib/Cargo.toml | 1 + .../storage-weight-reclaim/Cargo.toml | 4 +- .../storage-weight-reclaim/src/lib.rs | 726 +------------- .../storage-weight-reclaim/src/tests.rs | 706 ++++++++++++++ cumulus/test/client/Cargo.toml | 1 + cumulus/test/client/src/lib.rs | 13 +- cumulus/test/runtime/src/lib.rs | 9 +- cumulus/test/service/Cargo.toml | 1 + cumulus/test/service/src/bench_utils.rs | 14 +- cumulus/test/service/src/lib.rs | 9 +- docs/sdk/Cargo.toml | 1 + docs/sdk/src/guides/enable_pov_reclaim.rs | 4 +- .../src/reference_docs/extrinsic_encoding.rs | 192 ++-- .../src/reference_docs/frame_runtime_types.rs | 2 +- docs/sdk/src/reference_docs/mod.rs | 5 +- .../src/reference_docs/signed_extensions.rs | 133 +-- .../reference_docs/transaction_extensions.rs | 103 ++ polkadot/node/service/Cargo.toml | 6 +- polkadot/node/service/src/benchmarking.rs | 18 +- polkadot/node/test/service/Cargo.toml | 1 + polkadot/node/test/service/src/lib.rs | 11 +- polkadot/runtime/common/Cargo.toml | 1 + .../runtime/common/src/assigned_slots/mod.rs | 13 +- polkadot/runtime/common/src/claims.rs | 166 ++-- .../runtime/common/src/integration_tests.rs | 13 +- .../runtime/common/src/paras_registrar/mod.rs | 13 +- .../parachains/src/disputes/slashing.rs | 7 +- polkadot/runtime/parachains/src/mock.rs | 13 +- polkadot/runtime/parachains/src/paras/mod.rs | 7 +- polkadot/runtime/rococo/Cargo.toml | 1 + .../constants/src/weights/block_weights.rs | 29 +- .../src/weights/extrinsic_weights.rs | 29 +- polkadot/runtime/rococo/src/lib.rs | 79 +- .../weights/frame_benchmarking_baseline.rs | 43 +- .../rococo/src/weights/frame_system.rs | 103 +- .../src/weights/frame_system_extensions.rs | 134 +++ polkadot/runtime/rococo/src/weights/mod.rs | 2 + .../rococo/src/weights/pallet_asset_rate.rs | 63 +- .../src/weights/pallet_balances_balances.rs | 12 +- ...allet_balances_nis_counterpart_balances.rs | 12 +- .../rococo/src/weights/pallet_bounties.rs | 218 +++-- .../src/weights/pallet_child_bounties.rs | 181 +++- .../src/weights/pallet_conviction_voting.rs | 196 ++-- .../rococo/src/weights/pallet_identity.rs | 409 ++++---- .../rococo/src/weights/pallet_indices.rs | 73 +- .../src/weights/pallet_message_queue.rs | 168 ++-- .../rococo/src/weights/pallet_multisig.rs | 123 +-- .../runtime/rococo/src/weights/pallet_nis.rs | 243 +++-- .../rococo/src/weights/pallet_preimage.rs | 252 ++--- .../rococo/src/weights/pallet_proxy.rs | 195 ++-- .../src/weights/pallet_ranked_collective.rs | 66 +- .../rococo/src/weights/pallet_recovery.rs | 186 ++++ .../pallet_referenda_fellowship_referenda.rs | 235 ++--- .../src/weights/pallet_referenda_referenda.rs | 283 +++--- .../rococo/src/weights/pallet_scheduler.rs | 146 +-- .../runtime/rococo/src/weights/pallet_sudo.rs | 45 +- .../rococo/src/weights/pallet_timestamp.rs | 35 +- .../src/weights/pallet_transaction_payment.rs | 68 ++ .../rococo/src/weights/pallet_treasury.rs | 183 ++-- .../rococo/src/weights/pallet_utility.rs | 47 +- .../rococo/src/weights/pallet_vesting.rs | 254 ++--- .../rococo/src/weights/pallet_whitelist.rs | 68 +- .../runtime/rococo/src/weights/pallet_xcm.rs | 90 +- .../weights/pallet_xcm_benchmarks_fungible.rs | 191 ++++ .../weights/pallet_xcm_benchmarks_generic.rs | 347 +++++++ .../polkadot_runtime_common_assigned_slots.rs | 68 +- .../polkadot_runtime_common_auctions.rs | 129 +-- .../weights/polkadot_runtime_common_claims.rs | 148 +-- .../polkadot_runtime_common_crowdloan.rs | 219 +++-- ...lkadot_runtime_common_identity_migrator.rs | 83 +- ...polkadot_runtime_common_paras_registrar.rs | 269 +++--- .../weights/polkadot_runtime_common_slots.rs | 119 ++- ...lkadot_runtime_parachains_configuration.rs | 48 +- .../polkadot_runtime_parachains_disputes.rs | 19 +- ...polkadot_runtime_parachains_initializer.rs | 23 +- .../polkadot_runtime_parachains_on_demand.rs | 8 +- .../polkadot_runtime_parachains_paras.rs | 344 +++---- .../src/weights/runtime_common_coretime.rs | 86 ++ polkadot/runtime/test-runtime/Cargo.toml | 1 + polkadot/runtime/test-runtime/src/lib.rs | 58 +- polkadot/runtime/westend/Cargo.toml | 1 + polkadot/runtime/westend/src/lib.rs | 78 +- polkadot/runtime/westend/src/tests.rs | 2 +- .../src/weights/frame_system_extensions.rs | 131 +++ polkadot/runtime/westend/src/weights/mod.rs | 2 + .../westend/src/weights/pallet_sudo.rs | 11 + .../src/weights/pallet_transaction_payment.rs | 68 ++ .../src/generic/benchmarking.rs | 2 +- polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs | 2 +- polkadot/xcm/pallet-xcm/src/lib.rs | 4 +- polkadot/xcm/xcm-builder/Cargo.toml | 1 + polkadot/xcm/xcm-builder/src/tests/mock.rs | 4 +- .../xcm/xcm-builder/src/tests/pay/mock.rs | 12 +- .../xcm-executor/integration-tests/src/lib.rs | 2 +- polkadot/xcm/xcm-executor/src/lib.rs | 2 +- polkadot/xcm/xcm-runtime-apis/tests/mock.rs | 13 +- .../xcm/xcm-simulator/fuzzer/src/parachain.rs | 4 +- .../xcm-simulator/fuzzer/src/relay_chain.rs | 4 +- prdoc/pr_3685.prdoc | 300 ++++++ substrate/.maintain/frame-weight-template.hbs | 2 +- .../bin/node/cli/benches/block_production.rs | 7 +- substrate/bin/node/cli/benches/executor.rs | 6 +- substrate/bin/node/cli/src/service.rs | 16 +- substrate/bin/node/cli/tests/basic.rs | 141 +-- substrate/bin/node/cli/tests/fees.rs | 14 +- .../bin/node/cli/tests/submit_transaction.rs | 13 +- substrate/bin/node/runtime/src/lib.rs | 150 ++- substrate/bin/node/testing/src/bench.rs | 29 +- substrate/bin/node/testing/src/keyring.rs | 28 +- .../client/api/src/notifications/tests.rs | 4 +- substrate/client/db/benches/state_access.rs | 4 +- substrate/client/db/src/lib.rs | 261 +++-- substrate/client/db/src/utils.rs | 8 +- .../network-gossip/src/state_machine.rs | 4 +- substrate/client/network/sync/src/blocks.rs | 4 +- .../fork_aware_txpool/fork_aware_txpool.rs | 5 +- .../single_state_txpool.rs | 5 +- substrate/frame/Cargo.toml | 5 +- substrate/frame/alliance/src/tests.rs | 6 +- .../frame/asset-conversion/src/weights.rs | 1 + substrate/frame/assets/src/tests.rs | 4 +- substrate/frame/babe/src/equivocation.rs | 7 +- substrate/frame/babe/src/mock.rs | 13 +- substrate/frame/babe/src/tests.rs | 2 +- substrate/frame/balances/Cargo.toml | 1 + .../balances/src/tests/currency_tests.rs | 17 +- substrate/frame/balances/src/tests/mod.rs | 9 +- substrate/frame/beefy/src/equivocation.rs | 7 +- substrate/frame/beefy/src/mock.rs | 13 +- substrate/frame/collective/src/lib.rs | 12 +- substrate/frame/collective/src/tests.rs | 28 +- substrate/frame/contracts/src/wasm/runtime.rs | 4 +- .../election-provider-multi-phase/src/lib.rs | 4 +- .../election-provider-multi-phase/src/mock.rs | 13 +- .../src/unsigned.rs | 14 +- .../test-staking-e2e/src/mock.rs | 17 +- substrate/frame/elections-phragmen/src/lib.rs | 2 +- substrate/frame/examples/Cargo.toml | 3 + .../authorization-tx-extension/Cargo.toml | 62 ++ .../src/extensions.rs | 132 +++ .../authorization-tx-extension/src/lib.rs | 158 ++++ .../authorization-tx-extension/src/mock.rs | 142 +++ .../authorization-tx-extension/src/tests.rs | 269 ++++++ substrate/frame/examples/basic/src/lib.rs | 98 +- substrate/frame/examples/basic/src/tests.rs | 15 +- .../frame/examples/offchain-worker/src/lib.rs | 11 +- .../examples/offchain-worker/src/tests.rs | 50 +- substrate/frame/examples/src/lib.rs | 8 +- substrate/frame/examples/tasks/src/lib.rs | 15 +- substrate/frame/examples/tasks/src/mock.rs | 13 +- substrate/frame/examples/tasks/src/tests.rs | 3 +- substrate/frame/examples/tasks/src/weights.rs | 78 +- substrate/frame/executive/src/tests.rs | 269 ++++-- substrate/frame/grandpa/src/equivocation.rs | 7 +- substrate/frame/grandpa/src/mock.rs | 13 +- substrate/frame/grandpa/src/tests.rs | 2 +- substrate/frame/im-online/src/lib.rs | 7 +- substrate/frame/im-online/src/mock.rs | 21 +- substrate/frame/im-online/src/tests.rs | 4 +- substrate/frame/lottery/src/lib.rs | 2 +- .../frame/metadata-hash-extension/src/lib.rs | 41 +- .../metadata-hash-extension/src/tests.rs | 14 +- substrate/frame/mixnet/src/lib.rs | 7 +- substrate/frame/multisig/src/lib.rs | 4 +- substrate/frame/multisig/src/tests.rs | 16 +- .../frame/offences/benchmarking/src/mock.rs | 17 +- substrate/frame/proxy/src/lib.rs | 4 +- substrate/frame/recovery/src/lib.rs | 2 +- substrate/frame/revive/src/wasm/runtime.rs | 4 +- substrate/frame/sassafras/src/lib.rs | 7 +- substrate/frame/sassafras/src/mock.rs | 17 +- substrate/frame/scheduler/src/lib.rs | 2 +- substrate/frame/src/lib.rs | 6 +- substrate/frame/sudo/src/benchmarking.rs | 34 +- substrate/frame/sudo/src/extension.rs | 69 +- substrate/frame/sudo/src/lib.rs | 8 +- substrate/frame/sudo/src/tests.rs | 2 +- substrate/frame/sudo/src/weights.rs | 21 + substrate/frame/support/Cargo.toml | 4 +- .../src/construct_runtime/expand/inherent.rs | 20 +- .../src/construct_runtime/expand/metadata.rs | 20 +- .../src/construct_runtime/expand/origin.rs | 20 + .../procedural/src/pallet/expand/call.rs | 3 +- substrate/frame/support/src/dispatch.rs | 555 ++++++++++- substrate/frame/support/src/lib.rs | 7 +- substrate/frame/support/src/traits.rs | 8 +- .../frame/support/src/traits/dispatch.rs | 21 +- substrate/frame/support/src/traits/misc.rs | 70 +- substrate/frame/support/test/Cargo.toml | 5 +- .../undefined_inherent_part.stderr | 5 +- .../support/test/tests/enum_deprecation.rs | 8 +- substrate/frame/support/test/tests/pallet.rs | 200 ++-- .../support/test/tests/pallet_instance.rs | 10 +- substrate/frame/support/test/tests/runtime.rs | 22 +- .../test/tests/runtime_legacy_ordering.rs | 22 +- .../system/benchmarking/src/extensions.rs | 248 +++++ .../frame/system/benchmarking/src/lib.rs | 1 + .../frame/system/benchmarking/src/mock.rs | 38 +- .../system/src/extensions/check_genesis.rs | 31 +- .../system/src/extensions/check_mortality.rs | 88 +- .../src/extensions/check_non_zero_sender.rs | 89 +- .../system/src/extensions/check_nonce.rs | 334 +++++-- .../src/extensions/check_spec_version.rs | 30 +- .../system/src/extensions/check_tx_version.rs | 29 +- .../system/src/extensions/check_weight.rs | 356 ++++--- substrate/frame/system/src/extensions/mod.rs | 3 + .../frame/system/src/extensions/weights.rs | 217 +++++ substrate/frame/system/src/lib.rs | 43 +- substrate/frame/system/src/offchain.rs | 103 +- substrate/frame/system/src/tests.rs | 55 +- .../frame/transaction-payment/Cargo.toml | 9 + .../asset-conversion-tx-payment/Cargo.toml | 12 + .../asset-conversion-tx-payment/README.md | 2 +- .../src/benchmarking.rs | 127 +++ .../asset-conversion-tx-payment/src/lib.rs | 301 ++++-- .../asset-conversion-tx-payment/src/mock.rs | 96 +- .../src/payment.rs | 56 +- .../asset-conversion-tx-payment/src/tests.rs | 308 +++--- .../src/weights.rs | 150 +++ .../asset-tx-payment/Cargo.toml | 1 + .../asset-tx-payment/README.md | 2 +- .../asset-tx-payment/src/benchmarking.rs | 131 +++ .../asset-tx-payment/src/lib.rs | 293 ++++-- .../asset-tx-payment/src/mock.rs | 79 +- .../asset-tx-payment/src/payment.rs | 38 + .../asset-tx-payment/src/tests.rs | 268 +++--- .../asset-tx-payment/src/weights.rs | 146 +++ .../skip-feeless-payment/src/lib.rs | 103 +- .../skip-feeless-payment/src/mock.rs | 44 +- .../skip-feeless-payment/src/tests.rs | 44 +- .../transaction-payment/src/benchmarking.rs | 86 ++ .../frame/transaction-payment/src/lib.rs | 218 +++-- .../frame/transaction-payment/src/mock.rs | 19 + .../frame/transaction-payment/src/payment.rs | 85 +- .../frame/transaction-payment/src/tests.rs | 581 ++++++------ .../frame/transaction-payment/src/types.rs | 2 +- .../frame/transaction-payment/src/weights.rs | 92 ++ substrate/frame/utility/src/lib.rs | 6 +- substrate/frame/utility/src/tests.rs | 44 +- substrate/frame/verify-signature/Cargo.toml | 70 ++ substrate/frame/verify-signature/README.md | 19 + .../verify-signature/src/benchmarking.rs | 65 ++ .../frame/verify-signature/src/extension.rs | 157 +++ substrate/frame/verify-signature/src/lib.rs | 68 ++ substrate/frame/verify-signature/src/tests.rs | 132 +++ .../frame/verify-signature/src/weights.rs | 75 ++ substrate/frame/whitelist/src/benchmarking.rs | 2 +- substrate/frame/whitelist/src/lib.rs | 4 +- substrate/frame/whitelist/src/tests.rs | 6 +- substrate/primitives/consensus/pow/Cargo.toml | 7 +- .../primitives/consensus/slots/Cargo.toml | 7 +- substrate/primitives/inherents/src/lib.rs | 4 +- substrate/primitives/metadata-ir/src/lib.rs | 2 +- substrate/primitives/metadata-ir/src/types.rs | 23 +- substrate/primitives/metadata-ir/src/v14.rs | 12 +- substrate/primitives/metadata-ir/src/v15.rs | 10 +- substrate/primitives/runtime/Cargo.toml | 2 + .../primitives/runtime/src/generic/block.rs | 2 +- .../runtime/src/generic/checked_extrinsic.rs | 116 ++- .../primitives/runtime/src/generic/mod.rs | 7 +- .../src/generic/unchecked_extrinsic.rs | 890 +++++++++++++----- substrate/primitives/runtime/src/lib.rs | 11 +- substrate/primitives/runtime/src/testing.rs | 233 +---- .../runtime/src/{traits.rs => traits/mod.rs} | 343 ++++--- .../as_transaction_extension.rs | 130 +++ .../dispatch_transaction.rs | 154 +++ .../src/traits/transaction_extension/mod.rs | 635 +++++++++++++ .../runtime/src/transaction_validity.rs | 10 +- substrate/primitives/storage/Cargo.toml | 7 +- .../primitives/test-primitives/src/lib.rs | 15 +- substrate/primitives/weights/Cargo.toml | 4 +- substrate/scripts/run_all_benchmarks.sh | 13 + substrate/test-utils/runtime/src/extrinsic.rs | 22 +- substrate/test-utils/runtime/src/lib.rs | 97 +- .../benchmarking-cli/src/pallet/template.hbs | 4 + .../frame/remote-externalities/src/lib.rs | 5 +- substrate/utils/frame/rpc/client/src/lib.rs | 5 +- templates/minimal/runtime/src/lib.rs | 6 +- .../parachain/runtime/src/configs/mod.rs | 1 + templates/parachain/runtime/src/lib.rs | 6 +- templates/solochain/node/Cargo.toml | 1 + templates/solochain/node/src/benchmarking.rs | 6 +- templates/solochain/runtime/Cargo.toml | 14 +- templates/solochain/runtime/src/apis.rs | 2 + templates/solochain/runtime/src/benchmarks.rs | 1 + .../solochain/runtime/src/configs/mod.rs | 1 + templates/solochain/runtime/src/lib.rs | 8 +- umbrella/Cargo.toml | 12 +- umbrella/src/lib.rs | 4 + 378 files changed, 17762 insertions(+), 6841 deletions(-) create mode 100644 cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion_tx_payment.rs create mode 100644 cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_transaction_payment.rs create mode 100644 cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion_tx_payment.rs create mode 100644 cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_transaction_payment.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_transaction_payment.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_transaction_payment.rs create mode 100644 cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_transaction_payment.rs create mode 100644 cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_transaction_payment.rs create mode 100644 cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_transaction_payment.rs create mode 100644 cumulus/parachains/runtimes/glutton/glutton-westend/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/people/people-rococo/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_transaction_payment.rs create mode 100644 cumulus/parachains/runtimes/people/people-westend/src/weights/frame_system_extensions.rs create mode 100644 cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_transaction_payment.rs create mode 100644 cumulus/primitives/storage-weight-reclaim/src/tests.rs create mode 100644 docs/sdk/src/reference_docs/transaction_extensions.rs create mode 100644 polkadot/runtime/rococo/src/weights/frame_system_extensions.rs create mode 100644 polkadot/runtime/rococo/src/weights/pallet_recovery.rs create mode 100644 polkadot/runtime/rococo/src/weights/pallet_transaction_payment.rs create mode 100644 polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_fungible.rs create mode 100644 polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_generic.rs create mode 100644 polkadot/runtime/rococo/src/weights/runtime_common_coretime.rs create mode 100644 polkadot/runtime/westend/src/weights/frame_system_extensions.rs create mode 100644 polkadot/runtime/westend/src/weights/pallet_transaction_payment.rs create mode 100644 prdoc/pr_3685.prdoc create mode 100644 substrate/frame/examples/authorization-tx-extension/Cargo.toml create mode 100644 substrate/frame/examples/authorization-tx-extension/src/extensions.rs create mode 100644 substrate/frame/examples/authorization-tx-extension/src/lib.rs create mode 100644 substrate/frame/examples/authorization-tx-extension/src/mock.rs create mode 100644 substrate/frame/examples/authorization-tx-extension/src/tests.rs create mode 100644 substrate/frame/system/benchmarking/src/extensions.rs create mode 100644 substrate/frame/system/src/extensions/weights.rs create mode 100644 substrate/frame/transaction-payment/asset-conversion-tx-payment/src/benchmarking.rs create mode 100644 substrate/frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs create mode 100644 substrate/frame/transaction-payment/asset-tx-payment/src/benchmarking.rs create mode 100644 substrate/frame/transaction-payment/asset-tx-payment/src/weights.rs create mode 100644 substrate/frame/transaction-payment/src/benchmarking.rs create mode 100644 substrate/frame/transaction-payment/src/weights.rs create mode 100644 substrate/frame/verify-signature/Cargo.toml create mode 100644 substrate/frame/verify-signature/README.md create mode 100644 substrate/frame/verify-signature/src/benchmarking.rs create mode 100644 substrate/frame/verify-signature/src/extension.rs create mode 100644 substrate/frame/verify-signature/src/lib.rs create mode 100644 substrate/frame/verify-signature/src/tests.rs create mode 100644 substrate/frame/verify-signature/src/weights.rs rename substrate/primitives/runtime/src/{traits.rs => traits/mod.rs} (91%) create mode 100644 substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs create mode 100644 substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs create mode 100644 substrate/primitives/runtime/src/traits/transaction_extension/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 846237e0374..4bb17e66dca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1234,7 +1234,7 @@ dependencies = [ "futures-lite 2.3.0", "parking", "polling 3.4.0", - "rustix 0.38.21", + "rustix 0.38.25", "slab", "tracing", "windows-sys 0.52.0", @@ -1316,7 +1316,7 @@ dependencies = [ "cfg-if", "event-listener 5.2.0", "futures-lite 2.3.0", - "rustix 0.38.21", + "rustix 0.38.25", "tracing", ] @@ -1332,7 +1332,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.21", + "rustix 0.38.25", "signal-hook-registry", "slab", "windows-sys 0.52.0", @@ -2559,6 +2559,7 @@ dependencies = [ "sp-runtime 31.0.1", "sp-std 14.0.0", "sp-trie 29.0.0", + "sp-weights 27.0.0", "staging-xcm", "static_assertions", "tuplex", @@ -4554,6 +4555,7 @@ dependencies = [ "cumulus-primitives-proof-size-hostfunction", "cumulus-test-runtime", "docify", + "frame-benchmarking", "frame-support", "frame-system", "log", @@ -6654,7 +6656,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29f9df8a11882c4e3335eb2d18a0137c505d9ca927470b0cac9c6f0ae07d28f7" dependencies = [ - "rustix 0.38.21", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -7845,7 +7847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.9", - "rustix 0.38.21", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -9203,9 +9205,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "lioness" @@ -10690,6 +10692,7 @@ dependencies = [ name = "pallet-asset-conversion-tx-payment" version = "10.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-asset-conversion", @@ -11511,6 +11514,24 @@ dependencies = [ "substrate-test-utils", ] +[[package]] +name = "pallet-example-authorization-tx-extension" +version = "1.0.0" +dependencies = [ + "docify", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-verify-signature", + "parity-scale-codec", + "scale-info", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-keyring", + "sp-runtime 31.0.1", +] + [[package]] name = "pallet-example-basic" version = "27.0.0" @@ -11636,6 +11657,7 @@ version = "4.0.0-dev" dependencies = [ "pallet-default-config-example", "pallet-dev-mode", + "pallet-example-authorization-tx-extension", "pallet-example-basic", "pallet-example-frame-crate", "pallet-example-kitchensink", @@ -12787,6 +12809,7 @@ dependencies = [ name = "pallet-transaction-payment" version = "28.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-balances", @@ -12918,6 +12941,25 @@ dependencies = [ "sp-runtime 31.0.1", ] +[[package]] +name = "pallet-verify-signature" +version = "1.0.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-collective", + "pallet-root-testing", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-runtime 31.0.1", + "sp-weights 27.0.0", +] + [[package]] name = "pallet-vesting" version = "28.0.0" @@ -15286,6 +15328,7 @@ dependencies = [ "pallet-tx-pause", "pallet-uniques", "pallet-utility", + "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", @@ -15521,6 +15564,7 @@ dependencies = [ "pallet-contracts", "pallet-default-config-example", "pallet-democracy", + "pallet-example-authorization-tx-extension", "pallet-example-offchain-worker", "pallet-example-single-block-migrations", "pallet-examples", @@ -16281,7 +16325,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.21", + "rustix 0.38.25", "tracing", "windows-sys 0.52.0", ] @@ -16577,7 +16621,7 @@ dependencies = [ "hex", "lazy_static", "procfs-core", - "rustix 0.38.21", + "rustix 0.38.25", ] [[package]] @@ -17996,14 +18040,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.10", + "linux-raw-sys 0.4.11", "windows-sys 0.48.0", ] @@ -21863,7 +21907,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.4.1" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +source = "git+https://github.com/paritytech/polkadot-sdk#838a534da874cf6071fba1df07643c6c5b033ae0" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -22374,6 +22418,7 @@ dependencies = [ "sp-weights 27.0.0", "substrate-test-runtime-client", "tracing", + "tuplex", "zstd 0.12.4", ] @@ -22538,7 +22583,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf590a34fca09b72dc5f" +source = "git+https://github.com/paritytech/polkadot-sdk#838a534da874cf6071fba1df07643c6c5b033ae0" dependencies = [ "Inflector", "proc-macro-crate 1.3.1", @@ -24359,7 +24404,7 @@ dependencies = [ "cfg-if", "fastrand 2.1.0", "redox_syscall 0.4.1", - "rustix 0.38.21", + "rustix 0.38.25", "windows-sys 0.48.0", ] @@ -24389,7 +24434,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.38.21", + "rustix 0.38.25", "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index 40d3615fed1..b357b99be11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -347,6 +347,7 @@ members = [ "substrate/frame/election-provider-support/solution-type/fuzzer", "substrate/frame/elections-phragmen", "substrate/frame/examples", + "substrate/frame/examples/authorization-tx-extension", "substrate/frame/examples/basic", "substrate/frame/examples/default-config", "substrate/frame/examples/dev-mode", @@ -442,6 +443,7 @@ members = [ "substrate/frame/tx-pause", "substrate/frame/uniques", "substrate/frame/utility", + "substrate/frame/verify-signature", "substrate/frame/vesting", "substrate/frame/whitelist", "substrate/primitives/api", @@ -915,6 +917,7 @@ pallet-dev-mode = { path = "substrate/frame/examples/dev-mode", default-features pallet-election-provider-multi-phase = { path = "substrate/frame/election-provider-multi-phase", default-features = false } pallet-election-provider-support-benchmarking = { path = "substrate/frame/election-provider-support/benchmarking", default-features = false } pallet-elections-phragmen = { path = "substrate/frame/elections-phragmen", default-features = false } +pallet-example-authorization-tx-extension = { path = "substrate/frame/examples/authorization-tx-extension", default-features = false } pallet-example-basic = { path = "substrate/frame/examples/basic", default-features = false } pallet-example-frame-crate = { path = "substrate/frame/examples/frame-crate", default-features = false } pallet-example-kitchensink = { path = "substrate/frame/examples/kitchensink", default-features = false } @@ -991,6 +994,7 @@ pallet-treasury = { path = "substrate/frame/treasury", default-features = false pallet-tx-pause = { default-features = false, path = "substrate/frame/tx-pause" } pallet-uniques = { path = "substrate/frame/uniques", default-features = false } pallet-utility = { path = "substrate/frame/utility", default-features = false } +pallet-verify-signature = { path = "substrate/frame/verify-signature", default-features = false } pallet-vesting = { path = "substrate/frame/vesting", default-features = false } pallet-whitelist = { path = "substrate/frame/whitelist", default-features = false } pallet-xcm = { path = "polkadot/xcm/pallet-xcm", default-features = false } diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index b8835d55f0d..37b56140c28 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -39,6 +39,7 @@ sp-io = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } sp-trie = { optional = true, workspace = true } +sp-weights = { workspace = true } # Polkadot dependencies xcm = { workspace = true } @@ -80,6 +81,7 @@ std = [ "sp-runtime/std", "sp-std/std", "sp-trie/std", + "sp-weights/std", "tuplex/std", "xcm/std", ] @@ -93,6 +95,7 @@ runtime-benchmarks = [ "pallet-bridge-messages/test-helpers", "pallet-bridge-parachains/runtime-benchmarks", "pallet-bridge-relayers/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-trie", diff --git a/bridges/bin/runtime-common/src/extensions.rs b/bridges/bin/runtime-common/src/extensions.rs index dced5023947..19d1554c668 100644 --- a/bridges/bin/runtime-common/src/extensions.rs +++ b/bridges/bin/runtime-common/src/extensions.rs @@ -47,8 +47,7 @@ pub trait BridgeRuntimeFilterCall { /// Data that may be passed from the validate to `post_dispatch`. type ToPostDispatch; /// Called during validation. Needs to checks whether a runtime call, submitted - /// by the `who` is valid. `who` may be `None` if transaction is not signed - /// by a regular account. + /// by the `who` is valid. Transactions not signed are not validated. fn validate(who: &AccountId, call: &Call) -> (Self::ToPostDispatch, TransactionValidity); /// Called after transaction is dispatched. fn post_dispatch(_who: &AccountId, _has_failed: bool, _to_post_dispatch: Self::ToPostDispatch) { @@ -274,12 +273,10 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { ($call:ty, $account_id:ty, $($filter_call:ty),*) => { #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, sp_runtime::RuntimeDebug, scale_info::TypeInfo)] pub struct BridgeRejectObsoleteHeadersAndMessages; - impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { + impl sp_runtime::traits::TransactionExtension<$call> for BridgeRejectObsoleteHeadersAndMessages { const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; - type AccountId = $account_id; - type Call = $call; - type AdditionalSigned = (); - type Pre = ( + type Implicit = (); + type Val = Option<( $account_id, ( $( <$filter_call as $crate::extensions::BridgeRuntimeFilterCall< @@ -287,72 +284,75 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { $call, >>::ToPostDispatch, )* ), - ); + )>; + type Pre = Self::Val; - fn additional_signed(&self) -> sp_std::result::Result< - (), - sp_runtime::transaction_validity::TransactionValidityError, - > { - Ok(()) + fn weight(&self, _: &$call) -> frame_support::pallet_prelude::Weight { + frame_support::pallet_prelude::Weight::zero() } - #[allow(unused_variables)] fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, + origin: <$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin, + call: &$call, + _info: &sp_runtime::traits::DispatchInfoOf<$call>, _len: usize, - ) -> sp_runtime::transaction_validity::TransactionValidity { + _self_implicit: Self::Implicit, + _inherited_implication: &impl codec::Encode, + ) -> Result< + ( + sp_runtime::transaction_validity::ValidTransaction, + Self::Val, + <$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin, + ), sp_runtime::transaction_validity::TransactionValidityError + > { + use $crate::extensions::__private::tuplex::PushBack; + use sp_runtime::traits::AsSystemOriginSigner; + + let Some(who) = origin.as_system_origin_signer() else { + return Ok((Default::default(), None, origin)); + }; + + let to_post_dispatch = (); let tx_validity = sp_runtime::transaction_validity::ValidTransaction::default(); - let to_prepare = (); $( let (from_validate, call_filter_validity) = < $filter_call as $crate::extensions::BridgeRuntimeFilterCall< - Self::AccountId, + $account_id, $call, - >>::validate(&who, call); + >>::validate(who, call); + let to_post_dispatch = to_post_dispatch.push_back(from_validate); let tx_validity = tx_validity.combine_with(call_filter_validity?); )* - Ok(tx_validity) + Ok((tx_validity, Some((who.clone(), to_post_dispatch)), origin)) } - #[allow(unused_variables)] - fn pre_dispatch( + fn prepare( self, - relayer: &Self::AccountId, - call: &Self::Call, - info: &sp_runtime::traits::DispatchInfoOf, - len: usize, + val: Self::Val, + _origin: &<$call as sp_runtime::traits::Dispatchable>::RuntimeOrigin, + _call: &$call, + _info: &sp_runtime::traits::DispatchInfoOf<$call>, + _len: usize, ) -> Result { - use $crate::extensions::__private::tuplex::PushBack; - - let to_post_dispatch = (); - $( - let (from_validate, call_filter_validity) = < - $filter_call as - $crate::extensions::BridgeRuntimeFilterCall< - $account_id, - $call, - >>::validate(&relayer, call); - let _ = call_filter_validity?; - let to_post_dispatch = to_post_dispatch.push_back(from_validate); - )* - Ok((relayer.clone(), to_post_dispatch)) + Ok(val) } #[allow(unused_variables)] - fn post_dispatch( - to_post_dispatch: Option, - info: &sp_runtime::traits::DispatchInfoOf, - post_info: &sp_runtime::traits::PostDispatchInfoOf, + fn post_dispatch_details( + to_post_dispatch: Self::Pre, + info: &sp_runtime::traits::DispatchInfoOf<$call>, + post_info: &sp_runtime::traits::PostDispatchInfoOf<$call>, len: usize, result: &sp_runtime::DispatchResult, - ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { + ) -> Result { use $crate::extensions::__private::tuplex::PopFront; - let Some((relayer, to_post_dispatch)) = to_post_dispatch else { return Ok(()) }; + let Some((relayer, to_post_dispatch)) = to_post_dispatch else { + return Ok(frame_support::pallet_prelude::Weight::zero()) + }; + let has_failed = result.is_err(); $( let (item, to_post_dispatch) = to_post_dispatch.pop_front(); @@ -363,7 +363,7 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { $call, >>::post_dispatch(&relayer, has_failed, item); )* - Ok(()) + Ok(frame_support::pallet_prelude::Weight::zero()) } } }; @@ -380,11 +380,16 @@ mod tests { use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::HeaderId; use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; + use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{assert_err, assert_ok, traits::fungible::Mutate}; use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet}; use pallet_bridge_parachains::Call as ParachainsCall; + use scale_info::TypeInfo; use sp_runtime::{ - traits::{parameter_types, ConstU64, Header as _, SignedExtension}, + traits::{ + parameter_types, AsSystemOriginSigner, AsTransactionAuthorizedOrigin, ConstU64, + DispatchTransaction, Header as _, TransactionExtension, + }, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, DispatchError, }; @@ -402,12 +407,34 @@ mod tests { ); } + #[derive(Debug, Clone, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen)] pub struct MockCall { data: u32, } + #[derive(Debug, Clone, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen)] + pub struct MockOrigin(pub u64); + + impl AsSystemOriginSigner for MockOrigin { + fn as_system_origin_signer(&self) -> Option<&u64> { + Some(&self.0) + } + } + + impl AsTransactionAuthorizedOrigin for MockOrigin { + fn is_transaction_authorized(&self) -> bool { + true + } + } + + impl From for MockOrigin { + fn from(o: u64) -> Self { + Self(o) + } + } + impl sp_runtime::traits::Dispatchable for MockCall { - type RuntimeOrigin = u64; + type RuntimeOrigin = MockOrigin; type Config = (); type Info = (); type PostInfo = (); @@ -579,12 +606,17 @@ mod tests { run_test(|| { assert_err!( - BridgeRejectObsoleteHeadersAndMessages.validate(&42, &MockCall { data: 1 }, &(), 0), + BridgeRejectObsoleteHeadersAndMessages.validate_only( + 42u64.into(), + &MockCall { data: 1 }, + &(), + 0 + ), InvalidTransaction::Custom(1) ); assert_err!( - BridgeRejectObsoleteHeadersAndMessages.pre_dispatch( - &42, + BridgeRejectObsoleteHeadersAndMessages.validate_and_prepare( + 42u64.into(), &MockCall { data: 1 }, &(), 0 @@ -593,12 +625,17 @@ mod tests { ); assert_err!( - BridgeRejectObsoleteHeadersAndMessages.validate(&42, &MockCall { data: 2 }, &(), 0), + BridgeRejectObsoleteHeadersAndMessages.validate_only( + 42u64.into(), + &MockCall { data: 2 }, + &(), + 0 + ), InvalidTransaction::Custom(2) ); assert_err!( - BridgeRejectObsoleteHeadersAndMessages.pre_dispatch( - &42, + BridgeRejectObsoleteHeadersAndMessages.validate_and_prepare( + 42u64.into(), &MockCall { data: 2 }, &(), 0 @@ -608,37 +645,40 @@ mod tests { assert_eq!( BridgeRejectObsoleteHeadersAndMessages - .validate(&42, &MockCall { data: 3 }, &(), 0) - .unwrap(), + .validate_only(42u64.into(), &MockCall { data: 3 }, &(), 0) + .unwrap() + .0, ValidTransaction { priority: 3, ..Default::default() }, ); assert_eq!( BridgeRejectObsoleteHeadersAndMessages - .pre_dispatch(&42, &MockCall { data: 3 }, &(), 0) + .validate_and_prepare(42u64.into(), &MockCall { data: 3 }, &(), 0) + .unwrap() + .0 .unwrap(), (42, (1, 2)), ); // when post_dispatch is called with `Ok(())`, it is propagated to all "nested" // extensions - assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch( + assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch_details( Some((0, (1, 2))), &(), &(), 0, - &Ok(()) + &Ok(()), )); FirstFilterCall::verify_post_dispatch_called_with(true); SecondFilterCall::verify_post_dispatch_called_with(true); // when post_dispatch is called with `Err(())`, it is propagated to all "nested" // extensions - assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch( + assert_ok!(BridgeRejectObsoleteHeadersAndMessages::post_dispatch_details( Some((0, (1, 2))), &(), &(), 0, - &Err(DispatchError::BadOrigin) + &Err(DispatchError::BadOrigin), )); FirstFilterCall::verify_post_dispatch_called_with(false); SecondFilterCall::verify_post_dispatch_called_with(false); diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index 1d4043fc4b6..6cf04b452da 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -162,7 +162,6 @@ impl pallet_transaction_payment::Config for TestRuntime { MinimumMultiplier, MaximumMultiplier, >; - type RuntimeEvent = RuntimeEvent; } impl pallet_bridge_grandpa::Config for TestRuntime { diff --git a/bridges/chains/chain-bridge-hub-cumulus/src/lib.rs b/bridges/chains/chain-bridge-hub-cumulus/src/lib.rs index a5c90ceba11..f626fa6df01 100644 --- a/bridges/chains/chain-bridge-hub-cumulus/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-cumulus/src/lib.rs @@ -26,7 +26,7 @@ pub use bp_polkadot_core::{ }; use bp_messages::*; -use bp_polkadot_core::SuffixedCommonSignedExtension; +use bp_polkadot_core::SuffixedCommonTransactionExtension; use bp_runtime::extensions::{ BridgeRejectObsoleteHeadersAndMessages, RefundBridgedParachainMessagesSchema, }; @@ -167,7 +167,7 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; /// Signed extension that is used by all bridge hubs. -pub type SignedExtension = SuffixedCommonSignedExtension<( +pub type TransactionExtension = SuffixedCommonTransactionExtension<( BridgeRejectObsoleteHeadersAndMessages, RefundBridgedParachainMessagesSchema, )>; diff --git a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs index 538bc44019f..fda6a5f0b72 100644 --- a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs @@ -109,11 +109,11 @@ frame_support::parameter_types! { /// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) - pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 297_644_174; + pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 297_685_840; /// Transaction fee that is paid at the Rococo BridgeHub for delivering single outbound message confirmation. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_confirmation_transaction` + `33%`) - pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 56_740_432; + pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 56_782_099; } /// Wrapper over `BridgeHubRococo`'s `RuntimeCall` that can be used without a runtime. diff --git a/bridges/chains/chain-bridge-hub-westend/src/lib.rs b/bridges/chains/chain-bridge-hub-westend/src/lib.rs index 7a213fdb28c..e941b584023 100644 --- a/bridges/chains/chain-bridge-hub-westend/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-westend/src/lib.rs @@ -98,11 +98,11 @@ frame_support::parameter_types! { /// Transaction fee that is paid at the Westend BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) - pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 89_293_427_116; + pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 89_305_927_116; /// Transaction fee that is paid at the Westend BridgeHub for delivering single outbound message confirmation. /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_standalone_message_confirmation_transaction` + `33%`) - pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 17_022_177_116; + pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 17_034_677_116; } /// Wrapper over `BridgeHubWestend`'s `RuntimeCall` that can be used without a runtime. diff --git a/bridges/chains/chain-kusama/src/lib.rs b/bridges/chains/chain-kusama/src/lib.rs index dcd0b23abbb..f1f30c4484e 100644 --- a/bridges/chains/chain-kusama/src/lib.rs +++ b/bridges/chains/chain-kusama/src/lib.rs @@ -61,8 +61,8 @@ impl ChainWithGrandpa for Kusama { const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } -// The SignedExtension used by Kusama. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; +// The TransactionExtension used by Kusama. +pub use bp_polkadot_core::CommonTransactionExtension as TransactionExtension; /// Name of the parachains pallet in the Kusama runtime. pub const PARAS_PALLET_NAME: &str = "Paras"; diff --git a/bridges/chains/chain-polkadot-bulletin/src/lib.rs b/bridges/chains/chain-polkadot-bulletin/src/lib.rs index d0093691972..c5c18beb2ca 100644 --- a/bridges/chains/chain-polkadot-bulletin/src/lib.rs +++ b/bridges/chains/chain-polkadot-bulletin/src/lib.rs @@ -25,7 +25,7 @@ use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, extensions::{ CheckEra, CheckGenesis, CheckNonZeroSender, CheckNonce, CheckSpecVersion, CheckTxVersion, - CheckWeight, GenericSignedExtension, GenericSignedExtensionSchema, + CheckWeight, GenericTransactionExtension, GenericTransactionExtensionSchema, }, Chain, ChainId, TransactionEra, }; @@ -38,7 +38,8 @@ use frame_support::{ use frame_system::limits; use scale_info::TypeInfo; use sp_runtime::{ - traits::DispatchInfoOf, transaction_validity::TransactionValidityError, Perbill, StateVersion, + impl_tx_ext_default, traits::Dispatchable, transaction_validity::TransactionValidityError, + Perbill, StateVersion, }; // This chain reuses most of Polkadot primitives. @@ -73,10 +74,10 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; /// This signed extension is used to ensure that the chain transactions are signed by proper -pub type ValidateSigned = GenericSignedExtensionSchema<(), ()>; +pub type ValidateSigned = GenericTransactionExtensionSchema<(), ()>; /// Signed extension schema, used by Polkadot Bulletin. -pub type SignedExtensionSchema = GenericSignedExtension<( +pub type TransactionExtensionSchema = GenericTransactionExtension<( ( CheckNonZeroSender, CheckSpecVersion, @@ -89,34 +90,30 @@ pub type SignedExtensionSchema = GenericSignedExtension<( ValidateSigned, )>; -/// Signed extension, used by Polkadot Bulletin. +/// Transaction extension, used by Polkadot Bulletin. #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub struct SignedExtension(SignedExtensionSchema); +pub struct TransactionExtension(TransactionExtensionSchema); -impl sp_runtime::traits::SignedExtension for SignedExtension { +impl sp_runtime::traits::TransactionExtension for TransactionExtension +where + C: Dispatchable, +{ const IDENTIFIER: &'static str = "Not needed."; - type AccountId = (); - type Call = (); - type AdditionalSigned = - ::AdditionalSigned; - type Pre = (); + type Implicit = + >::Implicit; - fn additional_signed(&self) -> Result { - self.0.additional_signed() + fn implicit(&self) -> Result { + >::implicit( + &self.0, + ) } + type Pre = (); + type Val = (); - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } + impl_tx_ext_default!(C; weight validate prepare); } -impl SignedExtension { +impl TransactionExtension { /// Create signed extension from its components. pub fn from_params( spec_version: u32, @@ -125,7 +122,7 @@ impl SignedExtension { genesis_hash: Hash, nonce: Nonce, ) -> Self { - Self(GenericSignedExtension::new( + Self(GenericTransactionExtension::new( ( ( (), // non-zero sender diff --git a/bridges/chains/chain-polkadot/src/lib.rs b/bridges/chains/chain-polkadot/src/lib.rs index f4b262d4073..5d2f9e4aa9e 100644 --- a/bridges/chains/chain-polkadot/src/lib.rs +++ b/bridges/chains/chain-polkadot/src/lib.rs @@ -63,8 +63,8 @@ impl ChainWithGrandpa for Polkadot { const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } -/// The SignedExtension used by Polkadot. -pub type SignedExtension = SuffixedCommonSignedExtension; +/// The TransactionExtension used by Polkadot. +pub type TransactionExtension = SuffixedCommonTransactionExtension; /// Name of the parachains pallet in the Polkadot runtime. pub const PARAS_PALLET_NAME: &str = "Paras"; diff --git a/bridges/chains/chain-rococo/src/lib.rs b/bridges/chains/chain-rococo/src/lib.rs index bfcafdf41ea..2827d1f137b 100644 --- a/bridges/chains/chain-rococo/src/lib.rs +++ b/bridges/chains/chain-rococo/src/lib.rs @@ -61,8 +61,8 @@ impl ChainWithGrandpa for Rococo { const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } -// The SignedExtension used by Rococo. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; +// The TransactionExtension used by Rococo. +pub use bp_polkadot_core::CommonTransactionExtension as TransactionExtension; /// Name of the parachains pallet in the Rococo runtime. pub const PARAS_PALLET_NAME: &str = "Paras"; diff --git a/bridges/chains/chain-westend/src/lib.rs b/bridges/chains/chain-westend/src/lib.rs index 2a247e03e59..2b0a609115b 100644 --- a/bridges/chains/chain-westend/src/lib.rs +++ b/bridges/chains/chain-westend/src/lib.rs @@ -61,8 +61,8 @@ impl ChainWithGrandpa for Westend { const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } -// The SignedExtension used by Westend. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; +// The TransactionExtension used by Westend. +pub use bp_polkadot_core::CommonTransactionExtension as TransactionExtension; /// Name of the parachains pallet in the Rococo runtime. pub const PARAS_PALLET_NAME: &str = "Paras"; diff --git a/bridges/modules/relayers/Cargo.toml b/bridges/modules/relayers/Cargo.toml index 0bf889bcca0..04e7b52ed86 100644 --- a/bridges/modules/relayers/Cargo.toml +++ b/bridges/modules/relayers/Cargo.toml @@ -79,6 +79,7 @@ runtime-benchmarks = [ "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", "pallet-bridge-parachains/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] diff --git a/bridges/modules/relayers/src/extension/mod.rs b/bridges/modules/relayers/src/extension/mod.rs index 9a248eb8e79..710533c223a 100644 --- a/bridges/modules/relayers/src/extension/mod.rs +++ b/bridges/modules/relayers/src/extension/mod.rs @@ -33,6 +33,7 @@ use bp_runtime::{Chain, RangeInclusiveExt, StaticStrProvider}; use codec::{Decode, Encode}; use frame_support::{ dispatch::{DispatchInfo, PostDispatchInfo}, + weights::Weight, CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; use frame_system::Config as SystemConfig; @@ -44,10 +45,11 @@ use pallet_transaction_payment::{ }; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, - transaction_validity::{ - TransactionValidity, TransactionValidityError, ValidTransactionBuilder, + traits::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, + TransactionExtension, ValidateResult, Zero, }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransactionBuilder}, DispatchResult, RuntimeDebug, }; use sp_std::{fmt::Debug, marker::PhantomData}; @@ -62,7 +64,7 @@ mod messages_adapter; mod parachain_adapter; mod priority; -/// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. +/// Data that is crafted in `validate`, passed to `prepare` and used at `post_dispatch` method. #[cfg_attr(test, derive(Debug, PartialEq))] pub struct PreDispatchData< AccountId, @@ -78,7 +80,7 @@ pub struct PreDispatchData< impl PreDispatchData { - /// Returns mutable reference to pre-dispatch `finality_target` sent to the + /// Returns mutable reference to `finality_target` sent to the /// `SubmitFinalityProof` call. #[cfg(test)] pub fn submit_finality_proof_info_mut( @@ -119,11 +121,11 @@ pub enum RelayerAccountAction { TypeInfo, )] #[scale_info(skip_type_params(Runtime, Config, LaneId))] -pub struct BridgeRelayersSignedExtension( +pub struct BridgeRelayersTransactionExtension( PhantomData<(Runtime, Config, LaneId)>, ); -impl BridgeRelayersSignedExtension +impl BridgeRelayersTransactionExtension where Self: 'static + Send + Sync, R: RelayersConfig @@ -131,6 +133,7 @@ where + TransactionPaymentConfig, C: ExtensionConfig, R::RuntimeCall: Dispatchable, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::OnChargeTransaction: OnChargeTransaction, LaneId: Clone + Copy + Decode + Encode + Debug + TypeInfo, @@ -145,13 +148,12 @@ where /// virtually boosted. The relayer registration (we only boost priority for registered /// relayer transactions) must be checked outside. fn bundled_messages_for_priority_boost( - call_info: Option<&ExtensionCallInfo>, + parsed_call: &ExtensionCallInfo, ) -> Option { // we only boost priority of message delivery transactions - let parsed_call = match call_info { - Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, - _ => return None, - }; + if !parsed_call.is_receive_messages_proof_call() { + return None; + } // compute total number of messages in transaction let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); @@ -169,9 +171,7 @@ where /// Given post-dispatch information, analyze the outcome of relayer call and return /// actions that need to be performed on relayer account. fn analyze_call_result( - pre: Option< - Option>, - >, + pre: Option>, info: &DispatchInfo, post_info: &PostDispatchInfo, len: usize, @@ -179,7 +179,7 @@ where ) -> RelayerAccountAction { // We don't refund anything for transactions that we don't support. let (relayer, call_info) = match pre { - Some(Some(pre)) => (pre.relayer, pre.call_info), + Some(pre) => (pre.relayer, pre.call_info), _ => return RelayerAccountAction::None, }; @@ -201,15 +201,14 @@ where // // we are not checking if relayer is registered here - it happens during the slash attempt // - // there are couple of edge cases here: + // there are a couple of edge cases here: // // - when the relayer becomes registered during message dispatch: this is unlikely + relayer // should be ready for slashing after registration; // // - when relayer is registered after `validate` is called and priority is not boosted: // relayer should be ready for slashing after registration. - let may_slash_relayer = - Self::bundled_messages_for_priority_boost(Some(&call_info)).is_some(); + let may_slash_relayer = Self::bundled_messages_for_priority_boost(&call_info).is_some(); let slash_relayer_if_delivery_result = may_slash_relayer .then(|| RelayerAccountAction::Slash(relayer.clone(), reward_account_params)) .unwrap_or(RelayerAccountAction::None); @@ -244,7 +243,7 @@ where let post_info_len = len.saturating_sub(call_data.extra_size as usize); let mut post_info_weight = post_info .actual_weight - .unwrap_or(info.weight) + .unwrap_or(info.total_weight()) .saturating_sub(call_data.extra_weight); // let's also replace the weight of slashing relayer with the weight of rewarding relayer @@ -274,7 +273,8 @@ where } } -impl SignedExtension for BridgeRelayersSignedExtension +impl TransactionExtension + for BridgeRelayersTransactionExtension where Self: 'static + Send + Sync, R: RelayersConfig @@ -282,46 +282,50 @@ where + TransactionPaymentConfig, C: ExtensionConfig, R::RuntimeCall: Dispatchable, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::OnChargeTransaction: OnChargeTransaction, LaneId: Clone + Copy + Decode + Encode + Debug + TypeInfo, { const IDENTIFIER: &'static str = C::IdProvider::STR; - type AccountId = R::AccountId; - type Call = R::RuntimeCall; - type AdditionalSigned = (); + type Implicit = (); type Pre = Option>; + type Val = Self::Pre; - fn additional_signed(&self) -> Result<(), TransactionValidityError> { - Ok(()) + fn weight(&self, _call: &R::RuntimeCall) -> Weight { + Weight::zero() } fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &R::RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { - // this is the only relevant line of code for the `pre_dispatch` - // - // we're not calling `validate` from `pre_dispatch` directly because of performance - // reasons, so if you're adding some code that may fail here, please check if it needs - // to be added to the `pre_dispatch` as well - let parsed_call = C::parse_and_check_for_obsolete_call(call)?; + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + // Prepare relevant data for `prepare` + let parsed_call = match C::parse_and_check_for_obsolete_call(call)? { + Some(parsed_call) => parsed_call, + None => return Ok((Default::default(), None, origin)), + }; + // Those calls are only for signed transactions. + let relayer = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + + let data = PreDispatchData { relayer: relayer.clone(), call_info: parsed_call }; - // the following code just plays with transaction priority and never returns an error + // the following code just plays with transaction priority // we only boost priority of presumably correct message delivery transactions - let bundled_messages = match Self::bundled_messages_for_priority_boost(parsed_call.as_ref()) - { + let bundled_messages = match Self::bundled_messages_for_priority_boost(&data.call_info) { Some(bundled_messages) => bundled_messages, - None => return Ok(Default::default()), + None => return Ok((Default::default(), Some(data), origin)), }; // we only boost priority if relayer has staked required balance - if !RelayersPallet::::is_registration_active(who) { - return Ok(Default::default()) + if !RelayersPallet::::is_registration_active(&data.relayer) { + return Ok((Default::default(), Some(data), origin)) } // compute priority boost @@ -334,48 +338,43 @@ where "{}.{:?}: has boosted priority of message delivery transaction \ of relayer {:?}: {} messages -> {} priority", Self::IDENTIFIER, - parsed_call.as_ref().map(|p| p.messages_call_info().lane_id()), - who, + data.call_info.messages_call_info().lane_id(), + data.relayer, bundled_messages, priority_boost, ); - valid_transaction.build() + let validity = valid_transaction.build()?; + Ok((validity, Some(data), origin)) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + val: Self::Val, + _origin: &::RuntimeOrigin, + _call: &R::RuntimeCall, + _info: &DispatchInfoOf, _len: usize, ) -> Result { - // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) - let parsed_call = C::parse_and_check_for_obsolete_call(call)?; - - Ok(parsed_call.map(|call_info| { + Ok(val.inspect(|data| { log::trace!( target: LOG_TARGET, - "{}.{:?}: parsed bridge transaction in pre-dispatch: {:?}", + "{}.{:?}: parsed bridge transaction in prepare: {:?}", Self::IDENTIFIER, - call_info.messages_call_info().lane_id(), - call_info, + data.call_info.messages_call_info().lane_id(), + data.call_info, ); - PreDispatchData { relayer: who.clone(), call_info } })) } - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, len: usize, result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - let lane_id = pre - .as_ref() - .and_then(|p| p.as_ref()) - .map(|p| p.call_info.messages_call_info().lane_id()); + ) -> Result { + let lane_id = pre.as_ref().map(|p| p.call_info.messages_call_info().lane_id()); let call_result = Self::analyze_call_result(pre, info, post_info, len, result); match call_result { @@ -399,7 +398,7 @@ where ), } - Ok(()) + Ok(Weight::zero()) } } @@ -463,8 +462,8 @@ mod tests { use pallet_bridge_parachains::{Call as ParachainsCall, Pallet as ParachainsPallet}; use pallet_utility::Call as UtilityCall; use sp_runtime::{ - traits::{ConstU64, Header as HeaderT}, - transaction_validity::{InvalidTransaction, ValidTransaction}, + traits::{ConstU64, DispatchTransaction, Header as HeaderT}, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, DispatchError, }; @@ -496,7 +495,7 @@ mod tests { ConstU64<1>, >; type TestGrandpaExtension = - BridgeRelayersSignedExtension; + BridgeRelayersTransactionExtension; type TestExtensionConfig = parachain_adapter::WithParachainExtensionConfig< StrTestExtension, TestRuntime, @@ -507,7 +506,7 @@ mod tests { ConstU64<1>, >; type TestExtension = - BridgeRelayersSignedExtension; + BridgeRelayersTransactionExtension; type TestMessagesExtensionConfig = messages_adapter::WithMessagesExtensionConfig< StrTestMessagesExtension, TestRuntime, @@ -515,8 +514,11 @@ mod tests { (), ConstU64<1>, >; - type TestMessagesExtension = - BridgeRelayersSignedExtension; + type TestMessagesExtension = BridgeRelayersTransactionExtension< + TestRuntime, + TestMessagesExtensionConfig, + TestLaneIdType, + >; fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { let test_stake: ThisChainBalance = Stake::get(); @@ -1067,18 +1069,39 @@ mod tests { } fn run_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + let extension: TestExtension = BridgeRelayersTransactionExtension(PhantomData); + extension + .validate_only( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|t| t.0) } fn run_grandpa_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + let extension: TestGrandpaExtension = BridgeRelayersTransactionExtension(PhantomData); + extension + .validate_only( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|t| t.0) } fn run_messages_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestMessagesExtension = BridgeRelayersSignedExtension(PhantomData); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + let extension: TestMessagesExtension = BridgeRelayersTransactionExtension(PhantomData); + extension + .validate_only( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|t| t.0) } fn ignore_priority(tx: TransactionValidity) -> TransactionValidity { @@ -1095,8 +1118,15 @@ mod tests { TransactionValidityError, > { sp_tracing::try_init_simple(); - let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + let extension: TestExtension = BridgeRelayersTransactionExtension(PhantomData); + extension + .validate_and_prepare( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|(pre, _)| pre) } fn run_grandpa_pre_dispatch( @@ -1105,8 +1135,15 @@ mod tests { Option>, TransactionValidityError, > { - let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + let extension: TestGrandpaExtension = BridgeRelayersTransactionExtension(PhantomData); + extension + .validate_and_prepare( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|(pre, _)| pre) } fn run_messages_pre_dispatch( @@ -1115,16 +1152,24 @@ mod tests { Option>, TransactionValidityError, > { - let extension: TestMessagesExtension = BridgeRelayersSignedExtension(PhantomData); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + let extension: TestMessagesExtension = BridgeRelayersTransactionExtension(PhantomData); + extension + .validate_and_prepare( + Some(relayer_account_at_this_chain()).into(), + &call, + &DispatchInfo::default(), + 0, + ) + .map(|(pre, _)| pre) } fn dispatch_info() -> DispatchInfo { DispatchInfo { - weight: Weight::from_parts( + call_weight: Weight::from_parts( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, 0, ), + extension_weight: Weight::zero(), class: frame_support::dispatch::DispatchClass::Normal, pays_fee: frame_support::dispatch::Pays::Yes, } @@ -1140,21 +1185,21 @@ mod tests { >, dispatch_result: DispatchResult, ) { - let post_dispatch_result = TestExtension::post_dispatch( - Some(pre_dispatch_data), + let post_dispatch_result = TestExtension::post_dispatch_details( + pre_dispatch_data, &dispatch_info(), &post_dispatch_info(), 1024, &dispatch_result, ); - assert_eq!(post_dispatch_result, Ok(())); + assert_eq!(post_dispatch_result, Ok(Weight::zero())); } fn expected_delivery_reward() -> ThisChainBalance { let mut post_dispatch_info = post_dispatch_info(); let extra_weight = ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(); post_dispatch_info.actual_weight = - Some(dispatch_info().weight.saturating_sub(extra_weight)); + Some(dispatch_info().call_weight.saturating_sub(extra_weight)); pallet_transaction_payment::Pallet::::compute_actual_fee( 1024, &dispatch_info(), @@ -1714,7 +1759,7 @@ mod tests { initialize_environment(200, 200, 200); let mut dispatch_info = dispatch_info(); - dispatch_info.weight = Weight::from_parts( + dispatch_info.call_weight = Weight::from_parts( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, 0, ); @@ -1918,7 +1963,7 @@ mod tests { dispatch_result: DispatchResult, ) -> RelayerAccountAction { TestExtension::analyze_call_result( - Some(Some(pre_dispatch_data)), + Some(pre_dispatch_data), &dispatch_info(), &post_dispatch_info(), 1024, diff --git a/bridges/modules/relayers/src/extension/priority.rs b/bridges/modules/relayers/src/extension/priority.rs index da188eaf5bd..e09e8627c67 100644 --- a/bridges/modules/relayers/src/extension/priority.rs +++ b/bridges/modules/relayers/src/extension/priority.rs @@ -206,14 +206,17 @@ mod integrity_tests { // finally we are able to estimate transaction size and weight let transaction_size = base_tx_size.saturating_add(tx_call_size); - let transaction_weight = Runtime::WeightInfo::submit_finality_proof_weight( + let transaction_weight = >::WeightInfo::submit_finality_proof_weight( Runtime::BridgedChain::MAX_AUTHORITIES_COUNT * 2 / 3 + 1, Runtime::BridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY, ); pallet_transaction_payment::ChargeTransactionPayment::::get_priority( &DispatchInfo { - weight: transaction_weight, + call_weight: transaction_weight, + extension_weight: Default::default(), class: DispatchClass::Normal, pays_fee: Pays::Yes, }, @@ -315,7 +318,8 @@ mod integrity_tests { pallet_transaction_payment::ChargeTransactionPayment::::get_priority( &DispatchInfo { - weight: transaction_weight, + call_weight: transaction_weight, + extension_weight: Default::default(), class: DispatchClass::Normal, pays_fee: Pays::Yes, }, @@ -385,20 +389,27 @@ mod integrity_tests { // trie nodes to the proof (x0.5 because we expect some nodes to be reused) let estimated_message_size = 512; // let's say all our messages have the same dispatch weight - let estimated_message_dispatch_weight = - Runtime::WeightInfo::message_dispatch_weight(estimated_message_size); + let estimated_message_dispatch_weight = >::WeightInfo::message_dispatch_weight( + estimated_message_size + ); // messages proof argument size is (for every message) messages size + some additional // trie nodes. Some of them are reused by different messages, so let's take 2/3 of // default "overhead" constant - let messages_proof_size = Runtime::WeightInfo::expected_extra_storage_proof_size() - .saturating_mul(2) - .saturating_div(3) - .saturating_add(estimated_message_size) - .saturating_mul(messages as _); + let messages_proof_size = >::WeightInfo::expected_extra_storage_proof_size() + .saturating_mul(2) + .saturating_div(3) + .saturating_add(estimated_message_size) + .saturating_mul(messages as _); // finally we are able to estimate transaction size and weight let transaction_size = base_tx_size.saturating_add(messages_proof_size); - let transaction_weight = Runtime::WeightInfo::receive_messages_proof_weight( + let transaction_weight = >::WeightInfo::receive_messages_proof_weight( &PreComputedSize(transaction_size as _), messages as _, estimated_message_dispatch_weight.saturating_mul(messages), @@ -406,7 +417,8 @@ mod integrity_tests { pallet_transaction_payment::ChargeTransactionPayment::::get_priority( &DispatchInfo { - weight: transaction_weight, + call_weight: transaction_weight, + extension_weight: Default::default(), class: DispatchClass::Normal, pays_fee: Pays::Yes, }, diff --git a/bridges/primitives/polkadot-core/src/lib.rs b/bridges/primitives/polkadot-core/src/lib.rs index e83be59b238..a8abdb59bea 100644 --- a/bridges/primitives/polkadot-core/src/lib.rs +++ b/bridges/primitives/polkadot-core/src/lib.rs @@ -24,8 +24,8 @@ use bp_runtime::{ self, extensions::{ ChargeTransactionPayment, CheckEra, CheckGenesis, CheckNonZeroSender, CheckNonce, - CheckSpecVersion, CheckTxVersion, CheckWeight, GenericSignedExtension, - SignedExtensionSchema, + CheckSpecVersion, CheckTxVersion, CheckWeight, GenericTransactionExtension, + TransactionExtensionSchema, }, EncodedOrDecodedCall, StorageMapKeyProvider, TransactionEra, }; @@ -229,8 +229,12 @@ pub type SignedBlock = generic::SignedBlock; pub type Balance = u128; /// Unchecked Extrinsic type. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic, Signature, SignedExt>; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic< + AccountAddress, + EncodedOrDecodedCall, + Signature, + TransactionExt, +>; /// Account address, used by the Polkadot-like chain. pub type Address = MultiAddress; @@ -275,7 +279,7 @@ impl AccountInfoStorageMapKeyProvider { } /// Extra signed extension data that is used by most chains. -pub type CommonSignedExtra = ( +pub type CommonTransactionExtra = ( CheckNonZeroSender, CheckSpecVersion, CheckTxVersion, @@ -286,12 +290,12 @@ pub type CommonSignedExtra = ( ChargeTransactionPayment, ); -/// Extra signed extension data that starts with `CommonSignedExtra`. -pub type SuffixedCommonSignedExtension = - GenericSignedExtension<(CommonSignedExtra, Suffix)>; +/// Extra transaction extension data that starts with `CommonTransactionExtra`. +pub type SuffixedCommonTransactionExtension = + GenericTransactionExtension<(CommonTransactionExtra, Suffix)>; -/// Helper trait to define some extra methods on `SuffixedCommonSignedExtension`. -pub trait SuffixedCommonSignedExtensionExt { +/// Helper trait to define some extra methods on `SuffixedCommonTransactionExtension`. +pub trait SuffixedCommonTransactionExtensionExt { /// Create signed extension from its components. fn from_params( spec_version: u32, @@ -300,7 +304,7 @@ pub trait SuffixedCommonSignedExtensionExt { genesis_hash: Hash, nonce: Nonce, tip: Balance, - extra: (Suffix::Payload, Suffix::AdditionalSigned), + extra: (Suffix::Payload, Suffix::Implicit), ) -> Self; /// Return transaction nonce. @@ -310,9 +314,10 @@ pub trait SuffixedCommonSignedExtensionExt { fn tip(&self) -> Balance; } -impl SuffixedCommonSignedExtensionExt for SuffixedCommonSignedExtension +impl SuffixedCommonTransactionExtensionExt + for SuffixedCommonTransactionExtension where - Suffix: SignedExtensionSchema, + Suffix: TransactionExtensionSchema, { fn from_params( spec_version: u32, @@ -321,9 +326,9 @@ where genesis_hash: Hash, nonce: Nonce, tip: Balance, - extra: (Suffix::Payload, Suffix::AdditionalSigned), + extra: (Suffix::Payload, Suffix::Implicit), ) -> Self { - GenericSignedExtension::new( + GenericTransactionExtension::new( ( ( (), // non-zero sender @@ -365,7 +370,7 @@ where } /// Signed extension that is used by most chains. -pub type CommonSignedExtension = SuffixedCommonSignedExtension<()>; +pub type CommonTransactionExtension = SuffixedCommonTransactionExtension<()>; #[cfg(test)] mod tests { diff --git a/bridges/primitives/relayers/src/extension.rs b/bridges/primitives/relayers/src/extension.rs index a9fa4df27ea..8fd0f151e2a 100644 --- a/bridges/primitives/relayers/src/extension.rs +++ b/bridges/primitives/relayers/src/extension.rs @@ -138,7 +138,7 @@ pub trait ExtensionConfig { /// Lane identifier type. type LaneId: Clone + Copy + Decode + Encode + Debug; - /// Given runtime call, check if it is supported by the signed extension. Additionally, + /// Given runtime call, check if it is supported by the transaction extension. Additionally, /// check if call (or any of batched calls) are obsolete. fn parse_and_check_for_obsolete_call( call: &::RuntimeCall, diff --git a/bridges/primitives/runtime/src/extensions.rs b/bridges/primitives/runtime/src/extensions.rs index d896bc92eff..25553f9c7b2 100644 --- a/bridges/primitives/runtime/src/extensions.rs +++ b/bridges/primitives/runtime/src/extensions.rs @@ -20,135 +20,131 @@ use codec::{Compact, Decode, Encode}; use impl_trait_for_tuples::impl_for_tuples; use scale_info::{StaticTypeInfo, TypeInfo}; use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension}, + impl_tx_ext_default, + traits::{Dispatchable, TransactionExtension}, transaction_validity::TransactionValidityError, }; use sp_std::{fmt::Debug, marker::PhantomData}; -/// Trait that describes some properties of a `SignedExtension` that are needed in order to send a -/// transaction to the chain. -pub trait SignedExtensionSchema: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo { +/// Trait that describes some properties of a `TransactionExtension` that are needed in order to +/// send a transaction to the chain. +pub trait TransactionExtensionSchema: + Encode + Decode + Debug + Eq + Clone + StaticTypeInfo +{ /// A type of the data encoded as part of the transaction. type Payload: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo; /// Parameters which are part of the payload used to produce transaction signature, /// but don't end up in the transaction itself (i.e. inherent part of the runtime). - type AdditionalSigned: Encode + Debug + Eq + Clone + StaticTypeInfo; + type Implicit: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo; } -impl SignedExtensionSchema for () { +impl TransactionExtensionSchema for () { type Payload = (); - type AdditionalSigned = (); + type Implicit = (); } -/// An implementation of `SignedExtensionSchema` using generic params. +/// An implementation of `TransactionExtensionSchema` using generic params. #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)] -pub struct GenericSignedExtensionSchema(PhantomData<(P, S)>); +pub struct GenericTransactionExtensionSchema(PhantomData<(P, S)>); -impl SignedExtensionSchema for GenericSignedExtensionSchema +impl TransactionExtensionSchema for GenericTransactionExtensionSchema where P: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo, - S: Encode + Debug + Eq + Clone + StaticTypeInfo, + S: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo, { type Payload = P; - type AdditionalSigned = S; + type Implicit = S; } -/// The `SignedExtensionSchema` for `frame_system::CheckNonZeroSender`. -pub type CheckNonZeroSender = GenericSignedExtensionSchema<(), ()>; +/// The `TransactionExtensionSchema` for `frame_system::CheckNonZeroSender`. +pub type CheckNonZeroSender = GenericTransactionExtensionSchema<(), ()>; -/// The `SignedExtensionSchema` for `frame_system::CheckSpecVersion`. -pub type CheckSpecVersion = GenericSignedExtensionSchema<(), u32>; +/// The `TransactionExtensionSchema` for `frame_system::CheckSpecVersion`. +pub type CheckSpecVersion = GenericTransactionExtensionSchema<(), u32>; -/// The `SignedExtensionSchema` for `frame_system::CheckTxVersion`. -pub type CheckTxVersion = GenericSignedExtensionSchema<(), u32>; +/// The `TransactionExtensionSchema` for `frame_system::CheckTxVersion`. +pub type CheckTxVersion = GenericTransactionExtensionSchema<(), u32>; -/// The `SignedExtensionSchema` for `frame_system::CheckGenesis`. -pub type CheckGenesis = GenericSignedExtensionSchema<(), Hash>; +/// The `TransactionExtensionSchema` for `frame_system::CheckGenesis`. +pub type CheckGenesis = GenericTransactionExtensionSchema<(), Hash>; -/// The `SignedExtensionSchema` for `frame_system::CheckEra`. -pub type CheckEra = GenericSignedExtensionSchema; +/// The `TransactionExtensionSchema` for `frame_system::CheckEra`. +pub type CheckEra = GenericTransactionExtensionSchema; -/// The `SignedExtensionSchema` for `frame_system::CheckNonce`. -pub type CheckNonce = GenericSignedExtensionSchema, ()>; +/// The `TransactionExtensionSchema` for `frame_system::CheckNonce`. +pub type CheckNonce = GenericTransactionExtensionSchema, ()>; -/// The `SignedExtensionSchema` for `frame_system::CheckWeight`. -pub type CheckWeight = GenericSignedExtensionSchema<(), ()>; +/// The `TransactionExtensionSchema` for `frame_system::CheckWeight`. +pub type CheckWeight = GenericTransactionExtensionSchema<(), ()>; -/// The `SignedExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. -pub type ChargeTransactionPayment = GenericSignedExtensionSchema, ()>; +/// The `TransactionExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. +pub type ChargeTransactionPayment = + GenericTransactionExtensionSchema, ()>; -/// The `SignedExtensionSchema` for `polkadot-runtime-common::PrevalidateAttests`. -pub type PrevalidateAttests = GenericSignedExtensionSchema<(), ()>; +/// The `TransactionExtensionSchema` for `polkadot-runtime-common::PrevalidateAttests`. +pub type PrevalidateAttests = GenericTransactionExtensionSchema<(), ()>; -/// The `SignedExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. -pub type BridgeRejectObsoleteHeadersAndMessages = GenericSignedExtensionSchema<(), ()>; +/// The `TransactionExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. +pub type BridgeRejectObsoleteHeadersAndMessages = GenericTransactionExtensionSchema<(), ()>; -/// The `SignedExtensionSchema` for `RefundBridgedParachainMessages`. +/// The `TransactionExtensionSchema` for `RefundBridgedParachainMessages`. /// This schema is dedicated for `RefundBridgedParachainMessages` signed extension as /// wildcard/placeholder, which relies on the scale encoding for `()` or `((), ())`, or `((), (), /// ())` is the same. So runtime can contains any kind of tuple: /// `(BridgeRefundBridgeHubRococoMessages)` /// `(BridgeRefundBridgeHubRococoMessages, BridgeRefundBridgeHubWestendMessages)` /// `(BridgeRefundParachainMessages1, ..., BridgeRefundParachainMessagesN)` -pub type RefundBridgedParachainMessagesSchema = GenericSignedExtensionSchema<(), ()>; +pub type RefundBridgedParachainMessagesSchema = GenericTransactionExtensionSchema<(), ()>; #[impl_for_tuples(1, 12)] -impl SignedExtensionSchema for Tuple { +impl TransactionExtensionSchema for Tuple { for_tuples!( type Payload = ( #( Tuple::Payload ),* ); ); - for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); + for_tuples!( type Implicit = ( #( Tuple::Implicit ),* ); ); } /// A simplified version of signed extensions meant for producing signed transactions /// and signed payloads in the client code. #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub struct GenericSignedExtension { +pub struct GenericTransactionExtension { /// A payload that is included in the transaction. pub payload: S::Payload, #[codec(skip)] // It may be set to `None` if extensions are decoded. We are never reconstructing transactions - // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to - // read fields of the `payload`. And when resigning transaction, we're reconstructing - // `SignedExtensions` from scratch. - additional_signed: Option, + // (and it makes no sense to do that) => decoded version of `TransactionExtensions` is only + // used to read fields of the `payload`. And when resigning transaction, we're reconstructing + // `TransactionExtensions` from scratch. + implicit: Option, } -impl GenericSignedExtension { - /// Create new `GenericSignedExtension` object. - pub fn new(payload: S::Payload, additional_signed: Option) -> Self { - Self { payload, additional_signed } +impl GenericTransactionExtension { + /// Create new `GenericTransactionExtension` object. + pub fn new(payload: S::Payload, implicit: Option) -> Self { + Self { payload, implicit } } } -impl SignedExtension for GenericSignedExtension +impl TransactionExtension for GenericTransactionExtension where - S: SignedExtensionSchema, + C: Dispatchable, + S: TransactionExtensionSchema, S::Payload: Send + Sync, - S::AdditionalSigned: Send + Sync, + S::Implicit: Send + Sync, { const IDENTIFIER: &'static str = "Not needed."; - type AccountId = (); - type Call = (); - type AdditionalSigned = S::AdditionalSigned; - type Pre = (); + type Implicit = S::Implicit; - fn additional_signed(&self) -> Result { + fn implicit(&self) -> Result { // we shall not ever see this error in relay, because we are never signing decoded // transactions. Instead we're constructing and signing new transactions. So the error code // is kinda random here - self.additional_signed.clone().ok_or( - frame_support::unsigned::TransactionValidityError::Unknown( + self.implicit + .clone() + .ok_or(frame_support::unsigned::TransactionValidityError::Unknown( frame_support::unsigned::UnknownTransaction::Custom(0xFF), - ), - ) + )) } + type Pre = (); + type Val = (); - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } + impl_tx_ext_default!(C; weight validate prepare); } diff --git a/bridges/relays/lib-substrate-relay/src/messages/mod.rs b/bridges/relays/lib-substrate-relay/src/messages/mod.rs index f7031648bc3..b4ee57ed774 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/mod.rs @@ -428,7 +428,7 @@ where "Prepared {} -> {} messages delivery call. Weight: {}/{}, size: {}/{}", P::SourceChain::NAME, P::TargetChain::NAME, - call.get_dispatch_info().weight, + call.get_dispatch_info().call_weight, P::TargetChain::max_extrinsic_weight(), call.encode().len(), P::TargetChain::max_extrinsic_size(), @@ -521,7 +521,7 @@ where "Prepared {} -> {} delivery confirmation transaction. Weight: {}/{}, size: {}/{}", P::TargetChain::NAME, P::SourceChain::NAME, - call.get_dispatch_info().weight, + call.get_dispatch_info().call_weight, P::SourceChain::max_extrinsic_weight(), call.encode().len(), P::SourceChain::max_extrinsic_size(), diff --git a/bridges/snowbridge/pallets/ethereum-client/Cargo.toml b/bridges/snowbridge/pallets/ethereum-client/Cargo.toml index 666ac3fbc8a..262d9a7f380 100644 --- a/bridges/snowbridge/pallets/ethereum-client/Cargo.toml +++ b/bridges/snowbridge/pallets/ethereum-client/Cargo.toml @@ -49,13 +49,7 @@ serde = { workspace = true, default-features = true } [features] default = ["std"] -fuzzing = [ - "hex-literal", - "pallet-timestamp", - "serde", - "serde_json", - "sp-io", -] +fuzzing = ["hex-literal", "pallet-timestamp", "serde", "serde_json", "sp-io"] std = [ "codec/std", "frame-support/std", diff --git a/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml b/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml index bd417687573..87f0cf9a551 100644 --- a/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml +++ b/bridges/snowbridge/pallets/ethereum-client/fixtures/Cargo.toml @@ -29,6 +29,4 @@ std = [ "sp-core/std", "sp-std/std", ] -runtime-benchmarks = [ - "snowbridge-core/runtime-benchmarks", -] +runtime-benchmarks = ["snowbridge-core/runtime-benchmarks"] diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml b/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml index b66b57c3620..6162a17728b 100644 --- a/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml @@ -29,6 +29,4 @@ std = [ "sp-core/std", "sp-std/std", ] -runtime-benchmarks = [ - "snowbridge-core/runtime-benchmarks", -] +runtime-benchmarks = ["snowbridge-core/runtime-benchmarks"] diff --git a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml index 9d4cffc98d7..16241428df8 100644 --- a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/Cargo.toml @@ -30,9 +30,4 @@ sp-tracing = { workspace = true, default-features = true } [features] default = ["std"] -std = [ - "codec/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", -] +std = ["codec/std", "scale-info/std", "sp-core/std", "sp-runtime/std"] diff --git a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs index c4c8440e518..2c531c39acc 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs @@ -33,7 +33,7 @@ use frame_support::traits::{ExecuteBlock, ExtrinsicCall, Get, IsSubType}; use sp_core::storage::{ChildInfo, StateVersion}; use sp_externalities::{set_and_run_with_externalities, Externalities}; use sp_io::KillStorageResult; -use sp_runtime::traits::{Block as BlockT, Extrinsic, HashingFor, Header as HeaderT}; +use sp_runtime::traits::{Block as BlockT, ExtrinsicLike, HashingFor, Header as HeaderT}; use sp_trie::{MemoryDB, ProofSizeProvider}; use trie_recorder::SizeOnlyRecorderProvider; @@ -96,7 +96,7 @@ pub fn validate_block< ) -> ValidationResult where B::Extrinsic: ExtrinsicCall, - ::Call: IsSubType>, + ::Call: IsSubType>, { let block_data = codec::decode_from_bytes::>(block_data) .expect("Invalid parachain block data"); @@ -240,16 +240,13 @@ fn extract_parachain_inherent_data( ) -> &ParachainInherentData where B::Extrinsic: ExtrinsicCall, - ::Call: IsSubType>, + ::Call: IsSubType>, { block .extrinsics() .iter() // Inherents are at the front of the block and are unsigned. - // - // If `is_signed` is returning `None`, we keep it safe and assume that it is "signed". - // We are searching for unsigned transactions anyway. - .take_while(|e| !e.is_signed().unwrap_or(true)) + .take_while(|e| e.is_bare()) .filter_map(|e| e.call().is_sub_type()) .find_map(|c| match c { crate::Call::set_validation_data { data: validation_data } => Some(validation_data), diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 00f2cf8f636..42adaba7a27 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -117,6 +117,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-asset-conversion-ops/runtime-benchmarks", + "pallet-asset-conversion-tx-payment/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "pallet-assets-freezer/runtime-benchmarks", "pallet-assets/runtime-benchmarks", @@ -128,6 +129,7 @@ runtime-benchmarks = [ "pallet-nfts/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", 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 eb3e26764f6..64fdf488372 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -175,6 +175,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = frame_support::traits::ConstU32<16>; @@ -229,6 +230,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -818,6 +820,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime { AssetConversion, ResolveAssetTo, >; + type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetConversionTxHelper; } parameter_types! { @@ -998,8 +1003,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -1013,7 +1018,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( InitStorageVersions, @@ -1100,14 +1105,81 @@ pub type Executive = frame_executive::Executive< type XcmTrustedQueryResult = Result; +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetConversionTxHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl + pallet_asset_conversion_tx_payment::BenchmarkHelperTrait< + AccountId, + cumulus_primitives_core::Location, + cumulus_primitives_core::Location, + > for AssetConversionTxHelper +{ + fn create_asset_id_parameter(seed: u32) -> (Location, Location) { + // Use a different parachain' foreign assets pallet so that the asset is indeed foreign. + let asset_id = Location::new( + 1, + [ + cumulus_primitives_core::Junction::Parachain(3000), + cumulus_primitives_core::Junction::PalletInstance(53), + cumulus_primitives_core::Junction::GeneralIndex(seed.into()), + ], + ); + (asset_id.clone(), asset_id) + } + + fn setup_balances_and_pool(asset_id: cumulus_primitives_core::Location, account: AccountId) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + assert_ok!(ForeignAssets::force_create( + RuntimeOrigin::root(), + asset_id.clone().into(), + account.clone().into(), /* owner */ + true, /* is_sufficient */ + 1, + )); + + let lp_provider = account.clone(); + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&lp_provider, u64::MAX.into()); + assert_ok!(ForeignAssets::mint_into( + asset_id.clone().into(), + &lp_provider, + u64::MAX.into() + )); + + let token_native = alloc::boxed::Box::new(TokenLocation::get()); + let token_second = alloc::boxed::Box::new(asset_id); + + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider.clone()), + token_native.clone(), + token_second.clone() + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(lp_provider.clone()), + token_native, + token_second, + (u32::MAX / 8).into(), // 1 desired + u32::MAX.into(), // 2 desired + 1, // 1 min + 1, // 2 min + lp_provider, + )); + } +} + #[cfg(feature = "runtime-benchmarks")] mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_assets, Local] [pallet_assets, Foreign] [pallet_assets, Pool] [pallet_asset_conversion, AssetConversion] + [pallet_asset_conversion_tx_payment, AssetTxPayment] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] @@ -1118,6 +1190,7 @@ mod benches { [pallet_uniques, Uniques] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] @@ -1445,6 +1518,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench; @@ -1479,6 +1553,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &alloc::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..182410f20ff --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,132 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ +// --chain=asset-hub-rococo-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs index f20790cde39..33f111009ed 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs @@ -19,8 +19,10 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_asset_conversion; pub mod pallet_asset_conversion_ops; +pub mod pallet_asset_conversion_tx_payment; pub mod pallet_assets_foreign; pub mod pallet_assets_local; pub mod pallet_assets_pool; @@ -33,6 +35,7 @@ pub mod pallet_nfts; pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_xcm; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion_tx_payment.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion_tx_payment.rs new file mode 100644 index 00000000000..0a639b368af --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion_tx_payment.rs @@ -0,0 +1,92 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_asset_conversion_tx_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-01-04, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Georges-MacBook-Pro.local`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_asset_conversion_tx_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ +// --chain=asset-hub-rococo-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_asset_conversion_tx_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_asset_conversion_tx_payment::WeightInfo for WeightInfo { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(10_000_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 209_000_000 picoseconds. + Weight::from_parts(212_000_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `631` + // Estimated: `7404` + // Minimum execution time: 1_228_000_000 picoseconds. + Weight::from_parts(1_268_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..035f9a6dbe5 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ +// --chain=asset-hub-rococo-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 72a125cee2a..5fa48381b67 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -118,6 +118,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-asset-conversion-ops/runtime-benchmarks", + "pallet-asset-conversion-tx-payment/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "pallet-assets-freezer/runtime-benchmarks", "pallet-assets/runtime-benchmarks", @@ -130,6 +131,7 @@ runtime-benchmarks = [ "pallet-proxy/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", 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 74e75ebb489..32d12174953 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -175,6 +175,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = frame_support::traits::ConstU32<16>; @@ -229,6 +230,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -811,6 +813,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime { AssetConversion, ResolveAssetTo, >; + type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetConversionTxHelper; } parameter_types! { @@ -994,8 +999,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -1009,7 +1014,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -1148,14 +1153,86 @@ pub type Executive = frame_executive::Executive< Migrations, >; +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetConversionTxHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl + pallet_asset_conversion_tx_payment::BenchmarkHelperTrait< + AccountId, + cumulus_primitives_core::Location, + cumulus_primitives_core::Location, + > for AssetConversionTxHelper +{ + fn create_asset_id_parameter( + seed: u32, + ) -> (cumulus_primitives_core::Location, cumulus_primitives_core::Location) { + // Use a different parachain' foreign assets pallet so that the asset is indeed foreign. + let asset_id = cumulus_primitives_core::Location::new( + 1, + [ + cumulus_primitives_core::Junction::Parachain(3000), + cumulus_primitives_core::Junction::PalletInstance(53), + cumulus_primitives_core::Junction::GeneralIndex(seed.into()), + ], + ); + (asset_id.clone(), asset_id) + } + + fn setup_balances_and_pool(asset_id: cumulus_primitives_core::Location, account: AccountId) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + assert_ok!(ForeignAssets::force_create( + RuntimeOrigin::root(), + asset_id.clone().into(), + account.clone().into(), /* owner */ + true, /* is_sufficient */ + 1, + )); + + let lp_provider = account.clone(); + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&lp_provider, u64::MAX.into()); + assert_ok!(ForeignAssets::mint_into( + asset_id.clone().into(), + &lp_provider, + u64::MAX.into() + )); + + let token_native = alloc::boxed::Box::new(cumulus_primitives_core::Location::new( + 1, + cumulus_primitives_core::Junctions::Here, + )); + let token_second = alloc::boxed::Box::new(asset_id); + + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider.clone()), + token_native.clone(), + token_second.clone() + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(lp_provider.clone()), + token_native, + token_second, + (u32::MAX / 2).into(), // 1 desired + u32::MAX.into(), // 2 desired + 1, // 1 min + 1, // 2 min + lp_provider, + )); + } +} + #[cfg(feature = "runtime-benchmarks")] mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_assets, Local] [pallet_assets, Foreign] [pallet_assets, Pool] [pallet_asset_conversion, AssetConversion] + [pallet_asset_conversion_tx_payment, AssetTxPayment] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] @@ -1166,6 +1243,7 @@ mod benches { [pallet_uniques, Uniques] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] @@ -1539,6 +1617,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench; @@ -1573,6 +1652,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &alloc::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..e8dd9763c28 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,132 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ +// --chain=asset-hub-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_206_000 picoseconds. + Weight::from_parts(6_212_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_851_000 picoseconds. + Weight::from_parts(8_847_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_851_000 picoseconds. + Weight::from_parts(8_847_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 631_000 picoseconds. + Weight::from_parts(3_086_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_446_000 picoseconds. + Weight::from_parts(5_911_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 481_000 picoseconds. + Weight::from_parts(2_916_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_927_000 picoseconds. + Weight::from_parts(6_613_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs index 4eebb1f8d78..b0f986768f4 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs @@ -18,8 +18,10 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_asset_conversion; pub mod pallet_asset_conversion_ops; +pub mod pallet_asset_conversion_tx_payment; pub mod pallet_assets_foreign; pub mod pallet_assets_local; pub mod pallet_assets_pool; @@ -32,6 +34,7 @@ pub mod pallet_nfts; pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_xcm; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion_tx_payment.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion_tx_payment.rs new file mode 100644 index 00000000000..8fe302630fb --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion_tx_payment.rs @@ -0,0 +1,92 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_asset_conversion_tx_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-01-04, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Georges-MacBook-Pro.local`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_asset_conversion_tx_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ +// --chain=asset-hub-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_asset_conversion_tx_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_asset_conversion_tx_payment::WeightInfo for WeightInfo { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 214_000_000 picoseconds. + Weight::from_parts(219_000_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `631` + // Estimated: `7404` + // Minimum execution time: 1_211_000_000 picoseconds. + Weight::from_parts(1_243_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..b4c78a78b48 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ +// --chain=asset-hub-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 40_847_000 picoseconds. + Weight::from_parts(49_674_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index fd5782d68e4..4af8a9f4385 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -241,6 +241,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm-bridge-hub/runtime-benchmarks", 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 c971fa59c68..c226ed9c4fa 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 @@ -38,7 +38,7 @@ use frame_support::{ use frame_system::{EnsureNever, EnsureRoot}; use pallet_bridge_messages::LaneIdOf; use pallet_bridge_relayers::extension::{ - BridgeRelayersSignedExtension, WithMessagesExtensionConfig, + BridgeRelayersTransactionExtension, WithMessagesExtensionConfig, }; use pallet_xcm_bridge_hub::XcmAsPlainPayload; use polkadot_parachain_primitives::primitives::Sibling; @@ -92,9 +92,9 @@ type FromRococoBulletinMessageBlobDispatcher = BridgeBlobDispatcher< BridgeRococoToRococoBulletinMessagesPalletInstance, >; -/// Signed extension that refunds relayers that are delivering messages from the Rococo Bulletin -/// chain. -pub type OnBridgeHubRococoRefundRococoBulletinMessages = BridgeRelayersSignedExtension< +/// Transaction extension that refunds relayers that are delivering messages from the Rococo +/// Bulletin chain. +pub type OnBridgeHubRococoRefundRococoBulletinMessages = BridgeRelayersTransactionExtension< Runtime, WithMessagesExtensionConfig< StrOnBridgeHubRococoRefundRococoBulletinMessages, 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 8fe04572310..29ea4e05f29 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 @@ -37,7 +37,7 @@ use frame_support::{parameter_types, traits::PalletInfoAccess}; use frame_system::{EnsureNever, EnsureRoot}; use pallet_bridge_messages::LaneIdOf; use pallet_bridge_relayers::extension::{ - BridgeRelayersSignedExtension, WithMessagesExtensionConfig, + BridgeRelayersTransactionExtension, WithMessagesExtensionConfig, }; use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains}; use polkadot_parachain_primitives::primitives::Sibling; @@ -84,8 +84,9 @@ pub type ToWestendBridgeHubMessagesDeliveryProof = type FromWestendMessageBlobDispatcher = BridgeBlobDispatcher; -/// Signed extension that refunds relayers that are delivering messages from the Westend parachain. -pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = BridgeRelayersSignedExtension< +/// Transaction extension that refunds relayers that are delivering messages from the Westend +/// parachain. +pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = BridgeRelayersTransactionExtension< Runtime, WithMessagesExtensionConfig< StrOnBridgeHubRococoRefundBridgeHubWestendMessages, 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 cafd2b33fa8..259b0355916 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 @@ -120,8 +120,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -132,13 +132,13 @@ pub type SignedExtra = ( pallet_transaction_payment::ChargeTransactionPayment, BridgeRejectObsoleteHeadersAndMessages, (bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages,), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, frame_metadata_hash_extension::CheckMetadataHash, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -298,6 +298,8 @@ impl frame_system::Config for Runtime { type DbWeight = RocksDbWeight; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = weights::frame_system::WeightInfo; + /// Weight information for the extensions of this pallet. + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; /// Block & extrinsics weights: base values and limits. type BlockWeights = RuntimeBlockWeights; /// The maximum length of a block (in bytes). @@ -358,6 +360,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -650,12 +653,14 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] @@ -1037,6 +1042,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -1069,6 +1075,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &alloc::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); @@ -1538,16 +1545,16 @@ mod tests { use codec::Encode; use sp_runtime::{ generic::Era, - traits::{SignedExtension, Zero}, + traits::{TransactionExtension, Zero}, }; #[test] - fn ensure_signed_extension_definition_is_compatible_with_relay() { - use bp_polkadot_core::SuffixedCommonSignedExtensionExt; + fn ensure_transaction_extension_definition_is_compatible_with_relay() { + use bp_polkadot_core::SuffixedCommonTransactionExtensionExt; sp_io::TestExternalities::default().execute_with(|| { frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( + let payload: TxExtension = ( frame_system::CheckNonZeroSender::new(), frame_system::CheckSpecVersion::new(), frame_system::CheckTxVersion::new(), @@ -1560,13 +1567,13 @@ mod tests { ( bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), ), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), frame_metadata_hash_extension::CheckMetadataHash::new(false), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), ); // for BridgeHubRococo { - let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::from_params( + let bhr_indirect_payload = bp_bridge_hub_rococo::TransactionExtension::from_params( VERSION.spec_version, VERSION.transaction_version, bp_runtime::TransactionEra::Immortal, @@ -1577,8 +1584,8 @@ mod tests { ); assert_eq!(payload.encode().split_last().unwrap().1, bhr_indirect_payload.encode()); assert_eq!( - payload.additional_signed().unwrap().encode().split_last().unwrap().1, - bhr_indirect_payload.additional_signed().unwrap().encode() + TxExtension::implicit(&payload).unwrap().encode().split_last().unwrap().1, + sp_runtime::traits::TransactionExtension::::implicit(&bhr_indirect_payload).unwrap().encode() ) } }); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..64eef1b4f74 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,132 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_136_000 picoseconds. + Weight::from_parts(5_842_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_771_000 picoseconds. + Weight::from_parts(8_857_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_771_000 picoseconds. + Weight::from_parts(8_857_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 732_000 picoseconds. + Weight::from_parts(2_875_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_627_000 picoseconds. + Weight::from_parts(6_322_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 471_000 picoseconds. + Weight::from_parts(2_455_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 491_000 picoseconds. + Weight::from_parts(2_916_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_798_000 picoseconds. + Weight::from_parts(6_272_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index 517b3eb69fc..74796e626a2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_bridge_grandpa; pub mod pallet_bridge_messages_rococo_to_rococo_bulletin; @@ -38,6 +39,7 @@ pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..71d17e7259f --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3593` + // Minimum execution time: 34_956_000 picoseconds. + Weight::from_parts(40_788_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs index 7a0f1462e7a..8be2993c68f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs @@ -21,7 +21,7 @@ use bridge_hub_rococo_runtime::{ bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages, xcm_config::XcmConfig, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, - SignedExtra, UncheckedExtrinsic, + TxExtension, UncheckedExtrinsic, }; use codec::{Decode, Encode}; use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; @@ -170,7 +170,7 @@ fn construct_extrinsic( call: RuntimeCall, ) -> UncheckedExtrinsic { let account_id = AccountId32::from(sender.public()); - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -183,12 +183,12 @@ fn construct_extrinsic( pallet_transaction_payment::ChargeTransactionPayment::::from(0), BridgeRejectObsoleteHeadersAndMessages::default(), (OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), frame_metadata_hash_extension::CheckMetadataHash::::new(false), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), ); - let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); + let payload = SignedPayload::new(call.clone(), tx_ext.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); - UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), extra) + UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), tx_ext) } fn construct_and_apply_extrinsic( 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 20bd5d12913..01674287fde 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 @@ -18,13 +18,11 @@ use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ - bridge_common_config, bridge_to_bulletin_config, - bridge_to_ethereum_config::EthereumGatewayAddress, - bridge_to_westend_config, - xcm_config::{LocationToAccountId, RelayNetwork, TokenLocation, XcmConfig}, + bridge_common_config, bridge_to_bulletin_config, bridge_to_westend_config, + xcm_config::{RelayNetwork, TokenLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, - SignedExtra, TransactionPayment, UncheckedExtrinsic, + TransactionPayment, TxExtension, UncheckedExtrinsic, }; use bridge_hub_test_utils::SlotDurations; use codec::{Decode, Encode}; @@ -51,7 +49,7 @@ fn construct_extrinsic( call: RuntimeCall, ) -> UncheckedExtrinsic { let account_id = AccountId32::from(sender.public()); - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -64,12 +62,13 @@ fn construct_extrinsic( pallet_transaction_payment::ChargeTransactionPayment::::from(0), BridgeRejectObsoleteHeadersAndMessages::default(), (bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), frame_metadata_hash_extension::CheckMetadataHash::new(false), - ); - let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + ) + .into(); + let payload = SignedPayload::new(call.clone(), tx_ext.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); - UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), extra) + UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), tx_ext) } fn construct_and_apply_extrinsic( @@ -128,6 +127,9 @@ mod bridge_hub_westend_tests { BridgeGrandpaWestendInstance, BridgeParachainWestendInstance, DeliveryRewardInBalance, RelayersForLegacyLaneIdsMessagesInstance, }; + use bridge_hub_rococo_runtime::{ + bridge_to_ethereum_config::EthereumGatewayAddress, xcm_config::LocationToAccountId, + }; use bridge_hub_test_utils::test_cases::from_parachain; use bridge_to_westend_config::{ BridgeHubWestendLocation, WestendGlobalConsensusNetwork, @@ -498,7 +500,10 @@ mod bridge_hub_bulletin_tests { use super::*; use bp_messages::{HashedLaneId, LaneIdType}; use bridge_common_config::BridgeGrandpaRococoBulletinInstance; - use bridge_hub_rococo_runtime::bridge_common_config::RelayersForPermissionlessLanesInstance; + use bridge_hub_rococo_runtime::{ + bridge_common_config::RelayersForPermissionlessLanesInstance, + xcm_config::LocationToAccountId, + }; use bridge_hub_test_utils::test_cases::from_grandpa_chain; use bridge_to_bulletin_config::{ RococoBulletinGlobalConsensusNetwork, RococoBulletinGlobalConsensusNetworkLocation, @@ -818,9 +823,10 @@ fn location_conversion_works() { let expected = AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); - let got = LocationToAccountHelper::::convert_location( - tc.location.into(), - ) + let got = LocationToAccountHelper::< + AccountId, + bridge_hub_rococo_runtime::xcm_config::LocationToAccountId, + >::convert_location(tc.location.into()) .unwrap(); assert_eq!(got, expected, "{}", tc.description); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index 471158d9645..637e7c71064 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -238,6 +238,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm-bridge-hub/runtime-benchmarks", 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 e45654bc62b..aca51b320e9 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 @@ -38,7 +38,7 @@ use frame_support::{ use frame_system::{EnsureNever, EnsureRoot}; use pallet_bridge_messages::LaneIdOf; use pallet_bridge_relayers::extension::{ - BridgeRelayersSignedExtension, WithMessagesExtensionConfig, + BridgeRelayersTransactionExtension, WithMessagesExtensionConfig, }; use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains}; use polkadot_parachain_primitives::primitives::Sibling; @@ -91,8 +91,9 @@ pub type ToRococoBridgeHubMessagesDeliveryProof = type FromRococoMessageBlobDispatcher = BridgeBlobDispatcher; -/// Signed extension that refunds relayers that are delivering messages from the Rococo parachain. -pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = BridgeRelayersSignedExtension< +/// Transaction extension that refunds relayers that are delivering messages from the Rococo +/// parachain. +pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = BridgeRelayersTransactionExtension< Runtime, WithMessagesExtensionConfig< StrOnBridgeHubWestendRefundBridgeHubRococoMessages, 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 a18fc8accc9..85be26d1170 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 @@ -116,8 +116,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -128,13 +128,13 @@ pub type SignedExtra = ( pallet_transaction_payment::ChargeTransactionPayment, BridgeRejectObsoleteHeadersAndMessages, (bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages,), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, frame_metadata_hash_extension::CheckMetadataHash, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -283,6 +283,8 @@ impl frame_system::Config for Runtime { type DbWeight = RocksDbWeight; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = weights::frame_system::WeightInfo; + /// Weight information for the transaction extensions of this pallet. + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; /// Block & extrinsics weights: base values and limits. type BlockWeights = RuntimeBlockWeights; /// The maximum length of a block (in bytes). @@ -343,6 +345,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -589,12 +592,14 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] @@ -925,6 +930,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -954,6 +960,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &alloc::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); @@ -1359,16 +1366,16 @@ mod tests { use codec::Encode; use sp_runtime::{ generic::Era, - traits::{SignedExtension, Zero}, + traits::{TransactionExtension, Zero}, }; #[test] - fn ensure_signed_extension_definition_is_compatible_with_relay() { - use bp_polkadot_core::SuffixedCommonSignedExtensionExt; + fn ensure_transaction_extension_definition_is_compatible_with_relay() { + use bp_polkadot_core::SuffixedCommonTransactionExtensionExt; sp_io::TestExternalities::default().execute_with(|| { frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( + let payload: TxExtension = ( frame_system::CheckNonZeroSender::new(), frame_system::CheckSpecVersion::new(), frame_system::CheckTxVersion::new(), @@ -1381,12 +1388,12 @@ mod tests { ( bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(), ), + frame_metadata_hash_extension::CheckMetadataHash::new(false), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), - frame_metadata_hash_extension::CheckMetadataHash::new(false), ); { - let bh_indirect_payload = bp_bridge_hub_westend::SignedExtension::from_params( + let bh_indirect_payload = bp_bridge_hub_westend::TransactionExtension::from_params( VERSION.spec_version, VERSION.transaction_version, bp_runtime::TransactionEra::Immortal, @@ -1397,8 +1404,8 @@ mod tests { ); assert_eq!(payload.encode().split_last().unwrap().1, bh_indirect_payload.encode()); assert_eq!( - payload.additional_signed().unwrap().encode().split_last().unwrap().1, - bh_indirect_payload.additional_signed().unwrap().encode() + TxExtension::implicit(&payload).unwrap().encode().split_last().unwrap().1, + sp_runtime::traits::TransactionExtension::::implicit(&bh_indirect_payload).unwrap().encode() ) } }); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..459b137d3b8 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,132 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/ +// --chain=bridge-hub-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_166_000 picoseconds. + Weight::from_parts(6_021_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_651_000 picoseconds. + Weight::from_parts(9_177_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_651_000 picoseconds. + Weight::from_parts(9_177_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 601_000 picoseconds. + Weight::from_parts(2_805_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_727_000 picoseconds. + Weight::from_parts(6_051_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 471_000 picoseconds. + Weight::from_parts(2_494_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 521_000 picoseconds. + Weight::from_parts(2_655_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_808_000 picoseconds. + Weight::from_parts(6_402_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs index d60529f9a23..c1c5c337aca 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_bridge_grandpa; pub mod pallet_bridge_messages; @@ -37,6 +38,7 @@ pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..92c53b91879 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/ +// --chain=bridge-hub-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3593` + // Minimum execution time: 40_286_000 picoseconds. + Weight::from_parts(45_816_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs index c5f3871c079..1a1ce2a28ea 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs @@ -22,7 +22,7 @@ use bp_polkadot_core::Signature; use bridge_hub_westend_runtime::{ bridge_to_rococo_config, xcm_config::XcmConfig, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, MessageQueueServiceWeight, Runtime, - RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra, UncheckedExtrinsic, + RuntimeCall, RuntimeEvent, SessionKeys, TxExtension, UncheckedExtrinsic, }; use codec::{Decode, Encode}; use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; @@ -171,7 +171,7 @@ fn construct_extrinsic( call: RuntimeCall, ) -> UncheckedExtrinsic { let account_id = AccountId32::from(sender.public()); - let extra: SignedExtra = ( + let extra: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -184,8 +184,8 @@ fn construct_extrinsic( pallet_transaction_payment::ChargeTransactionPayment::::from(0), BridgeRejectObsoleteHeadersAndMessages::default(), (bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(),), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), frame_metadata_hash_extension::CheckMetadataHash::::new(false), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), ); let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); 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 c5a9b8c53a9..e5b67353c0f 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 @@ -29,7 +29,7 @@ use bridge_hub_westend_runtime::{ xcm_config::{LocationToAccountId, RelayNetwork, WestendLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, - SignedExtra, TransactionPayment, UncheckedExtrinsic, + TransactionPayment, TxExtension, UncheckedExtrinsic, }; use bridge_to_rococo_config::{ BridgeGrandpaRococoInstance, BridgeHubRococoLocation, BridgeParachainRococoInstance, @@ -81,7 +81,7 @@ fn construct_extrinsic( call: RuntimeCall, ) -> UncheckedExtrinsic { let account_id = AccountId32::from(sender.public()); - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -94,12 +94,13 @@ fn construct_extrinsic( pallet_transaction_payment::ChargeTransactionPayment::::from(0), BridgeRejectObsoleteHeadersAndMessages::default(), (bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(),), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), frame_metadata_hash_extension::CheckMetadataHash::new(false), - ); - let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap(); + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + ) + .into(); + let payload = SignedPayload::new(call.clone(), tx_ext.clone()).unwrap(); let signature = payload.using_encoded(|e| sender.sign(e)); - UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), extra) + UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), tx_ext) } fn construct_and_apply_extrinsic( 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 9c5d6269dc0..24372f57ae7 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 @@ -131,7 +131,7 @@ 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().weight, + initialize_call.get_dispatch_info().call_weight, ) .ensure_complete()); @@ -172,7 +172,7 @@ 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().weight, + set_operating_mode_call.get_dispatch_info().call_weight, ) .ensure_complete()); @@ -225,7 +225,7 @@ 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().weight, + set_operating_mode_call.get_dispatch_info().call_weight, ) .ensure_complete()); @@ -278,7 +278,7 @@ 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().weight, + set_operating_mode_call.get_dispatch_info().call_weight, ) .ensure_complete()); diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml index 8a47af18524..e03fc934cea 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml @@ -124,6 +124,7 @@ runtime-benchmarks = [ "pallet-scheduler/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index b516c264e91..030ed930ed5 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -185,6 +185,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = frame_support::traits::ConstU32<16>; @@ -239,6 +240,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -730,8 +732,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -743,7 +745,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// All migrations executed on runtime upgrade as a nested tuple of types implementing /// `OnRuntimeUpgrade`. Included migrations must be idempotent. type Migrations = ( @@ -774,6 +776,7 @@ pub type Executive = frame_executive::Executive< mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] @@ -781,6 +784,7 @@ mod benches { [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] @@ -1044,6 +1048,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -1061,6 +1066,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &alloc::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..f32f2730313 --- /dev/null +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,132 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/ +// --chain=collectives-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_497_000 picoseconds. + Weight::from_parts(5_961_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_240_000 picoseconds. + Weight::from_parts(8_175_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_240_000 picoseconds. + Weight::from_parts(8_175_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 671_000 picoseconds. + Weight::from_parts(3_005_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_426_000 picoseconds. + Weight::from_parts(6_131_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_715_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 491_000 picoseconds. + Weight::from_parts(2_635_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_958_000 picoseconds. + Weight::from_parts(6_753_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs index a9a298e547e..00b3bd92d5e 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs @@ -18,6 +18,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_alliance; pub mod pallet_asset_rate; pub mod pallet_balances; @@ -39,6 +40,7 @@ pub mod pallet_salary_fellowship_salary; pub mod pallet_scheduler; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_treasury; pub mod pallet_utility; pub mod pallet_xcm; diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..5d077b89d56 --- /dev/null +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/ +// --chain=collectives-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 39_815_000 picoseconds. + Weight::from_parts(46_067_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml index dfa75b8d3cf..c98ca7ba3e7 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml @@ -161,6 +161,7 @@ runtime-benchmarks = [ "pallet-multisig/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 6f79780dc17..0111b3d8522 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -87,8 +87,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -101,7 +101,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -250,6 +250,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight; } parameter_types! { @@ -434,6 +435,7 @@ construct_runtime!( mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] @@ -757,6 +759,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -774,6 +777,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &alloc::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml index 80417ea0036..a38b7400cfa 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml @@ -163,6 +163,7 @@ runtime-benchmarks = [ "pallet-proxy/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index d34689deed6..1ce980fa549 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -98,8 +98,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -114,7 +114,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -208,6 +208,8 @@ impl frame_system::Config for Runtime { type DbWeight = RocksDbWeight; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = weights::frame_system::WeightInfo; + /// Weight information for the extensions of this pallet. + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; /// Block & extrinsics weights: base values and limits. type BlockWeights = RuntimeBlockWeights; /// The maximum length of a block (in bytes). @@ -265,6 +267,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..a4d09696a1a --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,132 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/ +// --chain=coretime-rococo-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs index 216f41a5a66..24c4f50e6ab 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs @@ -22,6 +22,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_broker; pub mod pallet_collator_selection; @@ -30,6 +31,7 @@ pub mod pallet_multisig; pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..29d48abab89 --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/ +// --chain=coretime-rococo-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml index 25bf777047d..149fa5d0b04 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml @@ -161,6 +161,7 @@ runtime-benchmarks = [ "pallet-multisig/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index c3516df9aa1..632f2c657cf 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -98,8 +98,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -114,7 +114,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -208,6 +208,8 @@ impl frame_system::Config for Runtime { type DbWeight = RocksDbWeight; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = weights::frame_system::WeightInfo; + /// Weight information for the extensions of this pallet. + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; /// Block & extrinsics weights: base values and limits. type BlockWeights = RuntimeBlockWeights; /// The maximum length of a block (in bytes). @@ -266,6 +268,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..d928b73613a --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,132 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ +// --chain=coretime-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs index 216f41a5a66..24c4f50e6ab 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs @@ -22,6 +22,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_broker; pub mod pallet_collator_selection; @@ -30,6 +31,7 @@ pub mod pallet_multisig; pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..f159f877afe --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/ +// --chain=coretime-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index bc76f174b50..ad656cdbb83 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -290,8 +290,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( pallet_sudo::CheckOnlySudoAccount, frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, @@ -303,7 +303,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -318,6 +318,7 @@ mod benches { frame_benchmarking::define_benchmarks!( [cumulus_pallet_parachain_system, ParachainSystem] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_glutton, Glutton] [pallet_message_queue, MessageQueue] [pallet_timestamp, Timestamp] @@ -447,6 +448,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; let mut list = Vec::::new(); list_benchmarks!(list, extra); @@ -463,6 +465,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime { fn setup_set_code_requirements(code: &alloc::vec::Vec) -> Result<(), BenchmarkError> { ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..4fbbb8d6f78 --- /dev/null +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,130 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("glutton-westend-dev-1300")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/glutton/glutton-westend/src/weights/ +// --chain=glutton-westend-dev-1300 + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_908_000 picoseconds. + Weight::from_parts(4_007_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_510_000 picoseconds. + Weight::from_parts(6_332_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_510_000 picoseconds. + Weight::from_parts(6_332_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 651_000 picoseconds. + Weight::from_parts(851_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_387_000 picoseconds. + Weight::from_parts(3_646_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 491_000 picoseconds. + Weight::from_parts(651_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 451_000 picoseconds. + Weight::from_parts(662_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 3_537_000 picoseconds. + Weight::from_parts(4_208_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml index c969bb2985b..373b82639de 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml @@ -157,6 +157,7 @@ runtime-benchmarks = [ "pallet-multisig/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index b2883a2bbf9..af8e72fa094 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -91,8 +91,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The TransactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -106,7 +106,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -189,6 +189,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = ConstU32<16>; @@ -241,6 +242,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -587,6 +589,7 @@ mod benches { [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] // Polkadot [polkadot_runtime_common::identity_migrator, IdentityMigrator] // Cumulus diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..fb2b69e23e8 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,132 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/ +// --chain=people-rococo-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs index dce959e817b..58480231f06 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs @@ -20,6 +20,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_collator_selection; pub mod pallet_identity; @@ -28,6 +29,7 @@ pub mod pallet_multisig; pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..555fd5a32fa --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/ +// --chain=people-rococo-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml index 64e956d8b6b..efb67adba49 100644 --- a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml @@ -157,6 +157,7 @@ runtime-benchmarks = [ "pallet-multisig/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index f4f2c1ac22b..d0b0bec2e87 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -91,8 +91,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The transactionExtension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -106,7 +106,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -188,6 +188,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = ConstU32<16>; @@ -240,6 +241,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/frame_system_extensions.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..0a4b9e8e268 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,132 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/ +// --chain=people-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_637_000 picoseconds. + Weight::from_parts(6_382_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_841_000 picoseconds. + Weight::from_parts(8_776_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 561_000 picoseconds. + Weight::from_parts(2_705_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_316_000 picoseconds. + Weight::from_parts(5_771_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 511_000 picoseconds. + Weight::from_parts(2_575_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 501_000 picoseconds. + Weight::from_parts(2_595_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::BlockWeight` (r:1 w:1) + /// Proof: `System::BlockWeight` (`max_values`: Some(1), `max_size`: Some(48), added: 543, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1533` + // Minimum execution time: 3_687_000 picoseconds. + Weight::from_parts(6_192_000, 0) + .saturating_add(Weight::from_parts(0, 1533)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs index dce959e817b..58480231f06 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs @@ -20,6 +20,7 @@ pub mod cumulus_pallet_parachain_system; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_balances; pub mod pallet_collator_selection; pub mod pallet_identity; @@ -28,6 +29,7 @@ pub mod pallet_multisig; pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; pub mod paritydb_weights; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_transaction_payment.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..30e4524e586 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,67 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-12-21, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/polkadot-parachain +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=2 +// --repeat=2 +// --json +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/ +// --chain=people-westend-dev + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3593` + // Minimum execution time: 33_363_000 picoseconds. + Weight::from_parts(38_793_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs index 3b38eee244f..36cf2bf4f83 100644 --- a/cumulus/parachains/runtimes/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs @@ -475,7 +475,7 @@ impl< BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited }, Transact { origin_kind: OriginKind::Xcm, - require_weight_at_most: call.get_dispatch_info().weight, + require_weight_at_most: call.get_dispatch_info().call_weight, call: call.encode().into(), }, ExpectTransactStatus(MaybeErrorCode::Success), diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index 96338b64558..14c4fe52038 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -162,6 +162,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 917b3b04a76..2dff159f7ee 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -129,8 +129,8 @@ pub type BlockId = generic::BlockId; // Id used for identifying assets. pub type AssetId = u32; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -143,7 +143,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; pub type Migrations = ( pallet_balances::migration::MigrateToTrackInactive, @@ -435,6 +435,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = (); } parameter_types! { @@ -745,6 +746,19 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = (); } +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetTxHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_tx_payment::BenchmarkHelperTrait for AssetTxHelper { + fn create_asset_id_parameter(_id: u32) -> (u32, u32) { + unimplemented!("Penpal uses default weights"); + } + fn setup_balances_and_pool(_asset_id: u32, _account: AccountId) { + unimplemented!("Penpal uses default weights"); + } +} + impl pallet_asset_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Fungibles = Assets; @@ -757,6 +771,9 @@ impl pallet_asset_tx_payment::Config for Runtime { >, AssetsToBlockAuthor, >; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetTxHelper; } impl pallet_sudo::Config for Runtime { @@ -807,6 +824,7 @@ construct_runtime!( mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] [pallet_session, SessionBench::] @@ -1087,6 +1105,7 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; let mut list = Vec::::new(); @@ -1103,6 +1122,7 @@ impl_runtime_apis! { use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime {} use cumulus_pallet_session_benchmarking::Pallet as SessionBench; diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml index 9c905c87627..bbc1185db0d 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml @@ -126,6 +126,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 5abdc995c00..34bd45b6ef9 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -267,6 +267,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = (); } impl pallet_sudo::Config for Runtime { @@ -655,8 +656,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -669,7 +670,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, diff --git a/cumulus/polkadot-omni-node/lib/Cargo.toml b/cumulus/polkadot-omni-node/lib/Cargo.toml index 3dd482f4ada..a690229f169 100644 --- a/cumulus/polkadot-omni-node/lib/Cargo.toml +++ b/cumulus/polkadot-omni-node/lib/Cargo.toml @@ -105,6 +105,7 @@ runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-cli/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", diff --git a/cumulus/primitives/storage-weight-reclaim/Cargo.toml b/cumulus/primitives/storage-weight-reclaim/Cargo.toml index 3a98fdd017a..e1ae6743335 100644 --- a/cumulus/primitives/storage-weight-reclaim/Cargo.toml +++ b/cumulus/primitives/storage-weight-reclaim/Cargo.toml @@ -14,6 +14,7 @@ codec = { features = ["derive"], workspace = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } +frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -24,8 +25,8 @@ cumulus-primitives-proof-size-hostfunction = { workspace = true } docify = { workspace = true } [dev-dependencies] -sp-trie = { workspace = true } sp-io = { workspace = true } +sp-trie = { workspace = true } cumulus-test-runtime = { workspace = true } [features] @@ -34,6 +35,7 @@ std = [ "codec/std", "cumulus-primitives-core/std", "cumulus-primitives-proof-size-hostfunction/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "log/std", diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs index 2529297691e..5471640695c 100644 --- a/cumulus/primitives/storage-weight-reclaim/src/lib.rs +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -30,11 +30,15 @@ use frame_support::{ use frame_system::Config; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension}, + impl_tx_ext_default, + traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, TransactionExtension}, transaction_validity::TransactionValidityError, DispatchResult, }; +#[cfg(test)] +mod tests; + const LOG_TARGET: &'static str = "runtime::storage_reclaim"; /// `StorageWeightReclaimer` is a mechanism for manually reclaiming storage weight. @@ -43,7 +47,7 @@ const LOG_TARGET: &'static str = "runtime::storage_reclaim"; /// reclaim it computes the real consumed storage weight and refunds excess weight. /// /// # Example -#[doc = docify::embed!("src/lib.rs", simple_reclaimer_example)] +#[doc = docify::embed!("src/tests.rs", simple_reclaimer_example)] pub struct StorageWeightReclaimer { previous_remaining_proof_size: u64, previous_reported_proof_size: Option, @@ -119,43 +123,35 @@ impl core::fmt::Debug for StorageWeightReclaim { } } -impl SignedExtension for StorageWeightReclaim +impl TransactionExtension for StorageWeightReclaim where T::RuntimeCall: Dispatchable, { const IDENTIFIER: &'static str = "StorageWeightReclaim"; - - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); + type Implicit = (); + type Val = (); type Pre = Option; - fn additional_signed( - &self, - ) -> Result - { - Ok(()) - } - - fn pre_dispatch( + fn prepare( self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, + _val: Self::Val, + _origin: &T::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> Result { + ) -> Result { Ok(get_proof_size()) } - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, _len: usize, _result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - let Some(Some(pre_dispatch_proof_size)) = pre else { - return Ok(()); + ) -> Result { + let Some(pre_dispatch_proof_size) = pre else { + return Ok(Weight::zero()); }; let Some(post_dispatch_proof_size) = get_proof_size() else { @@ -163,13 +159,13 @@ where target: LOG_TARGET, "Proof recording enabled during pre-dispatch, now disabled. This should not happen." ); - return Ok(()) + return Ok(Weight::zero()) }; // Unspent weight according to the `actual_weight` from `PostDispatchInfo` // This unspent weight will be refunded by the `CheckWeight` extension, so we need to // account for that. let unspent = post_info.calc_unspent(info).proof_size(); - let benchmarked_weight = info.weight.proof_size().saturating_sub(unspent); + let benchmarked_weight = info.total_weight().proof_size().saturating_sub(unspent); let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size); let storage_size_diff = benchmarked_weight.abs_diff(consumed_weight as u64); @@ -209,678 +205,8 @@ where current.accrue(Weight::from_parts(0, missing_from_node), info.class); } }); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use core::marker::PhantomData; - use frame_support::{ - assert_ok, - dispatch::{DispatchClass, PerDispatchClass}, - weights::{Weight, WeightMeter}, - }; - use frame_system::{BlockWeight, CheckWeight}; - use sp_runtime::{AccountId32, BuildStorage}; - use sp_trie::proof_size_extension::ProofSizeExt; - - type Test = cumulus_test_runtime::Runtime; - const CALL: &::RuntimeCall = - &cumulus_test_runtime::RuntimeCall::System(frame_system::Call::set_heap_pages { - pages: 0u64, - }); - const ALICE: AccountId32 = AccountId32::new([1u8; 32]); - const LEN: usize = 150; - - pub fn new_test_ext() -> sp_io::TestExternalities { - let ext: sp_io::TestExternalities = cumulus_test_runtime::RuntimeGenesisConfig::default() - .build_storage() - .unwrap() - .into(); - ext - } - - struct TestRecorder { - return_values: Box<[usize]>, - counter: std::sync::atomic::AtomicUsize, - } - - impl TestRecorder { - fn new(values: &[usize]) -> Self { - TestRecorder { return_values: values.into(), counter: Default::default() } - } - } - - impl sp_trie::ProofSizeProvider for TestRecorder { - fn estimate_encoded_size(&self) -> usize { - let counter = self.counter.fetch_add(1, core::sync::atomic::Ordering::Relaxed); - self.return_values[counter] - } - } - - fn setup_test_externalities(proof_values: &[usize]) -> sp_io::TestExternalities { - let mut test_ext = new_test_ext(); - let test_recorder = TestRecorder::new(proof_values); - test_ext.register_extension(ProofSizeExt::new(test_recorder)); - test_ext - } - - fn set_current_storage_weight(new_weight: u64) { - BlockWeight::::mutate(|current_weight| { - current_weight.set(Weight::from_parts(0, new_weight), DispatchClass::Normal); - }); - } - - fn get_storage_weight() -> PerDispatchClass { - BlockWeight::::get() - } - - #[test] - fn basic_refund() { - // The real cost will be 100 bytes of storage size - let mut test_ext = setup_test_externalities(&[0, 100]); - - test_ext.execute_with(|| { - set_current_storage_weight(1000); - - // Benchmarked storage weight: 500 - let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; - let post_info = PostDispatchInfo::default(); - - // Should add 500 + 150 (len) to weight. - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(0)); - - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - // We expect a refund of 400 - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - assert_eq!(get_storage_weight().total().proof_size(), 1250); - }) - } - - #[test] - fn underestimating_refund() { - // We fixed a bug where `pre dispatch info weight > consumed weight > post info weight` - // resulted in error. - - // The real cost will be 100 bytes of storage size - let mut test_ext = setup_test_externalities(&[0, 100]); - - test_ext.execute_with(|| { - set_current_storage_weight(1000); - - // Benchmarked storage weight: 500 - let info = DispatchInfo { weight: Weight::from_parts(0, 101), ..Default::default() }; - let post_info = PostDispatchInfo { - actual_weight: Some(Weight::from_parts(0, 99)), - pays_fee: Default::default(), - }; - - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(0)); - - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - // We expect an accrue of 1 - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - assert_eq!(get_storage_weight().total().proof_size(), 1250); - }) - } - - #[test] - fn sets_to_node_storage_proof_if_higher() { - // The storage proof reported by the proof recorder is higher than what is stored on - // the runtime side. - { - let mut test_ext = setup_test_externalities(&[1000, 1005]); - - test_ext.execute_with(|| { - // Stored in BlockWeight is 5 - set_current_storage_weight(5); - - // Benchmarked storage weight: 10 - let info = DispatchInfo { weight: Weight::from_parts(0, 10), ..Default::default() }; - let post_info = PostDispatchInfo::default(); - - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(1000)); - - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - // We expect that the storage weight was set to the node-side proof size (1005) + - // extrinsics length (150) - assert_eq!(get_storage_weight().total().proof_size(), 1155); - }) - } - - // In this second scenario the proof size on the node side is only lower - // after reclaim happened. - { - let mut test_ext = setup_test_externalities(&[175, 180]); - test_ext.execute_with(|| { - set_current_storage_weight(85); - - // Benchmarked storage weight: 100 - let info = - DispatchInfo { weight: Weight::from_parts(0, 100), ..Default::default() }; - let post_info = PostDispatchInfo::default(); - - // After this pre_dispatch, the BlockWeight proof size will be - // 85 (initial) + 100 (benched) + 150 (tx length) = 335 - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(175)); - - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - - // First we will reclaim 95, which leaves us with 240 BlockWeight. This is lower - // than 180 (proof size hf) + 150 (length), so we expect it to be set to 330. - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - // We expect that the storage weight was set to the node-side proof weight - assert_eq!(get_storage_weight().total().proof_size(), 330); - }) - } - } - - #[test] - fn does_nothing_without_extension() { - let mut test_ext = new_test_ext(); - - // Proof size extension not registered - test_ext.execute_with(|| { - set_current_storage_weight(1000); - - // Benchmarked storage weight: 500 - let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; - let post_info = PostDispatchInfo::default(); - - // Adds 500 + 150 (len) weight - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, None); - - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - assert_eq!(get_storage_weight().total().proof_size(), 1650); - }) - } - - #[test] - fn negative_refund_is_added_to_weight() { - let mut test_ext = setup_test_externalities(&[100, 300]); - - test_ext.execute_with(|| { - set_current_storage_weight(1000); - // Benchmarked storage weight: 100 - let info = DispatchInfo { weight: Weight::from_parts(0, 100), ..Default::default() }; - let post_info = PostDispatchInfo::default(); - - // Weight added should be 100 + 150 (len) - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(100)); - - // We expect no refund - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - assert_eq!( - get_storage_weight().total().proof_size(), - 1100 + LEN as u64 + info.weight.proof_size() - ); - }) - } - - #[test] - fn test_zero_proof_size() { - let mut test_ext = setup_test_externalities(&[0, 0]); - - test_ext.execute_with(|| { - let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; - let post_info = PostDispatchInfo::default(); - - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(0)); - - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - // Proof size should be exactly equal to extrinsic length - assert_eq!(get_storage_weight().total().proof_size(), LEN as u64); - }); - } - - #[test] - fn test_larger_pre_dispatch_proof_size() { - let mut test_ext = setup_test_externalities(&[300, 100]); - - test_ext.execute_with(|| { - set_current_storage_weight(1300); - - let info = DispatchInfo { weight: Weight::from_parts(0, 500), ..Default::default() }; - let post_info = PostDispatchInfo::default(); - - // Adds 500 + 150 (len) weight, total weight is 1950 - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(300)); - - // Refund 500 unspent weight according to `post_info`, total weight is now 1650 - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - // Recorded proof size is negative -200, total weight is now 1450 - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - assert_eq!(get_storage_weight().total().proof_size(), 1450); - }); - } - - #[test] - fn test_incorporates_check_weight_unspent_weight() { - let mut test_ext = setup_test_externalities(&[100, 300]); - - test_ext.execute_with(|| { - set_current_storage_weight(1000); - - // Benchmarked storage weight: 300 - let info = DispatchInfo { weight: Weight::from_parts(100, 300), ..Default::default() }; - - // Actual weight is 50 - let post_info = PostDispatchInfo { - actual_weight: Some(Weight::from_parts(50, 250)), - pays_fee: Default::default(), - }; - - // Should add 300 + 150 (len) of weight - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(100)); - - // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` - // we always need to call `post_dispatch` to verify that they interoperate correctly. - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - // Reclaimed 100 - assert_eq!(get_storage_weight().total().proof_size(), 1350); - }) - } - - #[test] - fn test_incorporates_check_weight_unspent_weight_on_negative() { - let mut test_ext = setup_test_externalities(&[100, 300]); - - test_ext.execute_with(|| { - set_current_storage_weight(1000); - // Benchmarked storage weight: 50 - let info = DispatchInfo { weight: Weight::from_parts(100, 50), ..Default::default() }; - - // Actual weight is 25 - let post_info = PostDispatchInfo { - actual_weight: Some(Weight::from_parts(50, 25)), - pays_fee: Default::default(), - }; - - // Adds 50 + 150 (len) weight, total weight 1200 - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(100)); - - // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` - // we always need to call `post_dispatch` to verify that they interoperate correctly. - - // Refunds unspent 25 weight according to `post_info`, 1175 - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - // Adds 200 - 25 (unspent) == 175 weight, total weight 1350 - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - assert_eq!(get_storage_weight().total().proof_size(), 1350); - }) - } - - #[test] - fn test_nothing_relcaimed() { - let mut test_ext = setup_test_externalities(&[0, 100]); - - test_ext.execute_with(|| { - set_current_storage_weight(0); - // Benchmarked storage weight: 100 - let info = DispatchInfo { weight: Weight::from_parts(100, 100), ..Default::default() }; - - // Actual proof size is 100 - let post_info = PostDispatchInfo { - actual_weight: Some(Weight::from_parts(50, 100)), - pays_fee: Default::default(), - }; - - // Adds benchmarked weight 100 + 150 (len), total weight is now 250 - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - // Weight should go up by 150 len + 100 proof size weight, total weight 250 - assert_eq!(get_storage_weight().total().proof_size(), 250); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - // Should return `setup_test_externalities` proof recorder value: 100. - assert_eq!(pre, Some(0)); - - // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` - // we always need to call `post_dispatch` to verify that they interoperate correctly. - // Nothing to refund, unspent is 0, total weight 250 - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, LEN, &Ok(()))); - // `setup_test_externalities` proof recorder value: 200, so this means the extrinsic - // actually used 100 proof size. - // Nothing to refund or add, weight matches proof recorder - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - - // Check block len weight was not reclaimed: - // 100 weight + 150 extrinsic len == 250 proof size - assert_eq!(get_storage_weight().total().proof_size(), 250); - }) - } - - #[test] - fn test_incorporates_check_weight_unspent_weight_reverse_order() { - let mut test_ext = setup_test_externalities(&[100, 300]); - - test_ext.execute_with(|| { - set_current_storage_weight(1000); - - // Benchmarked storage weight: 300 - let info = DispatchInfo { weight: Weight::from_parts(100, 300), ..Default::default() }; - - // Actual weight is 50 - let post_info = PostDispatchInfo { - actual_weight: Some(Weight::from_parts(50, 250)), - pays_fee: Default::default(), - }; - - // Adds 300 + 150 (len) weight, total weight 1450 - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(100)); - - // This refunds 100 - 50(unspent), total weight is now 1400 - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - // `CheckWeight` gets called after `StorageWeightReclaim` this time. - // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` - // we always need to call `post_dispatch` to verify that they interoperate correctly. - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - - // Above call refunds 50 (unspent), total weight is 1350 now - assert_eq!(get_storage_weight().total().proof_size(), 1350); - }) - } - - #[test] - fn test_incorporates_check_weight_unspent_weight_on_negative_reverse_order() { - let mut test_ext = setup_test_externalities(&[100, 300]); - - test_ext.execute_with(|| { - set_current_storage_weight(1000); - // Benchmarked storage weight: 50 - let info = DispatchInfo { weight: Weight::from_parts(100, 50), ..Default::default() }; - - // Actual weight is 25 - let post_info = PostDispatchInfo { - actual_weight: Some(Weight::from_parts(50, 25)), - pays_fee: Default::default(), - }; - - // Adds 50 + 150 (len) weight, total weight is 1200 - assert_ok!(CheckWeight::::do_pre_dispatch(&info, LEN)); - - let pre = StorageWeightReclaim::(PhantomData) - .pre_dispatch(&ALICE, CALL, &info, LEN) - .unwrap(); - assert_eq!(pre, Some(100)); - - // Adds additional 150 weight recorded - assert_ok!(StorageWeightReclaim::::post_dispatch( - Some(pre), - &info, - &post_info, - LEN, - &Ok(()) - )); - // `CheckWeight` gets called after `StorageWeightReclaim` this time. - // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` - // we always need to call `post_dispatch` to verify that they interoperate correctly. - assert_ok!(CheckWeight::::post_dispatch(None, &info, &post_info, 0, &Ok(()))); - - assert_eq!(get_storage_weight().total().proof_size(), 1350); - }) - } - - #[test] - fn storage_size_reported_correctly() { - let mut test_ext = setup_test_externalities(&[1000]); - test_ext.execute_with(|| { - assert_eq!(get_proof_size(), Some(1000)); - }); - - let mut test_ext = new_test_ext(); - - let test_recorder = TestRecorder::new(&[0]); - - test_ext.register_extension(ProofSizeExt::new(test_recorder)); - - test_ext.execute_with(|| { - assert_eq!(get_proof_size(), Some(0)); - }); - } - - #[test] - fn storage_size_disabled_reported_correctly() { - let mut test_ext = setup_test_externalities(&[PROOF_RECORDING_DISABLED as usize]); - - test_ext.execute_with(|| { - assert_eq!(get_proof_size(), None); - }); - } - - #[test] - fn test_reclaim_helper() { - let mut test_ext = setup_test_externalities(&[1000, 1300, 1800]); - - test_ext.execute_with(|| { - let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 2000)); - let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); - remaining_weight_meter.consume(Weight::from_parts(0, 500)); - let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); - - assert_eq!(reclaimed, Some(Weight::from_parts(0, 200))); - - remaining_weight_meter.consume(Weight::from_parts(0, 800)); - let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); - assert_eq!(reclaimed, Some(Weight::from_parts(0, 300))); - assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(0, 1200)); - }); + Ok(Weight::zero()) } - #[test] - fn test_reclaim_helper_does_not_reclaim_negative() { - // Benchmarked weight does not change at all - let mut test_ext = setup_test_externalities(&[1000, 1300]); - - test_ext.execute_with(|| { - let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 1000)); - let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); - let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); - - assert_eq!(reclaimed, Some(Weight::from_parts(0, 0))); - assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(0, 1000)); - }); - - // Benchmarked weight increases less than storage proof consumes - let mut test_ext = setup_test_externalities(&[1000, 1300]); - - test_ext.execute_with(|| { - let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 1000)); - let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); - remaining_weight_meter.consume(Weight::from_parts(0, 0)); - let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); - - assert_eq!(reclaimed, Some(Weight::from_parts(0, 0))); - }); - } - - /// Just here for doc purposes - fn get_benched_weight() -> Weight { - Weight::from_parts(0, 5) - } - - /// Just here for doc purposes - fn do_work() {} - - #[docify::export_content(simple_reclaimer_example)] - fn reclaim_with_weight_meter() { - let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(10, 10)); - - let benched_weight = get_benched_weight(); - - // It is important to instantiate the `StorageWeightReclaimer` before we consume the weight - // for a piece of work from the weight meter. - let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); - - if remaining_weight_meter.try_consume(benched_weight).is_ok() { - // Perform some work that takes has `benched_weight` storage weight. - do_work(); - - // Reclaimer will detect that we only consumed 2 bytes, so 3 bytes are reclaimed. - let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); - - // We reclaimed 3 bytes of storage size! - assert_eq!(reclaimed, Some(Weight::from_parts(0, 3))); - assert_eq!(get_storage_weight().total().proof_size(), 10); - assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(10, 8)); - } - } - - #[test] - fn test_reclaim_helper_works_with_meter() { - // The node will report 12 - 10 = 2 consumed storage size between the calls. - let mut test_ext = setup_test_externalities(&[10, 12]); - - test_ext.execute_with(|| { - // Initial storage size is 10. - set_current_storage_weight(10); - reclaim_with_weight_meter(); - }); - } + impl_tx_ext_default!(T::RuntimeCall; weight validate); } diff --git a/cumulus/primitives/storage-weight-reclaim/src/tests.rs b/cumulus/primitives/storage-weight-reclaim/src/tests.rs new file mode 100644 index 00000000000..c5552b0f0a3 --- /dev/null +++ b/cumulus/primitives/storage-weight-reclaim/src/tests.rs @@ -0,0 +1,706 @@ +// 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::{ + assert_ok, + dispatch::{DispatchClass, PerDispatchClass}, + weights::{Weight, WeightMeter}, +}; +use frame_system::{BlockWeight, CheckWeight}; +use sp_runtime::{traits::DispatchTransaction, AccountId32, BuildStorage}; +use sp_trie::proof_size_extension::ProofSizeExt; + +type Test = cumulus_test_runtime::Runtime; +const CALL: &::RuntimeCall = + &cumulus_test_runtime::RuntimeCall::System(frame_system::Call::set_heap_pages { pages: 0u64 }); +const ALICE: AccountId32 = AccountId32::new([1u8; 32]); +const LEN: usize = 150; + +fn new_test_ext() -> sp_io::TestExternalities { + let ext: sp_io::TestExternalities = cumulus_test_runtime::RuntimeGenesisConfig::default() + .build_storage() + .unwrap() + .into(); + ext +} + +struct TestRecorder { + return_values: Box<[usize]>, + counter: core::sync::atomic::AtomicUsize, +} + +impl TestRecorder { + fn new(values: &[usize]) -> Self { + TestRecorder { return_values: values.into(), counter: Default::default() } + } +} + +impl sp_trie::ProofSizeProvider for TestRecorder { + fn estimate_encoded_size(&self) -> usize { + let counter = self.counter.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + self.return_values[counter] + } +} + +fn setup_test_externalities(proof_values: &[usize]) -> sp_io::TestExternalities { + let mut test_ext = new_test_ext(); + let test_recorder = TestRecorder::new(proof_values); + test_ext.register_extension(ProofSizeExt::new(test_recorder)); + test_ext +} + +fn set_current_storage_weight(new_weight: u64) { + BlockWeight::::mutate(|current_weight| { + current_weight.set(Weight::from_parts(0, new_weight), DispatchClass::Normal); + }); +} + +fn get_storage_weight() -> PerDispatchClass { + BlockWeight::::get() +} + +#[test] +fn basic_refund() { + // The real cost will be 100 bytes of storage size + let mut test_ext = setup_test_externalities(&[0, 100]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 500 + let info = DispatchInfo { call_weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + // Should add 500 + 150 (len) to weight. + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(0)); + + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()),)); + // We expect a refund of 400 + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()), + )); + + assert_eq!(get_storage_weight().total().proof_size(), 1250); + }) +} + +#[test] +fn underestimating_refund() { + // We fixed a bug where `pre dispatch info weight > consumed weight > post info weight` + // resulted in error. + + // The real cost will be 100 bytes of storage size + let mut test_ext = setup_test_externalities(&[0, 100]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 500 + let info = DispatchInfo { call_weight: Weight::from_parts(0, 101), ..Default::default() }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(0, 99)), + pays_fee: Default::default(), + }; + + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(0)); + + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()))); + // We expect an accrue of 1 + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()) + )); + + assert_eq!(get_storage_weight().total().proof_size(), 1250); + }) +} + +#[test] +fn sets_to_node_storage_proof_if_higher() { + // The storage proof reported by the proof recorder is higher than what is stored on + // the runtime side. + { + let mut test_ext = setup_test_externalities(&[1000, 1005]); + + test_ext.execute_with(|| { + // Stored in BlockWeight is 5 + set_current_storage_weight(5); + + // Benchmarked storage weight: 10 + let info = + DispatchInfo { call_weight: Weight::from_parts(0, 10), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(1000)); + + assert_ok!(CheckWeight::::post_dispatch_details( + (), + &info, + &post_info, + 0, + &Ok(()) + )); + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()) + )); + + // We expect that the storage weight was set to the node-side proof size (1005) + + // extrinsics length (150) + assert_eq!(get_storage_weight().total().proof_size(), 1155); + }) + } + + // In this second scenario the proof size on the node side is only lower + // after reclaim happened. + { + let mut test_ext = setup_test_externalities(&[175, 180]); + test_ext.execute_with(|| { + set_current_storage_weight(85); + + // Benchmarked storage weight: 100 + let info = + DispatchInfo { call_weight: Weight::from_parts(0, 100), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + // After this pre_dispatch, the BlockWeight proof size will be + // 85 (initial) + 100 (benched) + 150 (tx length) = 335 + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(175)); + + assert_ok!(CheckWeight::::post_dispatch_details( + (), + &info, + &post_info, + 0, + &Ok(()) + )); + + // First we will reclaim 95, which leaves us with 240 BlockWeight. This is lower + // than 180 (proof size hf) + 150 (length), so we expect it to be set to 330. + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()) + )); + + // We expect that the storage weight was set to the node-side proof weight + assert_eq!(get_storage_weight().total().proof_size(), 330); + }) + } +} + +#[test] +fn does_nothing_without_extension() { + let mut test_ext = new_test_ext(); + + // Proof size extension not registered + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 500 + let info = DispatchInfo { call_weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + // Adds 500 + 150 (len) weight + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, None); + + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()),)); + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()), + )); + + assert_eq!(get_storage_weight().total().proof_size(), 1650); + }) +} + +#[test] +fn negative_refund_is_added_to_weight() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + // Benchmarked storage weight: 100 + let info = DispatchInfo { call_weight: Weight::from_parts(0, 100), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + // Weight added should be 100 + 150 (len) + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // We expect no refund + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()),)); + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()), + )); + + assert_eq!( + get_storage_weight().total().proof_size(), + 1100 + LEN as u64 + info.total_weight().proof_size() + ); + }) +} + +#[test] +fn test_zero_proof_size() { + let mut test_ext = setup_test_externalities(&[0, 0]); + + test_ext.execute_with(|| { + let info = DispatchInfo { call_weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(0)); + + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()),)); + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()), + )); + + // Proof size should be exactly equal to extrinsic length + assert_eq!(get_storage_weight().total().proof_size(), LEN as u64); + }); +} + +#[test] +fn test_larger_pre_dispatch_proof_size() { + let mut test_ext = setup_test_externalities(&[300, 100]); + + test_ext.execute_with(|| { + set_current_storage_weight(1300); + + let info = DispatchInfo { call_weight: Weight::from_parts(0, 500), ..Default::default() }; + let post_info = PostDispatchInfo::default(); + + // Adds 500 + 150 (len) weight, total weight is 1950 + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(300)); + + // Refund 500 unspent weight according to `post_info`, total weight is now 1650 + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()),)); + // Recorded proof size is negative -200, total weight is now 1450 + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()), + )); + + assert_eq!(get_storage_weight().total().proof_size(), 1450); + }); +} + +#[test] +fn test_incorporates_check_weight_unspent_weight() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 300 + let info = DispatchInfo { call_weight: Weight::from_parts(100, 300), ..Default::default() }; + + // Actual weight is 50 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 250)), + pays_fee: Default::default(), + }; + + // Should add 300 + 150 (len) of weight + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // The `CheckWeight` extension will refunt `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()),)); + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()), + )); + + // Reclaimed 100 + assert_eq!(get_storage_weight().total().proof_size(), 1350); + }) +} + +#[test] +fn test_incorporates_check_weight_unspent_weight_on_negative() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + // Benchmarked storage weight: 50 + let info = DispatchInfo { call_weight: Weight::from_parts(100, 50), ..Default::default() }; + + // Actual weight is 25 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 25)), + pays_fee: Default::default(), + }; + + // Adds 50 + 150 (len) weight, total weight 1200 + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // The `CheckWeight` extension will refunt `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + // Refunds unspent 25 weight according to `post_info`, 1175 + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()),)); + // Adds 200 - 25 (unspent) == 175 weight, total weight 1350 + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()), + )); + + assert_eq!(get_storage_weight().total().proof_size(), 1350); + }) +} + +#[test] +fn test_nothing_relcaimed() { + let mut test_ext = setup_test_externalities(&[0, 100]); + + test_ext.execute_with(|| { + set_current_storage_weight(0); + // Benchmarked storage weight: 100 + let info = DispatchInfo { call_weight: Weight::from_parts(100, 100), ..Default::default() }; + + // Actual proof size is 100 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 100)), + pays_fee: Default::default(), + }; + + // Adds benchmarked weight 100 + 150 (len), total weight is now 250 + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + // Weight should go up by 150 len + 100 proof size weight, total weight 250 + assert_eq!(get_storage_weight().total().proof_size(), 250); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + // Should return `setup_test_externalities` proof recorder value: 100. + assert_eq!(pre, Some(0)); + + // The `CheckWeight` extension will refund `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + // Nothing to refund, unspent is 0, total weight 250 + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, LEN, &Ok(()))); + // `setup_test_externalities` proof recorder value: 200, so this means the extrinsic + // actually used 100 proof size. + // Nothing to refund or add, weight matches proof recorder + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()) + )); + + // Check block len weight was not reclaimed: + // 100 weight + 150 extrinsic len == 250 proof size + assert_eq!(get_storage_weight().total().proof_size(), 250); + }) +} + +#[test] +fn test_incorporates_check_weight_unspent_weight_reverse_order() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + + // Benchmarked storage weight: 300 + let info = DispatchInfo { call_weight: Weight::from_parts(100, 300), ..Default::default() }; + + // Actual weight is 50 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 250)), + pays_fee: Default::default(), + }; + + // Adds 300 + 150 (len) weight, total weight 1450 + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // This refunds 100 - 50(unspent), total weight is now 1400 + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()), + )); + // `CheckWeight` gets called after `StorageWeightReclaim` this time. + // The `CheckWeight` extension will refunt `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()),)); + + // Above call refunds 50 (unspent), total weight is 1350 now + assert_eq!(get_storage_weight().total().proof_size(), 1350); + }) +} + +#[test] +fn test_incorporates_check_weight_unspent_weight_on_negative_reverse_order() { + let mut test_ext = setup_test_externalities(&[100, 300]); + + test_ext.execute_with(|| { + set_current_storage_weight(1000); + // Benchmarked storage weight: 50 + let info = DispatchInfo { call_weight: Weight::from_parts(100, 50), ..Default::default() }; + + // Actual weight is 25 + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(50, 25)), + pays_fee: Default::default(), + }; + + // Adds 50 + 150 (len) weight, total weight is 1200 + let (_, next_len) = CheckWeight::::do_validate(&info, LEN).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&info, LEN, next_len)); + + let (pre, _) = StorageWeightReclaim::(PhantomData) + .validate_and_prepare(Some(ALICE.clone()).into(), CALL, &info, LEN) + .unwrap(); + assert_eq!(pre, Some(100)); + + // Adds additional 150 weight recorded + assert_ok!(StorageWeightReclaim::::post_dispatch_details( + pre, + &info, + &post_info, + LEN, + &Ok(()), + )); + // `CheckWeight` gets called after `StorageWeightReclaim` this time. + // The `CheckWeight` extension will refunt `actual_weight` from `PostDispatchInfo` + // we always need to call `post_dispatch` to verify that they interoperate correctly. + assert_ok!(CheckWeight::::post_dispatch_details((), &info, &post_info, 0, &Ok(()),)); + + assert_eq!(get_storage_weight().total().proof_size(), 1350); + }) +} + +#[test] +fn storage_size_reported_correctly() { + let mut test_ext = setup_test_externalities(&[1000]); + test_ext.execute_with(|| { + assert_eq!(get_proof_size(), Some(1000)); + }); + + let mut test_ext = new_test_ext(); + + let test_recorder = TestRecorder::new(&[0]); + + test_ext.register_extension(ProofSizeExt::new(test_recorder)); + + test_ext.execute_with(|| { + assert_eq!(get_proof_size(), Some(0)); + }); +} + +#[test] +fn storage_size_disabled_reported_correctly() { + let mut test_ext = setup_test_externalities(&[PROOF_RECORDING_DISABLED as usize]); + + test_ext.execute_with(|| { + assert_eq!(get_proof_size(), None); + }); +} + +#[test] +fn test_reclaim_helper() { + let mut test_ext = setup_test_externalities(&[1000, 1300, 1800]); + + test_ext.execute_with(|| { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 2000)); + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + remaining_weight_meter.consume(Weight::from_parts(0, 500)); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + assert_eq!(reclaimed, Some(Weight::from_parts(0, 200))); + + remaining_weight_meter.consume(Weight::from_parts(0, 800)); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + assert_eq!(reclaimed, Some(Weight::from_parts(0, 300))); + assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(0, 1200)); + }); +} + +#[test] +fn test_reclaim_helper_does_not_reclaim_negative() { + // Benchmarked weight does not change at all + let mut test_ext = setup_test_externalities(&[1000, 1300]); + + test_ext.execute_with(|| { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 1000)); + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + assert_eq!(reclaimed, Some(Weight::from_parts(0, 0))); + assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(0, 1000)); + }); + + // Benchmarked weight increases less than storage proof consumes + let mut test_ext = setup_test_externalities(&[1000, 1300]); + + test_ext.execute_with(|| { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(0, 1000)); + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + remaining_weight_meter.consume(Weight::from_parts(0, 0)); + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + assert_eq!(reclaimed, Some(Weight::from_parts(0, 0))); + }); +} + +/// Just here for doc purposes +fn get_benched_weight() -> Weight { + Weight::from_parts(0, 5) +} + +/// Just here for doc purposes +fn do_work() {} + +#[docify::export_content(simple_reclaimer_example)] +fn reclaim_with_weight_meter() { + let mut remaining_weight_meter = WeightMeter::with_limit(Weight::from_parts(10, 10)); + + let benched_weight = get_benched_weight(); + + // It is important to instantiate the `StorageWeightReclaimer` before we consume the weight + // for a piece of work from the weight meter. + let mut reclaim_helper = StorageWeightReclaimer::new(&remaining_weight_meter); + + if remaining_weight_meter.try_consume(benched_weight).is_ok() { + // Perform some work that takes has `benched_weight` storage weight. + do_work(); + + // Reclaimer will detect that we only consumed 2 bytes, so 3 bytes are reclaimed. + let reclaimed = reclaim_helper.reclaim_with_meter(&mut remaining_weight_meter); + + // We reclaimed 3 bytes of storage size! + assert_eq!(reclaimed, Some(Weight::from_parts(0, 3))); + assert_eq!(get_storage_weight().total().proof_size(), 10); + assert_eq!(remaining_weight_meter.remaining(), Weight::from_parts(10, 8)); + } +} + +#[test] +fn test_reclaim_helper_works_with_meter() { + // The node will report 12 - 10 = 2 consumed storage size between the calls. + let mut test_ext = setup_test_externalities(&[10, 12]); + + test_ext.execute_with(|| { + // Initial storage size is 10. + set_current_storage_weight(10); + reclaim_with_weight_meter(); + }); +} diff --git a/cumulus/test/client/Cargo.toml b/cumulus/test/client/Cargo.toml index fbbaab73ce7..33023816c71 100644 --- a/cumulus/test/client/Cargo.toml +++ b/cumulus/test/client/Cargo.toml @@ -53,6 +53,7 @@ runtime-benchmarks = [ "cumulus-test-service/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "sc-service/runtime-benchmarks", diff --git a/cumulus/test/client/src/lib.rs b/cumulus/test/client/src/lib.rs index f26413e441e..eaf81699f6d 100644 --- a/cumulus/test/client/src/lib.rs +++ b/cumulus/test/client/src/lib.rs @@ -25,7 +25,7 @@ pub use polkadot_parachain_primitives::primitives::{ BlockData, HeadData, ValidationParams, ValidationResult, }; use runtime::{ - Balance, Block, BlockHashCount, Runtime, RuntimeCall, Signature, SignedExtra, SignedPayload, + Balance, Block, BlockHashCount, Runtime, RuntimeCall, Signature, SignedPayload, TxExtension, UncheckedExtrinsic, VERSION, }; use sc_consensus_aura::standalone::{seal, slot_author}; @@ -117,7 +117,7 @@ impl DefaultTestClientBuilderExt for TestClientBuilder { /// Create an unsigned extrinsic from a runtime call. pub fn generate_unsigned(function: impl Into) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_unsigned(function.into()) + UncheckedExtrinsic::new_bare(function.into()) } /// Create a signed extrinsic from a runtime call and sign @@ -135,7 +135,7 @@ pub fn generate_extrinsic_with_pair( let period = BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckGenesis::::new(), @@ -144,13 +144,14 @@ pub fn generate_extrinsic_with_pair( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::::new(), - ); + ) + .into(); let function = function.into(); let raw_payload = SignedPayload::from_raw( function.clone(), - extra.clone(), + tx_ext.clone(), ((), VERSION.spec_version, genesis_block, current_block_hash, (), (), (), ()), ); let signature = raw_payload.using_encoded(|e| origin.sign(e)); @@ -159,7 +160,7 @@ pub fn generate_extrinsic_with_pair( function, origin.public().into(), Signature::Sr25519(signature), - extra, + tx_ext, ) } diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 92a6ab73a2e..5443bb5f526 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -279,6 +279,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight; } impl pallet_sudo::Config for Runtime { @@ -371,8 +372,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckGenesis, @@ -384,7 +385,7 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -395,7 +396,7 @@ pub type Executive = frame_executive::Executive< TestOnRuntimeUpgrade, >; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; pub struct TestOnRuntimeUpgrade; diff --git a/cumulus/test/service/Cargo.toml b/cumulus/test/service/Cargo.toml index a1b70c52395..3ef9424b9ed 100644 --- a/cumulus/test/service/Cargo.toml +++ b/cumulus/test/service/Cargo.toml @@ -110,6 +110,7 @@ runtime-benchmarks = [ "cumulus-test-client/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-cli/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", diff --git a/cumulus/test/service/src/bench_utils.rs b/cumulus/test/service/src/bench_utils.rs index 67ffbdd1d21..76717b4136f 100644 --- a/cumulus/test/service/src/bench_utils.rs +++ b/cumulus/test/service/src/bench_utils.rs @@ -68,12 +68,11 @@ pub fn extrinsic_set_time(client: &TestClient) -> OpaqueExtrinsic { let best_number = client.usage_info().chain.best_number; let timestamp = best_number as u64 * cumulus_test_runtime::MinimumPeriod::get(); - cumulus_test_runtime::UncheckedExtrinsic { - signature: None, - function: cumulus_test_runtime::RuntimeCall::Timestamp(pallet_timestamp::Call::set { + cumulus_test_runtime::UncheckedExtrinsic::new_bare( + cumulus_test_runtime::RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: timestamp, }), - } + ) .into() } @@ -101,12 +100,11 @@ pub fn extrinsic_set_validation_data( horizontal_messages: Default::default(), }; - cumulus_test_runtime::UncheckedExtrinsic { - signature: None, - function: cumulus_test_runtime::RuntimeCall::ParachainSystem( + cumulus_test_runtime::UncheckedExtrinsic::new_bare( + cumulus_test_runtime::RuntimeCall::ParachainSystem( cumulus_pallet_parachain_system::Call::set_validation_data { data }, ), - } + ) .into() } diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index a13399d3a40..a3e519a68b9 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -969,7 +969,7 @@ pub fn construct_extrinsic( .map(|c| c / 2) .unwrap_or(2) as u64; let tip = 0; - let extra: runtime::SignedExtra = ( + let tx_ext: runtime::TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckGenesis::::new(), @@ -981,10 +981,11 @@ pub fn construct_extrinsic( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::::new(), - ); + ) + .into(); let raw_payload = runtime::SignedPayload::from_raw( function.clone(), - extra.clone(), + tx_ext.clone(), ((), runtime::VERSION.spec_version, genesis_block, current_block_hash, (), (), (), ()), ); let signature = raw_payload.using_encoded(|e| caller.sign(e)); @@ -992,7 +993,7 @@ pub fn construct_extrinsic( function, caller.public().into(), runtime::Signature::Sr25519(signature), - extra, + tx_ext, ) } diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index adc1c1a8efb..b86ce986820 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -39,6 +39,7 @@ subkey = { workspace = true, default-features = true } frame-system = { workspace = true } frame-support = { workspace = true } frame-executive = { workspace = true } +pallet-example-authorization-tx-extension = { workspace = true, default-features = true } pallet-example-single-block-migrations = { workspace = true, default-features = true } frame-metadata-hash-extension = { workspace = true, default-features = true } log = { workspace = true, default-features = true } diff --git a/docs/sdk/src/guides/enable_pov_reclaim.rs b/docs/sdk/src/guides/enable_pov_reclaim.rs index 13b27d18956..cb6960b3df4 100644 --- a/docs/sdk/src/guides/enable_pov_reclaim.rs +++ b/docs/sdk/src/guides/enable_pov_reclaim.rs @@ -58,9 +58,9 @@ //! > that this step in the guide was not //! > set up correctly. //! -//! ## 3. Add the SignedExtension to your runtime +//! ## 3. Add the TransactionExtension to your runtime //! -//! In your runtime, you will find a list of SignedExtensions. +//! In your runtime, you will find a list of TransactionExtensions. //! To enable the reclaiming, //! add [`StorageWeightReclaim`](cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim) //! to that list. For maximum efficiency, make sure that `StorageWeightReclaim` is last in the list. diff --git a/docs/sdk/src/reference_docs/extrinsic_encoding.rs b/docs/sdk/src/reference_docs/extrinsic_encoding.rs index 31ce92c67e9..1d4b0405b32 100644 --- a/docs/sdk/src/reference_docs/extrinsic_encoding.rs +++ b/docs/sdk/src/reference_docs/extrinsic_encoding.rs @@ -12,7 +12,7 @@ //! //! What follows is a description of how extrinsics based on this //! [`sp_runtime::generic::UncheckedExtrinsic`] type are encoded into bytes. Specifically, we are -//! looking at how extrinsics with a format version of 4 are encoded. This version is itself a part +//! looking at how extrinsics with a format version of 5 are encoded. This version is itself a part //! of the payload, and if it changes, it indicates that something about the encoding may have //! changed. //! @@ -24,7 +24,8 @@ //! ```text //! extrinsic_bytes = concat( //! compact_encoded_length, -//! version_and_maybe_signature, +//! version_and_extrinsic_type, +//! maybe_extension_data, //! call_data //! ) //! ``` @@ -56,18 +57,38 @@ //! version_and_signed, //! from_address, //! signature, -//! signed_extensions_extra, +//! transaction_extensions_extra, //! ) //! ``` //! //! Each of the details to be concatenated together is explained below: //! -//! ### version_and_signed +//! ## version_and_extrinsic_type //! -//! This is one byte, equal to `0x84` or `0b1000_0100` (i.e. an upper 1 bit to denote that it is -//! signed, and then the transaction version, 4, in the lower bits). +//! This byte has 2 components: +//! - the 2 most significant bits represent the extrinsic type: +//! - bare - `0b00` +//! - signed - `0b10` +//! - general - `0b01` +//! - the 6 least significant bits represent the extrinsic format version (currently 5) //! -//! ### from_address +//! ### Bare extrinsics +//! +//! If the extrinsic is _bare_, then `version_and_extrinsic_type` will be just the _transaction +//! protocol version_, which is 5 (or `0b0000_0101`). Bare extrinsics do not carry any other +//! extension data, so `maybe_extension_data` would not be included in the payload and the +//! `version_and_extrinsic_type` would always be followed by the encoded call bytes. +//! +//! ### Signed extrinsics +//! +//! If the extrinsic is _signed_ (all extrinsics submitted from users used to be signed up until +//! version 4), then `version_and_extrinsic_type` is obtained by having a MSB of `1` on the +//! _transaction protocol version_ byte (which translates to `0b1000_0101`). +//! +//! Additionally, _signed_ extrinsics also carry with them address and signature information encoded +//! as follows: +//! +//! #### from_address //! //! This is the [SCALE encoded][frame::deps::codec] address of the sender of the extrinsic. The //! address is the first generic parameter of [`sp_runtime::generic::UncheckedExtrinsic`], and so @@ -78,7 +99,7 @@ //! signed extrinsic to be submitted to a Polkadot node, you'll always use the //! [`sp_runtime::MultiAddress::Id`] variant to wrap your `AccountId32`. //! -//! ### signature +//! #### signature //! //! This is the [SCALE encoded][frame::deps::codec] signature. The signature type is configured via //! the third generic parameter of [`sp_runtime::generic::UncheckedExtrinsic`], which determines the @@ -90,32 +111,41 @@ //! The signature type used on the Polkadot relay chain is [`sp_runtime::MultiSignature`]; the //! variants there are the types of signature that can be provided. //! -//! ### signed_extensions_extra +//! ### General extrinsics +//! +//! If the extrinsic is _general_ (it doesn't carry a signature in the payload, only extension +//! data), then `version_and_extrinsic_type` is obtained by logical OR between the general +//! transaction type bits and the _transaction protocol version_ byte (which translates to +//! `0b0100_0101`). //! -//! This is the concatenation of the [SCALE encoded][frame::deps::codec] bytes representing each of -//! the [_signed extensions_][sp_runtime::traits::SignedExtension], and are configured by the -//! fourth generic parameter of [`sp_runtime::generic::UncheckedExtrinsic`]. Learn more about -//! signed extensions [here][crate::reference_docs::signed_extensions]. +//! ### transaction_extensions_extra //! -//! When it comes to constructing an extrinsic, each signed extension has two things that we are -//! interested in here: +//! This is the concatenation of the [SCALE encoded][frame::deps::codec] bytes representing first a +//! single byte describing the extension version (this is bumped whenever a change occurs in the +//! transaction extension pipeline) followed by the bytes of each of the [_transaction +//! extensions_][sp_runtime::traits::TransactionExtension], and are configured by the fourth generic +//! parameter of [`sp_runtime::generic::UncheckedExtrinsic`]. Learn more about transaction +//! extensions [here][crate::reference_docs::transaction_extensions]. //! -//! - The actual SCALE encoding of the signed extension type itself; this is what will form our -//! `signed_extensions_extra` bytes. -//! - An `AdditionalSigned` type. This is SCALE encoded into the `signed_extensions_additional` data -//! of the _signed payload_ (see below). +//! When it comes to constructing an extrinsic, each transaction extension has two things that we +//! are interested in here: +//! +//! - The actual SCALE encoding of the transaction extension type itself; this is what will form our +//! `transaction_extensions_extra` bytes. +//! - An `Implicit` type. This is SCALE encoded into the `transaction_extensions_implicit` data (see +//! below). //! //! Either (or both) of these can encode to zero bytes. //! -//! Each chain configures the set of signed extensions that it uses in its runtime configuration. -//! At the time of writing, Polkadot configures them +//! Each chain configures the set of transaction extensions that it uses in its runtime +//! configuration. At the time of writing, Polkadot configures them //! [here](https://github.com/polkadot-fellows/runtimes/blob/1dc04eb954eadf8aadb5d83990b89662dbb5a074/relay/polkadot/src/lib.rs#L1432C25-L1432C25). -//! Some of the common signed extensions are defined -//! [here][frame::deps::frame_system#signed-extensions]. +//! Some of the common transaction extensions are defined +//! [here][frame::deps::frame_system#transaction-extensions]. //! -//! Information about exactly which signed extensions are present on a chain and in what order is -//! also a part of the metadata for the chain. For V15 metadata, it can be -//! [found here][frame::deps::frame_support::__private::metadata::v15::ExtrinsicMetadata]. +//! Information about exactly which transaction extensions are present on a chain and in what order +//! is also a part of the metadata for the chain. For V15 metadata, it can be [found +//! here][frame::deps::frame_support::__private::metadata::v15::ExtrinsicMetadata]. //! //! ## call_data //! @@ -150,53 +180,63 @@ //! are typically provided as values to the inner enum. //! //! Information about the pallets that exist for a chain (including their indexes), the calls -//! available in each pallet (including their indexes), and the arguments required for each call -//! can be found in the metadata for the chain. For V15 metadata, this information -//! [is here][frame::deps::frame_support::__private::metadata::v15::PalletMetadata]. +//! available in each pallet (including their indexes), and the arguments required for each call can +//! be found in the metadata for the chain. For V15 metadata, this information [is +//! here][frame::deps::frame_support::__private::metadata::v15::PalletMetadata]. //! //! # The Signed Payload Format //! -//! All extrinsics submitted to a node from the outside world (also known as _transactions_) need to -//! be _signed_. The data that needs to be signed for some extrinsic is called the _signed payload_, -//! and its shape is described by the following pseudo-code: +//! All _signed_ extrinsics submitted to a node from the outside world (also known as +//! _transactions_) need to be _signed_. The data that needs to be signed for some extrinsic is +//! called the _signed payload_, and its shape is described by the following pseudo-code: //! //! ```text -//! signed_payload = concat( -//! call_data, -//! signed_extensions_extra, -//! signed_extensions_additional, +//! signed_payload = blake2_256( +//! concat( +//! call_data, +//! transaction_extensions_extra, +//! transaction_extensions_implicit, +//! ) //! ) -//! -//! if length(signed_payload) > 256 { -//! signed_payload = blake2_256(signed_payload) -//! } //! ``` //! -//! The bytes representing `call_data` and `signed_extensions_extra` can be obtained as described -//! above. `signed_extensions_additional` is constructed by SCALE encoding the -//! ["additional signed" data][sp_runtime::traits::SignedExtension::AdditionalSigned] for each -//! signed extension that the chain is using, in order. +//! The bytes representing `call_data` and `transaction_extensions_extra` can be obtained as +//! descibed above. `transaction_extensions_implicit` is constructed by SCALE encoding the +//! ["implicit" data][sp_runtime::traits::TransactionExtension::Implicit] for each transaction +//! extension that the chain is using, in order. +//! +//! Once we've concatenated those together, we hash the result using a Blake2 256bit hasher. +//! +//! The [`sp_runtime::generic::SignedPayload`] type takes care of assembling the correct payload for +//! us, given `call_data` and a tuple of transaction extensions. //! -//! Once we've concatenated those together, we hash the result if it's greater than 256 bytes in -//! length using a Blake2 256bit hasher. +//! # The General Transaction Format //! -//! The [`sp_runtime::generic::SignedPayload`] type takes care of assembling the correct payload -//! for us, given `call_data` and a tuple of signed extensions. +//! A General transaction does not have a signature method hardcoded in the check logic of the +//! extrinsic, such as a traditionally signed transaction. Instead, general transactions should have +//! one or more extensions in the transaction extension pipeline that auhtorize origins in some way, +//! one of which could be the traditional signature check that happens for all signed transactions +//! in the [Checkable](sp_runtime::traits::Checkable) implementation of +//! [UncheckedExtrinsic](sp_runtime::generic::UncheckedExtrinsic). Therefore, it is up to each +//! extension to define the format of the payload it will try to check and authorize the right +//! origin type. For an example, look into the [authorization example pallet +//! extensions](pallet_example_authorization_tx_extension::extensions) //! //! # Example Encoding //! -//! Using [`sp_runtime::generic::UncheckedExtrinsic`], we can construct and encode an extrinsic -//! as follows: +//! Using [`sp_runtime::generic::UncheckedExtrinsic`], we can construct and encode an extrinsic as +//! follows: #![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", encoding_example)] #[docify::export] pub mod call_data { use codec::{Decode, Encode}; + use sp_runtime::{traits::Dispatchable, DispatchResultWithInfo}; // The outer enum composes calls within // different pallets together. We have two // pallets, "PalletA" and "PalletB". - #[derive(Encode, Decode)] + #[derive(Encode, Decode, Clone)] pub enum Call { #[codec(index = 0)] PalletA(PalletACall), @@ -207,23 +247,33 @@ pub mod call_data { // An inner enum represents the calls within // a specific pallet. "PalletA" has one call, // "Foo". - #[derive(Encode, Decode)] + #[derive(Encode, Decode, Clone)] pub enum PalletACall { #[codec(index = 0)] Foo(String), } - #[derive(Encode, Decode)] + #[derive(Encode, Decode, Clone)] pub enum PalletBCall { #[codec(index = 0)] Bar(String), } + + impl Dispatchable for Call { + type RuntimeOrigin = (); + type Config = (); + type Info = (); + type PostInfo = (); + fn dispatch(self, _origin: Self::RuntimeOrigin) -> DispatchResultWithInfo { + Ok(()) + } + } } #[docify::export] pub mod encoding_example { use super::call_data::{Call, PalletACall}; - use crate::reference_docs::signed_extensions::signed_extensions_example; + use crate::reference_docs::transaction_extensions::transaction_extensions_example; use codec::Encode; use sp_core::crypto::AccountId32; use sp_keyring::sr25519::Keyring; @@ -232,34 +282,40 @@ pub mod encoding_example { MultiAddress, MultiSignature, }; - // Define some signed extensions to use. We'll use a couple of examples - // from the signed extensions reference doc. - type SignedExtensions = - (signed_extensions_example::AddToPayload, signed_extensions_example::AddToSignaturePayload); + // Define some transaction extensions to use. We'll use a couple of examples + // from the transaction extensions reference doc. + type TransactionExtensions = ( + transaction_extensions_example::AddToPayload, + transaction_extensions_example::AddToSignaturePayload, + ); // We'll use `UncheckedExtrinsic` to encode our extrinsic for us. We set // the address and signature type to those used on Polkadot, use our custom - // `Call` type, and use our custom set of `SignedExtensions`. - type Extrinsic = - UncheckedExtrinsic, Call, MultiSignature, SignedExtensions>; + // `Call` type, and use our custom set of `TransactionExtensions`. + type Extrinsic = UncheckedExtrinsic< + MultiAddress, + Call, + MultiSignature, + TransactionExtensions, + >; pub fn encode_demo_extrinsic() -> Vec { // The "from" address will be our Alice dev account. let from_address = MultiAddress::::Id(Keyring::Alice.to_account_id()); - // We provide some values for our expected signed extensions. - let signed_extensions = ( - signed_extensions_example::AddToPayload(1), - signed_extensions_example::AddToSignaturePayload, + // We provide some values for our expected transaction extensions. + let transaction_extensions = ( + transaction_extensions_example::AddToPayload(1), + transaction_extensions_example::AddToSignaturePayload, ); // Construct our call data: let call_data = Call::PalletA(PalletACall::Foo("Hello".to_string())); // The signed payload. This takes care of encoding the call_data, - // signed_extensions_extra and signed_extensions_additional, and hashing + // transaction_extensions_extra and transaction_extensions_implicit, and hashing // the result if it's > 256 bytes: - let signed_payload = SignedPayload::new(&call_data, signed_extensions.clone()); + let signed_payload = SignedPayload::new(call_data.clone(), transaction_extensions.clone()); // Sign the signed payload with our Alice dev account's private key, // and wrap the signature into the expected type: @@ -269,7 +325,7 @@ pub mod encoding_example { }; // Now, we can build and encode our extrinsic: - let ext = Extrinsic::new_signed(call_data, from_address, signature, signed_extensions); + let ext = Extrinsic::new_signed(call_data, from_address, signature, transaction_extensions); let encoded_ext = ext.encode(); encoded_ext diff --git a/docs/sdk/src/reference_docs/frame_runtime_types.rs b/docs/sdk/src/reference_docs/frame_runtime_types.rs index e99106ade87..ec7196cea66 100644 --- a/docs/sdk/src/reference_docs/frame_runtime_types.rs +++ b/docs/sdk/src/reference_docs/frame_runtime_types.rs @@ -134,7 +134,7 @@ //! * [`crate::reference_docs::frame_origin`] explores further details about the usage of //! `RuntimeOrigin`. //! * [`RuntimeCall`] is a particularly interesting composite enum as it dictates the encoding of an -//! extrinsic. See [`crate::reference_docs::signed_extensions`] for more information. +//! extrinsic. See [`crate::reference_docs::transaction_extensions`] for more information. //! * See the documentation of [`construct_runtime`]. //! * See the corresponding lecture in the [pba-book](https://polkadot-blockchain-academy.github.io/pba-book/frame/outer-enum/page.html). //! diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs index 7f2edb08d46..9cf5605a88b 100644 --- a/docs/sdk/src/reference_docs/mod.rs +++ b/docs/sdk/src/reference_docs/mod.rs @@ -40,9 +40,12 @@ pub mod runtime_vs_smart_contract; /// Learn about how extrinsics are encoded to be transmitted to a node and stored in blocks. pub mod extrinsic_encoding; -/// Learn about the signed extensions that form a part of extrinsics. +/// Deprecated in favor of transaction extensions. pub mod signed_extensions; +/// Learn about the transaction extensions that form a part of extrinsics. +pub mod transaction_extensions; + /// Learn about *Origins*, a topic in FRAME that enables complex account abstractions to be built. pub mod frame_origin; diff --git a/docs/sdk/src/reference_docs/signed_extensions.rs b/docs/sdk/src/reference_docs/signed_extensions.rs index c644aeaa416..6e44fea88de 100644 --- a/docs/sdk/src/reference_docs/signed_extensions.rs +++ b/docs/sdk/src/reference_docs/signed_extensions.rs @@ -1,131 +1,2 @@ -//! Signed extensions are, briefly, a means for different chains to extend the "basic" extrinsic -//! format with custom data that can be checked by the runtime. -//! -//! # FRAME provided signed extensions -//! -//! FRAME by default already provides the following signed extensions: -//! -//! - [`CheckGenesis`](frame_system::CheckGenesis): Ensures that a transaction was sent for the same -//! network. Determined based on genesis. -//! -//! - [`CheckMortality`](frame_system::CheckMortality): Extends a transaction with a configurable -//! mortality. -//! -//! - [`CheckNonZeroSender`](frame_system::CheckNonZeroSender): Ensures that the sender of a -//! transaction is not the *all zero account* (all bytes of the accountid are zero). -//! -//! - [`CheckNonce`](frame_system::CheckNonce): Extends a transaction with a nonce to prevent replay -//! of transactions and to provide ordering of transactions. -//! -//! - [`CheckSpecVersion`](frame_system::CheckSpecVersion): Ensures that a transaction was built for -//! the currently active runtime. -//! -//! - [`CheckTxVersion`](frame_system::CheckTxVersion): Ensures that the transaction signer used the -//! correct encoding of the call. -//! -//! - [`CheckWeight`](frame_system::CheckWeight): Ensures that the transaction fits into the block -//! before dispatching it. -//! -//! - [`ChargeTransactionPayment`](pallet_transaction_payment::ChargeTransactionPayment): Charges -//! transaction fees from the signer based on the weight of the call using the native token. -//! -//! - [`ChargeAssetTxPayment`](pallet_asset_tx_payment::ChargeAssetTxPayment): Charges transaction -//! fees from the signer based on the weight of the call using any supported asset (including the -//! native token). -//! -//! - [`ChargeAssetTxPayment`(using -//! conversion)](pallet_asset_conversion_tx_payment::ChargeAssetTxPayment): Charges transaction -//! fees from the signer based on the weight of the call using any supported asset (including the -//! native token). The asset is converted to the native token using a pool. -//! -//! - [`SkipCheckIfFeeless`](pallet_skip_feeless_payment::SkipCheckIfFeeless): Allows transactions -//! to be processed without paying any fee. This requires that the `call` that should be -//! dispatched is augmented with the [`feeless_if`](frame_support::pallet_macros::feeless_if) -//! attribute. -//! -//! - [`CheckMetadataHash`](frame_metadata_hash_extension::CheckMetadataHash): Extends transactions -//! to include the so-called metadata hash. This is required by chains to support the generic -//! Ledger application and other similar offline wallets. -//! -//! - [`StorageWeightReclaim`](cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim): A -//! signed extension for parachains that reclaims unused storage weight after executing a -//! transaction. -//! -//! For more information about these extensions, follow the link to the type documentation. -//! -//! # Building a custom signed extension -//! -//! Defining a couple of very simple signed extensions looks like the following: -#![doc = docify::embed!("./src/reference_docs/signed_extensions.rs", signed_extensions_example)] - -#[docify::export] -pub mod signed_extensions_example { - use codec::{Decode, Encode}; - use scale_info::TypeInfo; - use sp_runtime::traits::SignedExtension; - - // This doesn't actually check anything, but simply allows - // some arbitrary `u32` to be added to the extrinsic payload - #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] - pub struct AddToPayload(pub u32); - - impl SignedExtension for AddToPayload { - const IDENTIFIER: &'static str = "AddToPayload"; - type AccountId = (); - type Call = (); - type AdditionalSigned = (); - type Pre = (); - - fn additional_signed( - &self, - ) -> Result< - Self::AdditionalSigned, - sp_runtime::transaction_validity::TransactionValidityError, - > { - Ok(()) - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } - } - - // This is the opposite; nothing will be added to the extrinsic payload, - // but the AdditionalSigned type (`1234u32`) will be added to the - // payload to be signed. - #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] - pub struct AddToSignaturePayload; - - impl SignedExtension for AddToSignaturePayload { - const IDENTIFIER: &'static str = "AddToSignaturePayload"; - type AccountId = (); - type Call = (); - type AdditionalSigned = u32; - type Pre = (); - - fn additional_signed( - &self, - ) -> Result< - Self::AdditionalSigned, - sp_runtime::transaction_validity::TransactionValidityError, - > { - Ok(1234) - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } - } -} +//! `SignedExtension`s are deprecated in favor of +//! [`TransactionExtension`s](crate::reference_docs::transaction_extensions). diff --git a/docs/sdk/src/reference_docs/transaction_extensions.rs b/docs/sdk/src/reference_docs/transaction_extensions.rs new file mode 100644 index 00000000000..0f8198e8372 --- /dev/null +++ b/docs/sdk/src/reference_docs/transaction_extensions.rs @@ -0,0 +1,103 @@ +//! Transaction extensions are, briefly, a means for different chains to extend the "basic" +//! extrinsic format with custom data that can be checked by the runtime. +//! +//! # FRAME provided transaction extensions +//! +//! FRAME by default already provides the following transaction extensions: +//! +//! - [`CheckGenesis`](frame_system::CheckGenesis): Ensures that a transaction was sent for the same +//! network. Determined based on genesis. +//! +//! - [`CheckMortality`](frame_system::CheckMortality): Extends a transaction with a configurable +//! mortality. +//! +//! - [`CheckNonZeroSender`](frame_system::CheckNonZeroSender): Ensures that the sender of a +//! transaction is not the *all zero account* (all bytes of the accountid are zero). +//! +//! - [`CheckNonce`](frame_system::CheckNonce): Extends a transaction with a nonce to prevent replay +//! of transactions and to provide ordering of transactions. +//! +//! - [`CheckSpecVersion`](frame_system::CheckSpecVersion): Ensures that a transaction was built for +//! the currently active runtime. +//! +//! - [`CheckTxVersion`](frame_system::CheckTxVersion): Ensures that the transaction signer used the +//! correct encoding of the call. +//! +//! - [`CheckWeight`](frame_system::CheckWeight): Ensures that the transaction fits into the block +//! before dispatching it. +//! +//! - [`ChargeTransactionPayment`](pallet_transaction_payment::ChargeTransactionPayment): Charges +//! transaction fees from the signer based on the weight of the call using the native token. +//! +//! - [`ChargeAssetTxPayment`](pallet_asset_tx_payment::ChargeAssetTxPayment): Charges transaction +//! fees from the signer based on the weight of the call using any supported asset (including the +//! native token). +//! +//! - [`ChargeAssetTxPayment`(using +//! conversion)](pallet_asset_conversion_tx_payment::ChargeAssetTxPayment): Charges transaction +//! fees from the signer based on the weight of the call using any supported asset (including the +//! native token). The asset is converted to the native token using a pool. +//! +//! - [`SkipCheckIfFeeless`](pallet_skip_feeless_payment::SkipCheckIfFeeless): Allows transactions +//! to be processed without paying any fee. This requires that the `call` that should be +//! dispatched is augmented with the [`feeless_if`](frame_support::pallet_macros::feeless_if) +//! attribute. +//! +//! - [`CheckMetadataHash`](frame_metadata_hash_extension::CheckMetadataHash): Extends transactions +//! to include the so-called metadata hash. This is required by chains to support the generic +//! Ledger application and other similar offline wallets. +//! +//! - [`StorageWeightReclaim`](cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim): A +//! transaction extension for parachains that reclaims unused storage weight after executing a +//! transaction. +//! +//! For more information about these extensions, follow the link to the type documentation. +//! +//! # Building a custom transaction extension +//! +//! Defining a couple of very simple transaction extensions looks like the following: +#![doc = docify::embed!("./src/reference_docs/transaction_extensions.rs", transaction_extensions_example)] + +#[docify::export] +pub mod transaction_extensions_example { + use codec::{Decode, Encode}; + use scale_info::TypeInfo; + use sp_runtime::{ + impl_tx_ext_default, + traits::{Dispatchable, TransactionExtension}, + transaction_validity::TransactionValidityError, + }; + + // This doesn't actually check anything, but simply allows + // some arbitrary `u32` to be added to the extrinsic payload + #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] + pub struct AddToPayload(pub u32); + + impl TransactionExtension for AddToPayload { + const IDENTIFIER: &'static str = "AddToPayload"; + type Implicit = (); + type Pre = (); + type Val = (); + + impl_tx_ext_default!(Call; weight validate prepare); + } + + // This is the opposite; nothing will be added to the extrinsic payload, + // but the Implicit type (`1234u32`) will be added to the + // payload to be signed. + #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] + pub struct AddToSignaturePayload; + + impl TransactionExtension for AddToSignaturePayload { + const IDENTIFIER: &'static str = "AddToSignaturePayload"; + type Implicit = u32; + + fn implicit(&self) -> Result { + Ok(1234) + } + type Pre = (); + type Val = (); + + impl_tx_ext_default!(Call; weight validate prepare); + } +} diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index 8d50b54b2fd..3edb3f4dadb 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -200,6 +200,7 @@ runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", "polkadot-test-client/runtime-benchmarks", @@ -217,7 +218,10 @@ try-runtime = [ "sp-runtime/try-runtime", "westend-runtime?/try-runtime", ] -fast-runtime = ["rococo-runtime?/fast-runtime", "westend-runtime?/fast-runtime"] +fast-runtime = [ + "rococo-runtime?/fast-runtime", + "westend-runtime?/fast-runtime", +] malus = ["full-node"] runtime-metrics = [ diff --git a/polkadot/node/service/src/benchmarking.rs b/polkadot/node/service/src/benchmarking.rs index 4dcff207841..186bea3960e 100644 --- a/polkadot/node/service/src/benchmarking.rs +++ b/polkadot/node/service/src/benchmarking.rs @@ -189,7 +189,7 @@ fn westend_sign_call( use sp_core::Pair; use westend_runtime as runtime; - let extra: runtime::SignedExtra = ( + let tx_ext: runtime::TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -202,11 +202,12 @@ fn westend_sign_call( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), frame_metadata_hash_extension::CheckMetadataHash::::new(false), - ); + ) + .into(); let payload = runtime::SignedPayload::from_raw( call.clone(), - extra.clone(), + tx_ext.clone(), ( (), runtime::VERSION.spec_version, @@ -225,7 +226,7 @@ fn westend_sign_call( call, sp_runtime::AccountId32::from(acc.public()).into(), polkadot_core_primitives::Signature::Sr25519(signature), - extra, + tx_ext, ) .into() } @@ -243,7 +244,7 @@ fn rococo_sign_call( use rococo_runtime as runtime; use sp_core::Pair; - let extra: runtime::SignedExtra = ( + let tx_ext: runtime::TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -256,11 +257,12 @@ fn rococo_sign_call( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), frame_metadata_hash_extension::CheckMetadataHash::::new(false), - ); + ) + .into(); let payload = runtime::SignedPayload::from_raw( call.clone(), - extra.clone(), + tx_ext.clone(), ( (), runtime::VERSION.spec_version, @@ -279,7 +281,7 @@ fn rococo_sign_call( call, sp_runtime::AccountId32::from(acc.public()).into(), polkadot_core_primitives::Signature::Sr25519(signature), - extra, + tx_ext, ) .into() } diff --git a/polkadot/node/test/service/Cargo.toml b/polkadot/node/test/service/Cargo.toml index 8eb6105f98e..4ef9d88621f 100644 --- a/polkadot/node/test/service/Cargo.toml +++ b/polkadot/node/test/service/Cargo.toml @@ -71,6 +71,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-staking/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", diff --git a/polkadot/node/test/service/src/lib.rs b/polkadot/node/test/service/src/lib.rs index aa7295dddc5..6e09bb9e431 100644 --- a/polkadot/node/test/service/src/lib.rs +++ b/polkadot/node/test/service/src/lib.rs @@ -32,7 +32,7 @@ use polkadot_service::{ Error, FullClient, IsParachainNode, NewFull, OverseerGen, PrometheusConfig, }; use polkadot_test_runtime::{ - ParasCall, ParasSudoWrapperCall, Runtime, SignedExtra, SignedPayload, SudoCall, + ParasCall, ParasSudoWrapperCall, Runtime, SignedPayload, SudoCall, TxExtension, UncheckedExtrinsic, VERSION, }; @@ -414,7 +414,7 @@ pub fn construct_extrinsic( let period = BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -423,10 +423,11 @@ pub fn construct_extrinsic( frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); + ) + .into(); let raw_payload = SignedPayload::from_raw( function.clone(), - extra.clone(), + tx_ext.clone(), ( (), VERSION.spec_version, @@ -443,7 +444,7 @@ pub fn construct_extrinsic( function.clone(), polkadot_test_runtime::Address::Id(caller.public().into()), polkadot_primitives::Signature::Sr25519(signature), - extra.clone(), + tx_ext.clone(), ) } diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index ad082f179b2..01b56b31cf2 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -131,6 +131,7 @@ runtime-benchmarks = [ "pallet-identity/runtime-benchmarks", "pallet-staking/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", diff --git a/polkadot/runtime/common/src/assigned_slots/mod.rs b/polkadot/runtime/common/src/assigned_slots/mod.rs index 96c98c45954..65942c127b1 100644 --- a/polkadot/runtime/common/src/assigned_slots/mod.rs +++ b/polkadot/runtime/common/src/assigned_slots/mod.rs @@ -665,12 +665,21 @@ mod tests { } ); - impl frame_system::offchain::SendTransactionTypes for Test + impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { type Extrinsic = UncheckedExtrinsic; - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; + } + + impl frame_system::offchain::CreateInherent for Test + where + RuntimeCall: From, + { + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + UncheckedExtrinsic::new_bare(call) + } } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] diff --git a/polkadot/runtime/common/src/claims.rs b/polkadot/runtime/common/src/claims.rs index 32686d1a0bf..2b36c19efce 100644 --- a/polkadot/runtime/common/src/claims.rs +++ b/polkadot/runtime/common/src/claims.rs @@ -33,7 +33,11 @@ use scale_info::TypeInfo; use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256}; use sp_runtime::{ - traits::{CheckedSub, DispatchInfoOf, SignedExtension, Zero}, + impl_tx_ext_default, + traits::{ + AsSystemOriginSigner, AsTransactionAuthorizedOrigin, CheckedSub, DispatchInfoOf, + Dispatchable, TransactionExtension, Zero, + }, transaction_validity::{ InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, }, @@ -51,6 +55,7 @@ pub trait WeightInfo { fn claim_attest() -> Weight; fn attest() -> Weight; fn move_claim() -> Weight; + fn prevalidate_attests() -> Weight; } pub struct TestWeightInfo; @@ -70,6 +75,9 @@ impl WeightInfo for TestWeightInfo { fn move_claim() -> Weight { Weight::zero() } + fn prevalidate_attests() -> Weight { + Weight::zero() + } } /// The kind of statement an account needs to make for a claim to be valid. @@ -400,7 +408,7 @@ pub mod pallet { /// Attest to a statement, needed to finalize the claims process. /// /// WARNING: Insecure unless your chain includes `PrevalidateAttests` as a - /// `SignedExtension`. + /// `TransactionExtension`. /// /// Unsigned Validation: /// A call to attest is deemed valid if the sender has a `Preclaim` registered @@ -611,58 +619,56 @@ impl PrevalidateAttests where ::RuntimeCall: IsSubType>, { - /// Create new `SignedExtension` to check runtime version. + /// Create new `TransactionExtension` to check runtime version. pub fn new() -> Self { Self(core::marker::PhantomData) } } -impl SignedExtension for PrevalidateAttests +impl TransactionExtension for PrevalidateAttests where ::RuntimeCall: IsSubType>, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: + AsSystemOriginSigner + AsTransactionAuthorizedOrigin + Clone, { - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = (); - type Pre = (); const IDENTIFIER: &'static str = "PrevalidateAttests"; + type Implicit = (); + type Pre = (); + type Val = (); - fn additional_signed(&self) -> Result { - Ok(()) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + fn weight(&self, call: &T::RuntimeCall) -> Weight { + if let Some(Call::attest { .. }) = call.is_sub_type() { + T::WeightInfo::prevalidate_attests() + } else { + Weight::zero() + } } - // - // The weight of this logic is included in the `attest` dispatchable. - // fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &T::RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { - if let Some(local_call) = call.is_sub_type() { - if let Call::attest { statement: attested_statement } = local_call { - let signer = Preclaims::::get(who) - .ok_or(InvalidTransaction::Custom(ValidityError::SignerHasNoClaim.into()))?; - if let Some(s) = Signing::::get(signer) { - let e = InvalidTransaction::Custom(ValidityError::InvalidStatement.into()); - ensure!(&attested_statement[..] == s.to_text(), e); - } + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + if let Some(Call::attest { statement: attested_statement }) = call.is_sub_type() { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + let signer = Preclaims::::get(who) + .ok_or(InvalidTransaction::Custom(ValidityError::SignerHasNoClaim.into()))?; + if let Some(s) = Signing::::get(signer) { + let e = InvalidTransaction::Custom(ValidityError::InvalidStatement.into()); + ensure!(&attested_statement[..] == s.to_text(), e); } } - Ok(ValidTransaction::default()) + Ok((ValidTransaction::default(), (), origin)) } + + impl_tx_ext_default!(T::RuntimeCall; prepare); } #[cfg(any(test, feature = "runtime-benchmarks"))] @@ -713,8 +719,11 @@ mod tests { }; use pallet_balances; use sp_runtime::{ - traits::Identity, transaction_validity::TransactionLongevity, BuildStorage, - DispatchError::BadOrigin, TokenError, + traits::{DispatchTransaction, Identity}, + transaction_validity::TransactionLongevity, + BuildStorage, + DispatchError::BadOrigin, + TokenError, }; type Block = frame_system::mocking::MockBlock; @@ -1055,8 +1064,8 @@ mod tests { }); let di = c.get_dispatch_info(); assert_eq!(di.pays_fee, Pays::No); - let r = p.validate(&42, &c, &di, 20); - assert_eq!(r, TransactionValidity::Ok(ValidTransaction::default())); + let r = p.validate_only(Some(42).into(), &c, &di, 20); + assert_eq!(r.unwrap().0, ValidTransaction::default()); }); } @@ -1068,13 +1077,13 @@ mod tests { statement: StatementKind::Regular.to_text().to_vec(), }); let di = c.get_dispatch_info(); - let r = p.validate(&42, &c, &di, 20); + let r = p.validate_only(Some(42).into(), &c, &di, 20); assert!(r.is_err()); let c = RuntimeCall::Claims(ClaimsCall::attest { statement: StatementKind::Saft.to_text().to_vec(), }); let di = c.get_dispatch_info(); - let r = p.validate(&69, &c, &di, 20); + let r = p.validate_only(Some(69).into(), &c, &di, 20); assert!(r.is_err()); }); } @@ -1432,10 +1441,16 @@ mod benchmarking { use super::*; use crate::claims::Call; use frame_benchmarking::{account, benchmarks}; - use frame_support::traits::UnfilteredDispatchable; + use frame_support::{ + dispatch::{DispatchInfo, GetDispatchInfo}, + traits::UnfilteredDispatchable, + }; use frame_system::RawOrigin; use secp_utils::*; - use sp_runtime::{traits::ValidateUnsigned, DispatchResult}; + use sp_runtime::{ + traits::{DispatchTransaction, ValidateUnsigned}, + DispatchResult, + }; const SEED: u32 = 0; @@ -1471,6 +1486,12 @@ mod benchmarking { } benchmarks! { + where_clause { where ::RuntimeCall: IsSubType> + From>, + ::RuntimeCall: Dispatchable + GetDispatchInfo, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner + AsTransactionAuthorizedOrigin + Clone, + <::RuntimeCall as Dispatchable>::PostInfo: Default, + } + // Benchmark `claim` including `validate_unsigned` logic. claim { let c = MAX_CLAIMS; @@ -1574,24 +1595,9 @@ mod benchmarking { Preclaims::::insert(&account, eth_address); assert_eq!(Claims::::get(eth_address), Some(VALUE.into())); - let call = super::Call::::attest { statement: StatementKind::Regular.to_text().to_vec() }; - // We have to copy the validate statement here because of trait issues... :( - let validate = |who: &T::AccountId, call: &super::Call| -> DispatchResult { - if let Call::attest{ statement: attested_statement } = call { - let signer = Preclaims::::get(who).ok_or("signer has no claim")?; - if let Some(s) = Signing::::get(signer) { - ensure!(&attested_statement[..] == s.to_text(), "invalid statement"); - } - } - Ok(()) - }; - let call_enc = call.encode(); - }: { - let call = as Decode>::decode(&mut &*call_enc) - .expect("call is encoded above, encoding must be correct"); - validate(&account, &call)?; - call.dispatch_bypass_filter(RawOrigin::Signed(account).into())?; - } + let stmt = StatementKind::Regular.to_text().to_vec(); + }: + _(RawOrigin::Signed(account), stmt) verify { assert_eq!(Claims::::get(eth_address), None); } @@ -1649,6 +1655,42 @@ mod benchmarking { } } + prevalidate_attests { + let c = MAX_CLAIMS; + + for i 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: ::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 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))?; + 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()); + } + impl_benchmark_test_suite!( Pallet, crate::claims::tests::new_test_ext(), diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 7a689a517ea..bfeed04a919 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -98,12 +98,21 @@ frame_support::construct_runtime!( } ); -impl frame_system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { type Extrinsic = UncheckedExtrinsic; - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; +} + +impl frame_system::offchain::CreateInherent for Test +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + UncheckedExtrinsic::new_bare(call) + } } use crate::{auctions::Error as AuctionsError, crowdloan::Error as CrowdloanError}; diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 07f02e92656..2ead621dedf 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -752,12 +752,21 @@ mod tests { } ); - impl frame_system::offchain::SendTransactionTypes for Test + impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { type Extrinsic = UncheckedExtrinsic; - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; + } + + impl frame_system::offchain::CreateInherent for Test + where + RuntimeCall: From, + { + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + UncheckedExtrinsic::new_bare(call) + } } const NORMAL_RATIO: Perbill = Perbill::from_percent(75); diff --git a/polkadot/runtime/parachains/src/disputes/slashing.rs b/polkadot/runtime/parachains/src/disputes/slashing.rs index 4b76fb47e1f..2e09ea667f7 100644 --- a/polkadot/runtime/parachains/src/disputes/slashing.rs +++ b/polkadot/runtime/parachains/src/disputes/slashing.rs @@ -653,7 +653,7 @@ impl Default for SlashingReportHandler { impl HandleReports for SlashingReportHandler where - T: Config + frame_system::offchain::SendTransactionTypes>, + T: Config + frame_system::offchain::CreateInherent>, R: ReportOffence< T::AccountId, T::KeyOwnerIdentification, @@ -685,7 +685,7 @@ where dispute_proof: DisputeProof, key_owner_proof: ::KeyOwnerProof, ) -> Result<(), sp_runtime::TryRuntimeError> { - use frame_system::offchain::SubmitTransaction; + use frame_system::offchain::{CreateInherent, SubmitTransaction}; let session_index = dispute_proof.time_slot.session_index; let validator_index = dispute_proof.validator_index.0; @@ -696,7 +696,8 @@ where key_owner_proof, }; - match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { + let xt = >>::create_inherent(call.into()); + match SubmitTransaction::>::submit_transaction(xt) { Ok(()) => { log::info!( target: LOG_TARGET, diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index 75c9e3a5c9b..80751a2b7a0 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -90,12 +90,21 @@ frame_support::construct_runtime!( } ); -impl frame_system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { type Extrinsic = UncheckedExtrinsic; - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; +} + +impl frame_system::offchain::CreateInherent for Test +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + UncheckedExtrinsic::new_bare(call) + } } parameter_types! { diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 5048656e636..e0f244dbd86 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -615,7 +615,7 @@ pub mod pallet { frame_system::Config + configuration::Config + shared::Config - + frame_system::offchain::SendTransactionTypes> + + frame_system::offchain::CreateInherent> { type RuntimeEvent: From + IsType<::RuntimeEvent>; @@ -2177,9 +2177,8 @@ impl Pallet { ) { use frame_system::offchain::SubmitTransaction; - if let Err(e) = SubmitTransaction::>::submit_unsigned_transaction( - Call::include_pvf_check_statement { stmt, signature }.into(), - ) { + let xt = T::create_inherent(Call::include_pvf_check_statement { stmt, signature }.into()); + if let Err(e) = SubmitTransaction::>::submit_transaction(xt) { log::error!(target: LOG_TARGET, "Error submitting pvf check statement: {:?}", e,); } } diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 7becf6376c3..6bcb0da3d99 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -256,6 +256,7 @@ runtime-benchmarks = [ "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-tips/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", diff --git a/polkadot/runtime/rococo/constants/src/weights/block_weights.rs b/polkadot/runtime/rococo/constants/src/weights/block_weights.rs index e2aa4a6cab7..f7dc2f19316 100644 --- a/polkadot/runtime/rococo/constants/src/weights/block_weights.rs +++ b/polkadot/runtime/rococo/constants/src/weights/block_weights.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26 (Y/M/D) -//! HOSTNAME: `bm5`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29 (Y/M/D) +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! //! SHORT-NAME: `block`, LONG-NAME: `BlockExecution`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` -//! WEIGHT-PATH: `runtime/rococo/constants/src/weights/` +//! WEIGHT-PATH: `./polkadot/runtime/rococo/constants/src/weights/` //! WEIGHT-METRIC: `Average`, WEIGHT-MUL: `1.0`, WEIGHT-ADD: `0` // Executed Command: @@ -28,12 +28,11 @@ // benchmark // overhead // --chain=rococo-dev -// --execution=wasm // --wasm-execution=compiled -// --weight-path=runtime/rococo/constants/src/weights/ +// --weight-path=./polkadot/runtime/rococo/constants/src/weights/ // --warmup=10 // --repeat=100 -// --header=./file_header.txt +// --header=./polkadot/file_header.txt use sp_core::parameter_types; use sp_weights::{constants::WEIGHT_REF_TIME_PER_NANOS, Weight}; @@ -43,17 +42,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 408_659, 450_716 - /// Average: 417_412 - /// Median: 411_177 - /// Std-Dev: 12242.31 + /// Min, Max: 440_142, 476_907 + /// Average: 450_240 + /// Median: 448_633 + /// Std-Dev: 7301.18 /// /// Percentiles nanoseconds: - /// 99th: 445_142 - /// 95th: 442_275 - /// 75th: 414_217 + /// 99th: 470_733 + /// 95th: 465_082 + /// 75th: 452_536 pub const BlockExecutionWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(417_412), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(450_240), 0); } #[cfg(test)] diff --git a/polkadot/runtime/rococo/constants/src/weights/extrinsic_weights.rs b/polkadot/runtime/rococo/constants/src/weights/extrinsic_weights.rs index adce840ebbc..000cee8a237 100644 --- a/polkadot/runtime/rococo/constants/src/weights/extrinsic_weights.rs +++ b/polkadot/runtime/rococo/constants/src/weights/extrinsic_weights.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26 (Y/M/D) -//! HOSTNAME: `bm5`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-02-29 (Y/M/D) +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! //! SHORT-NAME: `extrinsic`, LONG-NAME: `ExtrinsicBase`, RUNTIME: `Development` //! WARMUPS: `10`, REPEAT: `100` -//! WEIGHT-PATH: `runtime/rococo/constants/src/weights/` +//! WEIGHT-PATH: `./polkadot/runtime/rococo/constants/src/weights/` //! WEIGHT-METRIC: `Average`, WEIGHT-MUL: `1.0`, WEIGHT-ADD: `0` // Executed Command: @@ -28,12 +28,11 @@ // benchmark // overhead // --chain=rococo-dev -// --execution=wasm // --wasm-execution=compiled -// --weight-path=runtime/rococo/constants/src/weights/ +// --weight-path=./polkadot/runtime/rococo/constants/src/weights/ // --warmup=10 // --repeat=100 -// --header=./file_header.txt +// --header=./polkadot/file_header.txt use sp_core::parameter_types; use sp_weights::{constants::WEIGHT_REF_TIME_PER_NANOS, Weight}; @@ -43,17 +42,17 @@ parameter_types! { /// Calculated by multiplying the *Average* with `1.0` and adding `0`. /// /// Stats nanoseconds: - /// Min, Max: 97_574, 100_119 - /// Average: 98_236 - /// Median: 98_179 - /// Std-Dev: 394.9 + /// Min, Max: 92_961, 94_143 + /// Average: 93_369 + /// Median: 93_331 + /// Std-Dev: 217.39 /// /// Percentiles nanoseconds: - /// 99th: 99_893 - /// 95th: 98_850 - /// 75th: 98_318 + /// 99th: 93_848 + /// 95th: 93_691 + /// 75th: 93_514 pub const ExtrinsicBaseWeight: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(98_236), 0); + Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(93_369), 0); } #[cfg(test)] diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 1b5f7b5157d..6266febaa0b 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -102,9 +102,8 @@ use sp_core::{ConstU128, ConstU8, Get, OpaqueMetadata, H256}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ - AccountIdConversion, BlakeTwo256, Block as BlockT, ConstU32, ConvertInto, - Extrinsic as ExtrinsicT, IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, - Verify, + AccountIdConversion, BlakeTwo256, Block as BlockT, ConstU32, ConvertInto, IdentityLookup, + Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, RuntimeDebug, @@ -219,6 +218,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type MaxConsumers = frame_support::traits::ConstU32<16>; } @@ -418,6 +418,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -606,18 +607,33 @@ impl pallet_grandpa::Config for Runtime { pallet_grandpa::EquivocationReportSystem; } -/// Submits a transaction with the node's public and signature type. Adheres to the signed extension -/// format of the chain. +impl frame_system::offchain::SigningTypes for Runtime { + type Public = ::Signer; + type Signature = Signature; +} + +impl frame_system::offchain::CreateTransactionBase for Runtime +where + RuntimeCall: From, +{ + type Extrinsic = UncheckedExtrinsic; + type RuntimeCall = RuntimeCall; +} + +/// Submits a transaction with the node's public and signature type. Adheres to the signed +/// extension format of the chain. impl frame_system::offchain::CreateSignedTransaction for Runtime where RuntimeCall: From, { - fn create_transaction>( + fn create_signed_transaction< + C: frame_system::offchain::AppCrypto, + >( call: RuntimeCall, public: ::Signer, account: AccountId, nonce: ::Nonce, - ) -> Option<(RuntimeCall, ::SignaturePayload)> { + ) -> Option { use sp_runtime::traits::StaticLookup; // take the biggest period possible. let period = @@ -629,7 +645,7 @@ where // so the actual block number is `n`. .saturating_sub(1); let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -642,31 +658,39 @@ where frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), frame_metadata_hash_extension::CheckMetadataHash::new(true), - ); - - let raw_payload = SignedPayload::new(call, extra) + ) + .into(); + let raw_payload = SignedPayload::new(call, tx_ext) .map_err(|e| { log::warn!("Unable to create signed payload: {:?}", e); }) .ok()?; let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; - let (call, extra, _) = raw_payload.deconstruct(); + let (call, tx_ext, _) = raw_payload.deconstruct(); let address = ::Lookup::unlookup(account); - Some((call, (address, signature, extra))) + let transaction = UncheckedExtrinsic::new_signed(call, address, signature, tx_ext); + Some(transaction) } } -impl frame_system::offchain::SigningTypes for Runtime { - type Public = ::Signer; - type Signature = Signature; +impl frame_system::offchain::CreateTransaction for Runtime +where + RuntimeCall: From, +{ + type Extension = TxExtension; + + fn create_transaction(call: RuntimeCall, tx_ext: Self::Extension) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_transaction(call, tx_ext) + } } -impl frame_system::offchain::SendTransactionTypes for Runtime +impl frame_system::offchain::CreateInherent for Runtime where - RuntimeCall: From, + RuntimeCall: From, { - type Extrinsic = UncheckedExtrinsic; - type OverarchingCall = RuntimeCall; + fn create_inherent(call: RuntimeCall) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_bare(call) + } } parameter_types! { @@ -1538,8 +1562,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// `BlockId` type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The `SignedExtension` to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -1553,7 +1577,10 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; +/// Unchecked signature payload type as expected by this runtime. +pub type UncheckedSignaturePayload = + generic::UncheckedSignaturePayload; /// All migrations that will run on the next runtime upgrade. /// @@ -1697,7 +1724,7 @@ pub type Executive = frame_executive::Executive< Migrations, >; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; parameter_types! { // The deposit configuration for the singed migration. Specially if you want to allow any signed account to do the migration (see `SignedFilter`, these deposits should be high) @@ -1771,7 +1798,9 @@ mod benches { [pallet_scheduler, Scheduler] [pallet_sudo, Sudo] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_treasury, Treasury] [pallet_utility, Utility] [pallet_vesting, Vesting] @@ -2358,6 +2387,7 @@ sp_api::impl_runtime_apis! { use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use frame_benchmarking::baseline::Pallet as Baseline; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; @@ -2378,6 +2408,7 @@ sp_api::impl_runtime_apis! { use frame_support::traits::WhitelistedStorageKeys; use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use frame_benchmarking::baseline::Pallet as Baseline; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use sp_storage::TrackedStorageKey; diff --git a/polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs b/polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs index dfba0cfc4aa..0f68a5c6fb3 100644 --- a/polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs +++ b/polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `frame_benchmarking::baseline` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=frame_benchmarking::baseline // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/frame_benchmarking_baseline.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/frame_benchmarking_baseline.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -52,8 +55,8 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 157_000 picoseconds. - Weight::from_parts(175_233, 0) + // Minimum execution time: 172_000 picoseconds. + Weight::from_parts(199_481, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `i` is `[0, 1000000]`. @@ -61,8 +64,8 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 149_000 picoseconds. - Weight::from_parts(183_285, 0) + // Minimum execution time: 171_000 picoseconds. + Weight::from_parts(197_821, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `i` is `[0, 1000000]`. @@ -70,8 +73,8 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 158_000 picoseconds. - Weight::from_parts(184_720, 0) + // Minimum execution time: 172_000 picoseconds. + Weight::from_parts(200_942, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `i` is `[0, 1000000]`. @@ -79,16 +82,16 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 152_000 picoseconds. - Weight::from_parts(177_496, 0) + // Minimum execution time: 170_000 picoseconds. + Weight::from_parts(196_906, 0) .saturating_add(Weight::from_parts(0, 0)) } fn hashing() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 19_907_376_000 picoseconds. - Weight::from_parts(19_988_727_000, 0) + // Minimum execution time: 23_346_876_000 picoseconds. + Weight::from_parts(23_363_744_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `i` is `[0, 100]`. @@ -96,10 +99,10 @@ impl frame_benchmarking::baseline::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 198_000 picoseconds. - Weight::from_parts(228_000, 0) + // Minimum execution time: 201_000 picoseconds. + Weight::from_parts(219_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 20_467 - .saturating_add(Weight::from_parts(47_443_635, 0).saturating_mul(i.into())) + // Standard Error: 14_372 + .saturating_add(Weight::from_parts(45_375_800, 0).saturating_mul(i.into())) } } diff --git a/polkadot/runtime/rococo/src/weights/frame_system.rs b/polkadot/runtime/rococo/src/weights/frame_system.rs index 2e49483dcc6..1742a761ca7 100644 --- a/polkadot/runtime/rococo/src/weights/frame_system.rs +++ b/polkadot/runtime/rococo/src/weights/frame_system.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `frame_system` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=frame_system // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -52,91 +55,91 @@ impl frame_system::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_283_000 picoseconds. - Weight::from_parts(2_305_000, 0) + // Minimum execution time: 1_541_000 picoseconds. + Weight::from_parts(2_581_470, 0) .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 0 - .saturating_add(Weight::from_parts(366, 0).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(387, 0).saturating_mul(b.into())) } /// The range of component `b` is `[0, 3932160]`. fn remark_with_event(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_435_000 picoseconds. - Weight::from_parts(7_581_000, 0) + // Minimum execution time: 5_060_000 picoseconds. + Weight::from_parts(5_167_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_408, 0).saturating_mul(b.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_696, 0).saturating_mul(b.into())) } - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a686561707061676573` (r:0 w:1) - /// Proof Skipped: unknown `0x3a686561707061676573` (r:0 w:1) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1) fn set_heap_pages() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 4_010_000 picoseconds. - Weight::from_parts(4_112_000, 0) + // Minimum execution time: 2_649_000 picoseconds. + Weight::from_parts(2_909_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a636f6465` (r:0 w:1) - /// Proof Skipped: unknown `0x3a636f6465` (r:0 w:1) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1) fn set_code() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 80_405_511_000 picoseconds. - Weight::from_parts(83_066_478_000, 0) + // Minimum execution time: 88_417_540_000 picoseconds. + Weight::from_parts(91_809_291_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn set_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_210_000 picoseconds. - Weight::from_parts(2_247_000, 0) + // Minimum execution time: 1_538_000 picoseconds. + Weight::from_parts(1_589_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_058 - .saturating_add(Weight::from_parts(673_943, 0).saturating_mul(i.into())) + // Standard Error: 1_740 + .saturating_add(Weight::from_parts(730_941, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `i` is `[0, 1000]`. fn kill_storage(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_125_000 picoseconds. - Weight::from_parts(2_154_000, 0) + // Minimum execution time: 1_567_000 picoseconds. + Weight::from_parts(1_750_000, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 816 - .saturating_add(Weight::from_parts(491_194, 0).saturating_mul(i.into())) + // Standard Error: 835 + .saturating_add(Weight::from_parts(543_218, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `p` is `[0, 1000]`. fn kill_prefix(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `129 + p * (69 ±0)` - // Estimated: `125 + p * (70 ±0)` - // Minimum execution time: 4_002_000 picoseconds. - Weight::from_parts(4_145_000, 0) - .saturating_add(Weight::from_parts(0, 125)) - // Standard Error: 1_108 - .saturating_add(Weight::from_parts(1_014_971, 0).saturating_mul(p.into())) + // Measured: `80 + p * (69 ±0)` + // Estimated: `83 + p * (70 ±0)` + // Minimum execution time: 3_412_000 picoseconds. + Weight::from_parts(3_448_000, 0) + .saturating_add(Weight::from_parts(0, 83)) + // Standard Error: 1_395 + .saturating_add(Weight::from_parts(1_142_347, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into())) @@ -147,8 +150,8 @@ impl frame_system::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 33_027_000 picoseconds. - Weight::from_parts(33_027_000, 0) + // Minimum execution time: 9_178_000 picoseconds. + Weight::from_parts(9_780_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -162,8 +165,8 @@ impl frame_system::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `22` // Estimated: `1518` - // Minimum execution time: 118_101_992_000 picoseconds. - Weight::from_parts(118_101_992_000, 0) + // Minimum execution time: 94_523_563_000 picoseconds. + Weight::from_parts(96_983_131_000, 0) .saturating_add(Weight::from_parts(0, 1518)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(3)) diff --git a/polkadot/runtime/rococo/src/weights/frame_system_extensions.rs b/polkadot/runtime/rococo/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..99dac1ba75f --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/frame_system_extensions.rs @@ -0,0 +1,134 @@ +// 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 . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! 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: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// benchmark +// pallet +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=frame_system_extensions +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_262_000 picoseconds. + Weight::from_parts(3_497_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_416_000 picoseconds. + Weight::from_parts(5_690_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 5_416_000 picoseconds. + Weight::from_parts(5_690_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 471_000 picoseconds. + Weight::from_parts(552_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3593` + // Minimum execution time: 4_847_000 picoseconds. + Weight::from_parts(5_091_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 388_000 picoseconds. + Weight::from_parts(421_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 378_000 picoseconds. + Weight::from_parts(440_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 3_402_000 picoseconds. + Weight::from_parts(3_627_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/mod.rs b/polkadot/runtime/rococo/src/weights/mod.rs index 020f8e22594..99477baeb28 100644 --- a/polkadot/runtime/rococo/src/weights/mod.rs +++ b/polkadot/runtime/rococo/src/weights/mod.rs @@ -16,6 +16,7 @@ //! A list of the different weight modules for our runtime. pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_asset_rate; pub mod pallet_balances_balances; pub mod pallet_balances_nis_counterpart_balances; @@ -39,6 +40,7 @@ pub mod pallet_scheduler; pub mod pallet_session; pub mod pallet_sudo; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_treasury; pub mod pallet_utility; pub mod pallet_vesting; diff --git a/polkadot/runtime/rococo/src/weights/pallet_asset_rate.rs b/polkadot/runtime/rococo/src/weights/pallet_asset_rate.rs index da2d1958cef..56b1e2cbc57 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_asset_rate.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_asset_rate.rs @@ -16,25 +16,28 @@ //! Autogenerated weights for `pallet_asset_rate` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-03, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cob`, CPU: `` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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/debug/polkadot +// ./target/production/polkadot // benchmark // pallet // --chain=rococo-dev // --steps=50 -// --repeat=2 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_asset_rate // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --output=./runtime/rococo/src/weights/ -// --header=./file_header.txt +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,39 +50,39 @@ use core::marker::PhantomData; /// Weight functions for `pallet_asset_rate`. pub struct WeightInfo(PhantomData); impl pallet_asset_rate::WeightInfo for WeightInfo { - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`) fn create() -> Weight { // Proof Size summary in bytes: - // Measured: `42` - // Estimated: `4702` - // Minimum execution time: 143_000_000 picoseconds. - Weight::from_parts(155_000_000, 0) - .saturating_add(Weight::from_parts(0, 4702)) + // Measured: `142` + // Estimated: `4703` + // Minimum execution time: 10_277_000 picoseconds. + Weight::from_parts(10_487_000, 0) + .saturating_add(Weight::from_parts(0, 4703)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`) fn update() -> Weight { // Proof Size summary in bytes: - // Measured: `110` - // Estimated: `4702` - // Minimum execution time: 156_000_000 picoseconds. - Weight::from_parts(172_000_000, 0) - .saturating_add(Weight::from_parts(0, 4702)) + // Measured: `210` + // Estimated: `4703` + // Minimum execution time: 10_917_000 picoseconds. + Weight::from_parts(11_249_000, 0) + .saturating_add(Weight::from_parts(0, 4703)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:1) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`) fn remove() -> Weight { // Proof Size summary in bytes: - // Measured: `110` - // Estimated: `4702` - // Minimum execution time: 150_000_000 picoseconds. - Weight::from_parts(160_000_000, 0) - .saturating_add(Weight::from_parts(0, 4702)) + // Measured: `210` + // Estimated: `4703` + // Minimum execution time: 11_332_000 picoseconds. + Weight::from_parts(11_866_000, 0) + .saturating_add(Weight::from_parts(0, 4703)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs b/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs index d37bb9369c6..c3c3315edff 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_balances_balances.rs @@ -23,17 +23,19 @@ //! 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_balances // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_balances -// --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ diff --git a/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs b/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs index 706653aeb76..697e51faf53 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_balances_nis_counterpart_balances.rs @@ -23,17 +23,19 @@ //! 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_balances // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_balances -// --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ diff --git a/polkadot/runtime/rococo/src/weights/pallet_bounties.rs b/polkadot/runtime/rococo/src/weights/pallet_bounties.rs index 38d3645316f..8f8be5f2386 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_bounties.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_bounties.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_bounties` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --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 -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,118 +50,181 @@ use core::marker::PhantomData; /// Weight functions for `pallet_bounties`. pub struct WeightInfo(PhantomData); impl pallet_bounties::WeightInfo for WeightInfo { - /// Storage: Bounties BountyCount (r:1 w:1) - /// Proof: Bounties BountyCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(16400), added: 18875, mode: MaxEncodedLen) - /// Storage: Bounties Bounties (r:0 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) + /// Storage: `Bounties::BountyCount` (r:1 w:1) + /// Proof: `Bounties::BountyCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:0 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) /// The range of component `d` is `[0, 16384]`. fn propose_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `210` // Estimated: `3593` - // Minimum execution time: 28_907_000 picoseconds. - Weight::from_parts(31_356_074, 0) + // Minimum execution time: 21_772_000 picoseconds. + Weight::from_parts(22_861_341, 0) .saturating_add(Weight::from_parts(0, 3593)) - // Standard Error: 18 - .saturating_add(Weight::from_parts(606, 0).saturating_mul(d.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(721, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } + /// 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() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `302` + // Estimated: `3642` + // Minimum execution time: 11_218_000 picoseconds. + Weight::from_parts(11_796_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`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `322` + // Estimated: `3642` + // Minimum execution time: 10_959_000 picoseconds. + Weight::from_parts(11_658_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: `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: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `498` + // Estimated: `3642` + // Minimum execution time: 37_419_000 picoseconds. + Weight::from_parts(38_362_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 accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `494` + // Estimated: `3642` + // Minimum execution time: 27_328_000 picoseconds. + Weight::from_parts(27_661_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: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) fn award_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `400` + // Estimated: `3642` + // Minimum execution time: 16_067_000 picoseconds. + Weight::from_parts(16_865_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(2)) + .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: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn claim_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `764` + // Estimated: `8799` + // Minimum execution time: 101_153_000 picoseconds. + Weight::from_parts(102_480_000, 0) + .saturating_add(Weight::from_parts(0, 8799)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Bounties Bounties (r:1 w:1) - /// Proof: Bounties Bounties (max_values: None, max_size: Some(177), added: 2652, mode: MaxEncodedLen) - /// Storage: ChildBounties ParentChildBounties (r:1 w:0) - /// Proof: ChildBounties ParentChildBounties (max_values: None, max_size: Some(16), added: 2491, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyDescriptions (r:0 w:1) - /// Proof: Bounties BountyDescriptions (max_values: None, max_size: Some(16400), added: 18875, mode: MaxEncodedLen) + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn close_bounty_proposed() -> Weight { // Proof Size summary in bytes: - // Measured: `482` + // Measured: `444` // Estimated: `3642` - // Minimum execution time: 46_020_000 picoseconds. - Weight::from_parts(46_711_000, 0) + // Minimum execution time: 38_838_000 picoseconds. + Weight::from_parts(39_549_000, 0) .saturating_add(Weight::from_parts(0, 3642)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:0) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyDescriptions` (r:0 w:1) + /// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn close_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `680` + // Estimated: `6196` + // Minimum execution time: 68_592_000 picoseconds. + Weight::from_parts(70_727_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) } + /// Storage: `Bounties::Bounties` (r:1 w:1) + /// 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: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `358` + // Estimated: `3642` + // Minimum execution time: 11_272_000 picoseconds. + Weight::from_parts(11_592_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 BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:100 w:100) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:200 w:200) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `b` is `[0, 100]`. - fn spend_funds(_b: u32, ) -> Weight { + fn spend_funds(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `1887` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(2_405_233, 0) + // Measured: `0 + b * (297 ±0)` + // Estimated: `1887 + b * (5206 ±0)` + // Minimum execution time: 2_844_000 picoseconds. + Weight::from_parts(2_900_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())) + .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)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 5206).saturating_mul(b.into())) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_child_bounties.rs b/polkadot/runtime/rococo/src/weights/pallet_child_bounties.rs index e8c798d45e7..47ae3a5c90d 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_child_bounties.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_child_bounties.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_child_bounties` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_child_bounties // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,69 +50,153 @@ use core::marker::PhantomData; /// Weight functions for `pallet_child_bounties`. pub struct WeightInfo(PhantomData); impl pallet_child_bounties::WeightInfo for WeightInfo { + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyCount` (r:1 w:1) + /// Proof: `ChildBounties::ChildBountyCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:0 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) /// The range of component `d` is `[0, 16384]`. - fn add_child_bounty(_d: u32, ) -> Weight { + fn add_child_bounty(d: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `540` + // Estimated: `6196` + // Minimum execution time: 57_964_000 picoseconds. + Weight::from_parts(59_559_565, 0) + .saturating_add(Weight::from_parts(0, 6196)) + // Standard Error: 11 + .saturating_add(Weight::from_parts(697, 0).saturating_mul(d.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(6)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) fn propose_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `594` + // Estimated: `3642` + // Minimum execution time: 17_527_000 picoseconds. + Weight::from_parts(18_257_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn accept_curator() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `740` + // Estimated: `3642` + // Minimum execution time: 29_354_000 picoseconds. + Weight::from_parts(30_629_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// 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: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `740` + // Estimated: `3642` + // Minimum execution time: 40_643_000 picoseconds. + Weight::from_parts(42_072_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) fn award_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `637` + // Estimated: `3642` + // Minimum execution time: 18_616_000 picoseconds. + Weight::from_parts(19_316_000, 0) + .saturating_add(Weight::from_parts(0, 3642)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn claim_child_bounty() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `576` + // Estimated: `8799` + // Minimum execution time: 96_376_000 picoseconds. + Weight::from_parts(98_476_000, 0) + .saturating_add(Weight::from_parts(0, 8799)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(6)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn close_child_bounty_added() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `840` + // Estimated: `6196` + // Minimum execution time: 64_640_000 picoseconds. + Weight::from_parts(66_174_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) } + /// Storage: `Bounties::Bounties` (r:1 w:0) + /// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ChildBounties` (`max_values`: None, `max_size`: Some(145), added: 2620, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:3 w:3) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildrenCuratorFees` (r:1 w:1) + /// Proof: `ChildBounties::ChildrenCuratorFees` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ParentChildBounties` (r:1 w:1) + /// Proof: `ChildBounties::ParentChildBounties` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ChildBounties::ChildBountyDescriptions` (r:0 w:1) + /// Proof: `ChildBounties::ChildBountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`) fn close_child_bounty_active() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 0_000 picoseconds. - Weight::from_parts(0, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `1027` + // Estimated: `8799` + // Minimum execution time: 78_159_000 picoseconds. + Weight::from_parts(79_820_000, 0) + .saturating_add(Weight::from_parts(0, 8799)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(7)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_conviction_voting.rs b/polkadot/runtime/rococo/src/weights/pallet_conviction_voting.rs index ba505737f1b..5d92c158df4 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_conviction_voting.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_conviction_voting.rs @@ -16,17 +16,17 @@ //! Autogenerated weights for `pallet_conviction_voting` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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 // benchmark // pallet -// --chain=kusama-dev +// --chain=rococo-dev // --steps=50 // --repeat=20 // --no-storage-info @@ -36,8 +36,8 @@ // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/kusama/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,144 +50,152 @@ use core::marker::PhantomData; /// Weight functions for `pallet_conviction_voting`. pub struct WeightInfo(PhantomData); impl pallet_conviction_voting::WeightInfo for WeightInfo { - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(311), added: 2786, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:1 w:1) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(311), added: 2786, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn vote_new() -> Weight { // Proof Size summary in bytes: - // Measured: `13445` + // Measured: `13407` // Estimated: `42428` - // Minimum execution time: 151_077_000 picoseconds. - Weight::from_parts(165_283_000, 0) + // Minimum execution time: 128_378_000 picoseconds. + Weight::from_parts(131_028_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(311), added: 2786, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(311), added: 2786, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote_existing() -> Weight { // Proof Size summary in bytes: - // Measured: `14166` + // Measured: `14128` // Estimated: `83866` - // Minimum execution time: 232_420_000 picoseconds. - Weight::from_parts(244_439_000, 0) + // Minimum execution time: 155_379_000 picoseconds. + Weight::from_parts(161_597_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(7)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes(7)) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:1) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn remove_vote() -> Weight { // Proof Size summary in bytes: // Measured: `13918` // Estimated: `83866` - // Minimum execution time: 205_017_000 picoseconds. - Weight::from_parts(216_594_000, 0) + // Minimum execution time: 130_885_000 picoseconds. + Weight::from_parts(138_080_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:1 w:0) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn remove_other_vote() -> Weight { // Proof Size summary in bytes: - // Measured: `13004` + // Measured: `13005` // Estimated: `30706` - // Minimum execution time: 84_226_000 picoseconds. - Weight::from_parts(91_255_000, 0) + // Minimum execution time: 71_743_000 picoseconds. + Weight::from_parts(75_170_000, 0) .saturating_add(Weight::from_parts(0, 30706)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: ConvictionVoting VotingFor (r:2 w:2) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:512 w:512) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(311), added: 2786, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:512 w:512) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(311), added: 2786, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:50) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 512]`. fn delegate(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `29640 + r * (365 ±0)` + // Measured: `29602 + r * (365 ±0)` // Estimated: `83866 + r * (3411 ±0)` - // Minimum execution time: 78_708_000 picoseconds. - Weight::from_parts(2_053_488_615, 0) + // Minimum execution time: 58_504_000 picoseconds. + Weight::from_parts(814_301_018, 0) .saturating_add(Weight::from_parts(0, 83866)) - // Standard Error: 179_271 - .saturating_add(Weight::from_parts(47_806_482, 0).saturating_mul(r.into())) + // Standard Error: 59_961 + .saturating_add(Weight::from_parts(20_002_833, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes(45)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 3411).saturating_mul(r.into())) } - /// Storage: ConvictionVoting VotingFor (r:2 w:2) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: Referenda ReferendumInfoFor (r:512 w:512) - /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(936), added: 3411, mode: MaxEncodedLen) - /// Storage: Scheduler Agenda (r:2 w:2) - /// Proof: Scheduler Agenda (max_values: None, max_size: Some(38963), added: 41438, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:512 w:512) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:50) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `r` is `[0, 512]`. fn undelegate(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `29555 + r * (365 ±0)` // Estimated: `83866 + r * (3411 ±0)` - // Minimum execution time: 45_232_000 picoseconds. - Weight::from_parts(2_045_021_014, 0) + // Minimum execution time: 34_970_000 picoseconds. + Weight::from_parts(771_155_804, 0) .saturating_add(Weight::from_parts(0, 83866)) - // Standard Error: 185_130 - .saturating_add(Weight::from_parts(47_896_011, 0).saturating_mul(r.into())) + // Standard Error: 57_795 + .saturating_add(Weight::from_parts(19_781_645, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r.into()))) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(43)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r.into()))) .saturating_add(Weight::from_parts(0, 3411).saturating_mul(r.into())) } - /// Storage: ConvictionVoting VotingFor (r:1 w:1) - /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) - /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) - /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(311), added: 2786, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(27241), added: 29716, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(311), added: 2786, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) fn unlock() -> Weight { // Proof Size summary in bytes: - // Measured: `12218` + // Measured: `12180` // Estimated: `30706` - // Minimum execution time: 116_446_000 picoseconds. - Weight::from_parts(124_043_000, 0) + // Minimum execution time: 89_648_000 picoseconds. + Weight::from_parts(97_144_000, 0) .saturating_add(Weight::from_parts(0, 30706)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_identity.rs b/polkadot/runtime/rococo/src/weights/pallet_identity.rs index b334e21ea03..6df16351f2c 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_identity.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_identity.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_identity` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_identity // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,290 +50,291 @@ use core::marker::PhantomData; /// Weight functions for `pallet_identity`. pub struct WeightInfo(PhantomData); impl pallet_identity::WeightInfo for WeightInfo { - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn add_registrar(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 12_290_000 picoseconds. - Weight::from_parts(12_664_362, 0) + // Minimum execution time: 7_673_000 picoseconds. + Weight::from_parts(8_351_866, 0) .saturating_add(Weight::from_parts(0, 2626)) - // Standard Error: 1_347 - .saturating_add(Weight::from_parts(88_179, 0).saturating_mul(r.into())) + // Standard Error: 1_302 + .saturating_add(Weight::from_parts(79_198, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn set_identity(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `442 + r * (5 ±0)` - // Estimated: `11003` - // Minimum execution time: 31_373_000 picoseconds. - Weight::from_parts(30_435_545, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 2_307 - .saturating_add(Weight::from_parts(92_753, 0).saturating_mul(r.into())) + // Measured: `6978 + r * (5 ±0)` + // Estimated: `11037` + // Minimum execution time: 111_646_000 picoseconds. + Weight::from_parts(113_254_991, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 6_611 + .saturating_add(Weight::from_parts(162_119, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:100 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:100 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn set_subs_new(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `101` - // Estimated: `11003 + s * (2589 ±0)` - // Minimum execution time: 9_251_000 picoseconds. - Weight::from_parts(22_039_210, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 40_779 - .saturating_add(Weight::from_parts(2_898_525, 0).saturating_mul(s.into())) + // Estimated: `11037 + s * (2589 ±0)` + // Minimum execution time: 8_010_000 picoseconds. + Weight::from_parts(19_868_412, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 5_018 + .saturating_add(Weight::from_parts(3_115_007, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(s.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 2589).saturating_mul(s.into())) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 100]`. fn set_subs_old(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `194 + p * (32 ±0)` - // Estimated: `11003` - // Minimum execution time: 9_329_000 picoseconds. - Weight::from_parts(24_055_061, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 3_428 - .saturating_add(Weight::from_parts(1_130_604, 0).saturating_mul(p.into())) + // Estimated: `11037` + // Minimum execution time: 8_111_000 picoseconds. + Weight::from_parts(19_482_392, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 3_156 + .saturating_add(Weight::from_parts(1_305_890, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) } - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - fn clear_identity(_r: u32, s: u32, ) -> Weight { + fn clear_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `469 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 53_365_000 picoseconds. - Weight::from_parts(35_391_422, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 1_353 - .saturating_add(Weight::from_parts(1_074_019, 0).saturating_mul(s.into())) + // Measured: `7070 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037` + // Minimum execution time: 54_107_000 picoseconds. + Weight::from_parts(56_347_715, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 10_944 + .saturating_add(Weight::from_parts(191_321, 0).saturating_mul(r.into())) + // Standard Error: 2_135 + .saturating_add(Weight::from_parts(1_295_872, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) } - /// Storage: Identity Registrars (r:1 w:0) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn request_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `367 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 32_509_000 picoseconds. - Weight::from_parts(31_745_585, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 2_214 - .saturating_add(Weight::from_parts(83_822, 0).saturating_mul(r.into())) - + // Measured: `6968 + r * (57 ±0)` + // Estimated: `11037` + // Minimum execution time: 75_780_000 picoseconds. + Weight::from_parts(76_869_773, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 5_456 + .saturating_add(Weight::from_parts(135_316, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. fn cancel_request(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `398 + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 29_609_000 picoseconds. - Weight::from_parts(28_572_602, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 2_528 - .saturating_add(Weight::from_parts(85_593, 0).saturating_mul(r.into())) + // Measured: `6999` + // Estimated: `11037` + // Minimum execution time: 75_769_000 picoseconds. + Weight::from_parts(76_805_143, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 3_598 + .saturating_add(Weight::from_parts(84_593, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_fee(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_793_000 picoseconds. - Weight::from_parts(8_173_888, 0) + // Minimum execution time: 5_357_000 picoseconds. + Weight::from_parts(5_732_132, 0) .saturating_add(Weight::from_parts(0, 2626)) - // Standard Error: 1_569 - .saturating_add(Weight::from_parts(72_367, 0).saturating_mul(r.into())) + // Standard Error: 927 + .saturating_add(Weight::from_parts(70_832, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_account_id(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_708_000 picoseconds. - Weight::from_parts(8_091_149, 0) + // Minimum execution time: 5_484_000 picoseconds. + Weight::from_parts(5_892_704, 0) .saturating_add(Weight::from_parts(0, 2626)) - // Standard Error: 869 - .saturating_add(Weight::from_parts(87_993, 0).saturating_mul(r.into())) + // Standard Error: 947 + .saturating_add(Weight::from_parts(71_231, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity Registrars (r:1 w:1) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:1) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn set_fields(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `89 + r * (57 ±0)` // Estimated: `2626` - // Minimum execution time: 7_601_000 picoseconds. - Weight::from_parts(8_038_414, 0) + // Minimum execution time: 5_310_000 picoseconds. + Weight::from_parts(5_766_651, 0) .saturating_add(Weight::from_parts(0, 2626)) - // Standard Error: 1_041 - .saturating_add(Weight::from_parts(82_588, 0).saturating_mul(r.into())) + // Standard Error: 916 + .saturating_add(Weight::from_parts(74_776, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity Registrars (r:1 w:0) - /// Proof: Identity Registrars (max_values: Some(1), max_size: Some(1141), added: 1636, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) + /// Storage: `Identity::Registrars` (r:1 w:0) + /// Proof: `Identity::Registrars` (`max_values`: Some(1), `max_size`: Some(1141), added: 1636, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 19]`. fn provide_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `445 + r * (57 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 23_114_000 picoseconds. - Weight::from_parts(22_076_548, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 2_881 - .saturating_add(Weight::from_parts(109_812, 0).saturating_mul(r.into())) + // Measured: `7046 + r * (57 ±0)` + // Estimated: `11037` + // Minimum execution time: 98_200_000 picoseconds. + Weight::from_parts(100_105_482, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 6_152 + .saturating_add(Weight::from_parts(58_906, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: Identity IdentityOf (r:1 w:1) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:0 w:100) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `Identity::IdentityOf` (r:1 w:1) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:0 w:100) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. fn kill_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `676 + r * (5 ±0) + s * (32 ±0) + x * (66 ±0)` - // Estimated: `11003` - // Minimum execution time: 70_007_000 picoseconds. - Weight::from_parts(50_186_495, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 6_533 - .saturating_add(Weight::from_parts(15_486, 0).saturating_mul(r.into())) - // Standard Error: 1_275 - .saturating_add(Weight::from_parts(1_085_117, 0).saturating_mul(s.into())) + // Measured: `7277 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037` + // Minimum execution time: 64_647_000 picoseconds. + Weight::from_parts(68_877_027, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 9_965 + .saturating_add(Weight::from_parts(135_044, 0).saturating_mul(r.into())) + // Standard Error: 1_944 + .saturating_add(Weight::from_parts(1_388_151, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 99]`. fn add_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `475 + s * (36 ±0)` - // Estimated: `11003` - // Minimum execution time: 28_453_000 picoseconds. - Weight::from_parts(33_165_934, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 1_217 - .saturating_add(Weight::from_parts(65_401, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 23_550_000 picoseconds. + Weight::from_parts(29_439_842, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 1_453 + .saturating_add(Weight::from_parts(96_324, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 100]`. fn rename_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `591 + s * (3 ±0)` - // Estimated: `11003` - // Minimum execution time: 12_846_000 picoseconds. - Weight::from_parts(14_710_284, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 496 - .saturating_add(Weight::from_parts(19_539, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 13_704_000 picoseconds. + Weight::from_parts(15_241_441, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 498 + .saturating_add(Weight::from_parts(40_973, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Identity IdentityOf (r:1 w:0) - /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) + /// Storage: `Identity::IdentityOf` (r:1 w:0) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 100]`. fn remove_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `638 + s * (35 ±0)` - // Estimated: `11003` - // Minimum execution time: 32_183_000 picoseconds. - Weight::from_parts(35_296_731, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 854 - .saturating_add(Weight::from_parts(52_028, 0).saturating_mul(s.into())) + // Estimated: `11037` + // Minimum execution time: 29_310_000 picoseconds. + Weight::from_parts(31_712_666, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 967 + .saturating_add(Weight::from_parts(81_250, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Identity SuperOf (r:1 w:1) - /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) - /// Storage: Identity SubsOf (r:1 w:1) - /// Proof: Identity SubsOf (max_values: None, max_size: Some(3258), added: 5733, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Identity::SuperOf` (r:1 w:1) + /// Proof: `Identity::SuperOf` (`max_values`: None, `max_size`: Some(114), added: 2589, mode: `MaxEncodedLen`) + /// Storage: `Identity::SubsOf` (r:1 w:1) + /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 99]`. fn quit_sub(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `704 + s * (37 ±0)` // Estimated: `6723` - // Minimum execution time: 24_941_000 picoseconds. - Weight::from_parts(27_433_059, 0) + // Minimum execution time: 22_906_000 picoseconds. + Weight::from_parts(24_638_729, 0) .saturating_add(Weight::from_parts(0, 6723)) - // Standard Error: 856 - .saturating_add(Weight::from_parts(57_463, 0).saturating_mul(s.into())) + // Standard Error: 645 + .saturating_add(Weight::from_parts(75_121, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -340,90 +344,93 @@ impl pallet_identity::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 13_873_000 picoseconds. - Weight::from_parts(13_873_000, 0) + // Minimum execution time: 6_056_000 picoseconds. + Weight::from_parts(6_349_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `Identity::UsernameAuthorities` (r:0 w:1) + /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn remove_username_authority() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 10_653_000 picoseconds. - Weight::from_parts(10_653_000, 0) - .saturating_add(Weight::from_parts(0, 0)) + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 9_003_000 picoseconds. + Weight::from_parts(9_276_000, 0) + .saturating_add(Weight::from_parts(0, 3517)) + .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Identity::UsernameAuthorities` (r:1 w:1) /// Proof: `Identity::UsernameAuthorities` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Identity::PendingUsernames` (r:1 w:0) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn set_username_for() -> Weight { // Proof Size summary in bytes: // Measured: `80` // Estimated: `11037` - // Minimum execution time: 75_928_000 picoseconds. - Weight::from_parts(75_928_000, 0) + // Minimum execution time: 64_724_000 picoseconds. + Weight::from_parts(66_597_000, 0) .saturating_add(Weight::from_parts(0, 11037)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Identity::PendingUsernames` (r:1 w:1) - /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// Storage: `Identity::AccountOfUsername` (r:0 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) fn accept_username() -> Weight { // Proof Size summary in bytes: - // Measured: `106` + // Measured: `115` // Estimated: `11037` - // Minimum execution time: 38_157_000 picoseconds. - Weight::from_parts(38_157_000, 0) + // Minimum execution time: 19_538_000 picoseconds. + Weight::from_parts(20_204_000, 0) .saturating_add(Weight::from_parts(0, 11037)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Identity::PendingUsernames` (r:1 w:1) - /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(77), added: 2552, mode: `MaxEncodedLen`) + /// Proof: `Identity::PendingUsernames` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn remove_expired_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `106` - // Estimated: `3542` - // Minimum execution time: 46_821_000 picoseconds. - Weight::from_parts(46_821_000, 0) - .saturating_add(Weight::from_parts(0, 3542)) + // Measured: `115` + // Estimated: `3550` + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(19_354_000, 0) + .saturating_add(Weight::from_parts(0, 3550)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Identity::AccountOfUsername` (r:1 w:0) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:1) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn set_primary_username() -> Weight { // Proof Size summary in bytes: - // Measured: `247` + // Measured: `257` // Estimated: `11037` - // Minimum execution time: 22_515_000 picoseconds. - Weight::from_parts(22_515_000, 0) + // Minimum execution time: 15_298_000 picoseconds. + Weight::from_parts(15_760_000, 0) .saturating_add(Weight::from_parts(0, 11037)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Identity::AccountOfUsername` (r:1 w:1) - /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `Identity::AccountOfUsername` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) /// Storage: `Identity::IdentityOf` (r:1 w:0) /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) fn remove_dangling_username() -> Weight { // Proof Size summary in bytes: - // Measured: `126` + // Measured: `98` // Estimated: `11037` - // Minimum execution time: 15_997_000 picoseconds. - Weight::from_parts(15_997_000, 0) + // Minimum execution time: 10_829_000 picoseconds. + Weight::from_parts(11_113_000, 0) .saturating_add(Weight::from_parts(0, 11037)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_indices.rs b/polkadot/runtime/rococo/src/weights/pallet_indices.rs index 99ffd3210ed..434db97d4a7 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_indices.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_indices.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_indices` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_indices // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,66 +50,66 @@ use core::marker::PhantomData; /// Weight functions for `pallet_indices`. pub struct WeightInfo(PhantomData); impl pallet_indices::WeightInfo for WeightInfo { - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn claim() -> Weight { // Proof Size summary in bytes: - // Measured: `142` + // Measured: `4` // Estimated: `3534` - // Minimum execution time: 25_107_000 picoseconds. - Weight::from_parts(25_655_000, 0) + // Minimum execution time: 18_092_000 picoseconds. + Weight::from_parts(18_533_000, 0) .saturating_add(Weight::from_parts(0, 3534)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `341` + // Measured: `203` // Estimated: `3593` - // Minimum execution time: 36_208_000 picoseconds. - Weight::from_parts(36_521_000, 0) + // Minimum execution time: 31_616_000 picoseconds. + Weight::from_parts(32_556_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn free() -> Weight { // Proof Size summary in bytes: - // Measured: `238` + // Measured: `100` // Estimated: `3534` - // Minimum execution time: 25_915_000 picoseconds. - Weight::from_parts(26_220_000, 0) + // Minimum execution time: 19_593_000 picoseconds. + Weight::from_parts(20_100_000, 0) .saturating_add(Weight::from_parts(0, 3534)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `341` + // Measured: `203` // Estimated: `3593` - // Minimum execution time: 28_232_000 picoseconds. - Weight::from_parts(28_845_000, 0) + // Minimum execution time: 21_429_000 picoseconds. + Weight::from_parts(22_146_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Indices Accounts (r:1 w:1) - /// Proof: Indices Accounts (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: `Indices::Accounts` (r:1 w:1) + /// Proof: `Indices::Accounts` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) fn freeze() -> Weight { // Proof Size summary in bytes: - // Measured: `238` + // Measured: `100` // Estimated: `3534` - // Minimum execution time: 27_282_000 picoseconds. - Weight::from_parts(27_754_000, 0) + // Minimum execution time: 20_425_000 picoseconds. + Weight::from_parts(21_023_000, 0) .saturating_add(Weight::from_parts(0, 3534)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_message_queue.rs b/polkadot/runtime/rococo/src/weights/pallet_message_queue.rs index e1e360d374a..6ebfcd060b6 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_message_queue.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_message_queue.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_message_queue` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_message_queue // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,150 +50,149 @@ use core::marker::PhantomData; /// Weight functions for `pallet_message_queue`. pub struct WeightInfo(PhantomData); impl pallet_message_queue::WeightInfo for WeightInfo { - /// Storage: MessageQueue ServiceHead (r:1 w:0) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(6), added: 501, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:0) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(6), added: 501, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) fn ready_ring_knit() -> Weight { // Proof Size summary in bytes: - // Measured: `248` + // Measured: `281` // Estimated: `6050` - // Minimum execution time: 12_106_000 picoseconds. - Weight::from_parts(12_387_000, 0) + // Minimum execution time: 12_830_000 picoseconds. + Weight::from_parts(13_476_000, 0) .saturating_add(Weight::from_parts(0, 6050)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: MessageQueue BookStateFor (r:2 w:2) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(6), added: 501, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:2 w:2) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(6), added: 501, mode: `MaxEncodedLen`) fn ready_ring_unknit() -> Weight { // Proof Size summary in bytes: - // Measured: `248` + // Measured: `281` // Estimated: `6050` - // Minimum execution time: 11_227_000 picoseconds. - Weight::from_parts(11_616_000, 0) + // Minimum execution time: 11_583_000 picoseconds. + Weight::from_parts(11_902_000, 0) .saturating_add(Weight::from_parts(0, 6050)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) fn service_queue_base() -> Weight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3520` - // Minimum execution time: 5_052_000 picoseconds. - Weight::from_parts(5_216_000, 0) + // Minimum execution time: 3_801_000 picoseconds. + Weight::from_parts(3_943_000, 0) .saturating_add(Weight::from_parts(0, 3520)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(32818), added: 35293, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) fn service_page_base_completion() -> Weight { // Proof Size summary in bytes: // Measured: `115` // Estimated: `36283` - // Minimum execution time: 6_522_000 picoseconds. - Weight::from_parts(6_794_000, 0) + // Minimum execution time: 5_517_000 picoseconds. + Weight::from_parts(5_861_000, 0) .saturating_add(Weight::from_parts(0, 36283)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(32818), added: 35293, mode: MaxEncodedLen) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) fn service_page_base_no_completion() -> Weight { // Proof Size summary in bytes: // Measured: `115` // Estimated: `36283` - // Minimum execution time: 6_918_000 picoseconds. - Weight::from_parts(7_083_000, 0) + // Minimum execution time: 5_870_000 picoseconds. + Weight::from_parts(6_028_000, 0) .saturating_add(Weight::from_parts(0, 36283)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `MessageQueue::BookStateFor` (r:0 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:0 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) fn service_page_item() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 28_445_000 picoseconds. - Weight::from_parts(28_659_000, 0) + // Minimum execution time: 80_681_000 picoseconds. + Weight::from_parts(81_818_000, 0) .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(6), added: 501, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) + /// Storage: `MessageQueue::ServiceHead` (r:1 w:1) + /// Proof: `MessageQueue::ServiceHead` (`max_values`: Some(1), `max_size`: Some(6), added: 501, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) fn bump_service_head() -> Weight { // Proof Size summary in bytes: - // Measured: `149` + // Measured: `220` // Estimated: `3520` - // Minimum execution time: 7_224_000 picoseconds. - Weight::from_parts(7_441_000, 0) + // Minimum execution time: 8_641_000 picoseconds. + Weight::from_parts(8_995_000, 0) .saturating_add(Weight::from_parts(0, 3520)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(32818), added: 35293, mode: MaxEncodedLen) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Proof Skipped: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Storage: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// Proof Skipped: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) fn reap_page() -> Weight { // Proof Size summary in bytes: - // Measured: `33232` + // Measured: `32945` // Estimated: `36283` - // Minimum execution time: 45_211_000 picoseconds. - Weight::from_parts(45_505_000, 0) + // Minimum execution time: 38_473_000 picoseconds. + Weight::from_parts(39_831_000, 0) .saturating_add(Weight::from_parts(0, 36283)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(32818), added: 35293, mode: MaxEncodedLen) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Proof Skipped: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Storage: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// Proof Skipped: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) fn execute_overweight_page_removed() -> Weight { // Proof Size summary in bytes: - // Measured: `33232` + // Measured: `32945` // Estimated: `36283` - // Minimum execution time: 52_346_000 picoseconds. - Weight::from_parts(52_745_000, 0) + // Minimum execution time: 48_717_000 picoseconds. + Weight::from_parts(49_724_000, 0) .saturating_add(Weight::from_parts(0, 36283)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:1 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(32818), added: 35293, mode: MaxEncodedLen) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Proof Skipped: unknown `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) - /// Storage: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// Proof Skipped: unknown `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `MessageQueue::Pages` (r:1 w:1) + /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) + /// Storage: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Proof: UNKNOWN KEY `0x3a72656c61795f64697370617463685f71756575655f72656d61696e696e675f` (r:0 w:1) + /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) + /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) fn execute_overweight_page_updated() -> Weight { // Proof Size summary in bytes: - // Measured: `33232` + // Measured: `32945` // Estimated: `36283` - // Minimum execution time: 72_567_000 picoseconds. - Weight::from_parts(73_300_000, 0) + // Minimum execution time: 72_718_000 picoseconds. + Weight::from_parts(74_081_000, 0) .saturating_add(Weight::from_parts(0, 36283)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_multisig.rs b/polkadot/runtime/rococo/src/weights/pallet_multisig.rs index a4f33fe198c..f1b81759ece 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_multisig.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_multisig.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_multisig` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_multisig // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -52,110 +55,110 @@ impl pallet_multisig::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_475_000 picoseconds. - Weight::from_parts(11_904_745, 0) + // Minimum execution time: 12_023_000 picoseconds. + Weight::from_parts(12_643_116, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(492, 0).saturating_mul(z.into())) + // Standard Error: 3 + .saturating_add(Weight::from_parts(582, 0).saturating_mul(z.into())) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_create(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + s * (2 ±0)` + // Measured: `229 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 38_857_000 picoseconds. - Weight::from_parts(33_611_791, 0) + // Minimum execution time: 39_339_000 picoseconds. + Weight::from_parts(27_243_033, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 400 - .saturating_add(Weight::from_parts(59_263, 0).saturating_mul(s.into())) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_211, 0).saturating_mul(z.into())) + // Standard Error: 1_319 + .saturating_add(Weight::from_parts(142_212, 0).saturating_mul(s.into())) + // Standard Error: 12 + .saturating_add(Weight::from_parts(1_592, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[3, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_approve(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `211` + // Measured: `248` // Estimated: `6811` - // Minimum execution time: 25_715_000 picoseconds. - Weight::from_parts(20_607_294, 0) + // Minimum execution time: 27_647_000 picoseconds. + Weight::from_parts(15_828_725, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 285 - .saturating_add(Weight::from_parts(58_225, 0).saturating_mul(s.into())) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_160, 0).saturating_mul(z.into())) + // Standard Error: 908 + .saturating_add(Weight::from_parts(130_880, 0).saturating_mul(s.into())) + // Standard Error: 8 + .saturating_add(Weight::from_parts(1_532, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. /// The range of component `z` is `[0, 10000]`. fn as_multi_complete(s: u32, z: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `317 + s * (33 ±0)` + // Measured: `354 + s * (33 ±0)` // Estimated: `6811` - // Minimum execution time: 43_751_000 picoseconds. - Weight::from_parts(37_398_513, 0) + // Minimum execution time: 46_971_000 picoseconds. + Weight::from_parts(32_150_393, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 426 - .saturating_add(Weight::from_parts(70_904, 0).saturating_mul(s.into())) - // Standard Error: 4 - .saturating_add(Weight::from_parts(1_235, 0).saturating_mul(z.into())) + // Standard Error: 1_129 + .saturating_add(Weight::from_parts(154_796, 0).saturating_mul(s.into())) + // Standard Error: 11 + .saturating_add(Weight::from_parts(1_603, 0).saturating_mul(z.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_create(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `193 + s * (2 ±0)` + // Measured: `229 + s * (2 ±0)` // Estimated: `6811` - // Minimum execution time: 31_278_000 picoseconds. - Weight::from_parts(32_075_573, 0) + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_497_183, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 452 - .saturating_add(Weight::from_parts(62_018, 0).saturating_mul(s.into())) + // Standard Error: 1_615 + .saturating_add(Weight::from_parts(147_071, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn approve_as_multi_approve(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `211` + // Measured: `248` // Estimated: `6811` - // Minimum execution time: 18_178_000 picoseconds. - Weight::from_parts(18_649_867, 0) + // Minimum execution time: 13_897_000 picoseconds. + Weight::from_parts(14_828_339, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 293 - .saturating_add(Weight::from_parts(56_475, 0).saturating_mul(s.into())) + // Standard Error: 1_136 + .saturating_add(Weight::from_parts(133_925, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Multisig Multisigs (r:1 w:1) - /// Proof: Multisig Multisigs (max_values: None, max_size: Some(3346), added: 5821, mode: MaxEncodedLen) + /// Storage: `Multisig::Multisigs` (r:1 w:1) + /// Proof: `Multisig::Multisigs` (`max_values`: None, `max_size`: Some(3346), added: 5821, mode: `MaxEncodedLen`) /// The range of component `s` is `[2, 100]`. fn cancel_as_multi(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `383 + s * (1 ±0)` + // Measured: `420 + s * (1 ±0)` // Estimated: `6811` - // Minimum execution time: 32_265_000 picoseconds. - Weight::from_parts(32_984_014, 0) + // Minimum execution time: 28_984_000 picoseconds. + Weight::from_parts(29_853_232, 0) .saturating_add(Weight::from_parts(0, 6811)) - // Standard Error: 452 - .saturating_add(Weight::from_parts(59_934, 0).saturating_mul(s.into())) + // Standard Error: 650 + .saturating_add(Weight::from_parts(113_440, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_nis.rs b/polkadot/runtime/rococo/src/weights/pallet_nis.rs index 35dad482129..38b41f3a8e2 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_nis.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_nis.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_nis` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_nis // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,202 +50,186 @@ use core::marker::PhantomData; /// Weight functions for `pallet_nis`. pub struct WeightInfo(PhantomData); impl pallet_nis::WeightInfo for WeightInfo { - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6209 + l * (48 ±0)` // Estimated: `51487` - // Minimum execution time: 44_704_000 picoseconds. - Weight::from_parts(44_933_886, 0) + // Minimum execution time: 39_592_000 picoseconds. + Weight::from_parts(38_234_037, 0) .saturating_add(Weight::from_parts(0, 51487)) - // Standard Error: 712 - .saturating_add(Weight::from_parts(71_570, 0).saturating_mul(l.into())) + // Standard Error: 1_237 + .saturating_add(Weight::from_parts(88_816, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) fn place_bid_max() -> Weight { // Proof Size summary in bytes: // Measured: `54211` // Estimated: `51487` - // Minimum execution time: 126_544_000 picoseconds. - Weight::from_parts(128_271_000, 0) + // Minimum execution time: 134_847_000 picoseconds. + Weight::from_parts(139_510_000, 0) .saturating_add(Weight::from_parts(0, 51487)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6209 + l * (48 ±0)` // Estimated: `51487` - // Minimum execution time: 47_640_000 picoseconds. - Weight::from_parts(42_214_261, 0) + // Minimum execution time: 43_330_000 picoseconds. + Weight::from_parts(35_097_881, 0) .saturating_add(Weight::from_parts(0, 51487)) - // Standard Error: 732 - .saturating_add(Weight::from_parts(87_277, 0).saturating_mul(l.into())) + // Standard Error: 1_119 + .saturating_add(Weight::from_parts(73_640, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Nis Summary (r:1 w:0) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nis::Summary` (r:1 w:0) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn fund_deficit() -> Weight { // Proof Size summary in bytes: // Measured: `225` // Estimated: `3593` - // Minimum execution time: 38_031_000 picoseconds. - Weight::from_parts(38_441_000, 0) + // Minimum execution time: 29_989_000 picoseconds. + Weight::from_parts(30_865_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:1) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances Account (r:1 w:1) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) fn communify() -> Weight { // Proof Size summary in bytes: - // Measured: `469` + // Measured: `387` // Estimated: `3593` - // Minimum execution time: 69_269_000 picoseconds. - Weight::from_parts(70_000_000, 0) + // Minimum execution time: 58_114_000 picoseconds. + Weight::from_parts(59_540_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances Account (r:1 w:1) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:1) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) fn privatize() -> Weight { // Proof Size summary in bytes: - // Measured: `659` + // Measured: `543` // Estimated: `3593` - // Minimum execution time: 85_763_000 picoseconds. - Weight::from_parts(86_707_000, 0) + // Minimum execution time: 75_780_000 picoseconds. + Weight::from_parts(77_097_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(7)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Holds (r:1 w:1) - /// Proof: Balances Holds (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) fn thaw_private() -> Weight { // Proof Size summary in bytes: // Measured: `387` // Estimated: `3593` - // Minimum execution time: 47_336_000 picoseconds. - Weight::from_parts(47_623_000, 0) + // Minimum execution time: 46_133_000 picoseconds. + Weight::from_parts(47_250_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Nis Receipts (r:1 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances Account (r:1 w:1) - /// Proof: NisCounterpartBalances Account (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) - /// Storage: NisCounterpartBalances TotalIssuance (r:1 w:1) - /// Proof: NisCounterpartBalances TotalIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:1 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `NisCounterpartBalances::Account` (r:1 w:1) + /// Proof: `NisCounterpartBalances::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn thaw_communal() -> Weight { // Proof Size summary in bytes: - // Measured: `604` + // Measured: `488` // Estimated: `3593` - // Minimum execution time: 90_972_000 picoseconds. - Weight::from_parts(92_074_000, 0) + // Minimum execution time: 77_916_000 picoseconds. + Weight::from_parts(79_427_000, 0) .saturating_add(Weight::from_parts(0, 3593)) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Nis Summary (r:1 w:1) - /// Proof: Nis Summary (max_values: Some(1), max_size: Some(40), added: 535, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:0) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Nis QueueTotals (r:1 w:1) - /// Proof: Nis QueueTotals (max_values: Some(1), max_size: Some(6002), added: 6497, mode: MaxEncodedLen) + /// Storage: `Nis::Summary` (r:1 w:1) + /// Proof: `Nis::Summary` (`max_values`: Some(1), `max_size`: Some(40), added: 535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nis::QueueTotals` (r:1 w:1) + /// Proof: `Nis::QueueTotals` (`max_values`: Some(1), `max_size`: Some(6002), added: 6497, mode: `MaxEncodedLen`) fn process_queues() -> Weight { // Proof Size summary in bytes: // Measured: `6658` // Estimated: `7487` - // Minimum execution time: 21_469_000 picoseconds. - Weight::from_parts(21_983_000, 0) + // Minimum execution time: 22_992_000 picoseconds. + Weight::from_parts(24_112_000, 0) .saturating_add(Weight::from_parts(0, 7487)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Nis Queues (r:1 w:1) - /// Proof: Nis Queues (max_values: None, max_size: Some(48022), added: 50497, mode: MaxEncodedLen) + /// Storage: `Nis::Queues` (r:1 w:1) + /// Proof: `Nis::Queues` (`max_values`: None, `max_size`: Some(48022), added: 50497, mode: `MaxEncodedLen`) fn process_queue() -> Weight { // Proof Size summary in bytes: // Measured: `76` // Estimated: `51487` - // Minimum execution time: 4_912_000 picoseconds. - Weight::from_parts(5_013_000, 0) + // Minimum execution time: 3_856_000 picoseconds. + Weight::from_parts(4_125_000, 0) .saturating_add(Weight::from_parts(0, 51487)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Nis Receipts (r:0 w:1) - /// Proof: Nis Receipts (max_values: None, max_size: Some(81), added: 2556, mode: MaxEncodedLen) + /// Storage: `Nis::Receipts` (r:0 w:1) + /// Proof: `Nis::Receipts` (`max_values`: None, `max_size`: Some(81), added: 2556, mode: `MaxEncodedLen`) fn process_bid() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_048_000 picoseconds. - Weight::from_parts(7_278_000, 0) + // Minimum execution time: 4_344_000 picoseconds. + Weight::from_parts(4_545_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_preimage.rs b/polkadot/runtime/rococo/src/weights/pallet_preimage.rs index e051ebd5bba..7a2b77b84d8 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_preimage.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_preimage.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_preimage` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_preimage // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,184 +50,219 @@ use core::marker::PhantomData; /// Weight functions for `pallet_preimage`. pub struct WeightInfo(PhantomData); impl pallet_preimage::WeightInfo for WeightInfo { - fn ensure_updated(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `193 + n * (91 ±0)` - // Estimated: `3593 + n * (2566 ±0)` - // Minimum execution time: 2_000_000 picoseconds. - Weight::from_parts(2_000_000, 3593) - // Standard Error: 13_720 - .saturating_add(Weight::from_parts(17_309_199, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2566).saturating_mul(n.into())) - } - - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `215` - // Estimated: `3556` - // Minimum execution time: 31_040_000 picoseconds. - Weight::from_parts(31_236_000, 0) - .saturating_add(Weight::from_parts(0, 3556)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_974, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `114` + // Estimated: `3568` + // Minimum execution time: 40_363_000 picoseconds. + Weight::from_parts(41_052_000, 0) + .saturating_add(Weight::from_parts(0, 3568)) + // Standard Error: 6 + .saturating_add(Weight::from_parts(2_298, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_requested_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 18_025_000 picoseconds. - Weight::from_parts(18_264_000, 0) + // Minimum execution time: 14_570_000 picoseconds. + Weight::from_parts(14_890_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_974, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_364, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 4194304]`. fn note_no_deposit_preimage(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 17_122_000 picoseconds. - Weight::from_parts(17_332_000, 0) + // Minimum execution time: 13_933_000 picoseconds. + Weight::from_parts(14_290_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_968, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_349, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unnote_preimage() -> Weight { // Proof Size summary in bytes: - // Measured: `361` - // Estimated: `3556` - // Minimum execution time: 38_218_000 picoseconds. - Weight::from_parts(39_841_000, 0) - .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `315` + // Estimated: `3568` + // Minimum execution time: 54_373_000 picoseconds. + Weight::from_parts(58_205_000, 0) + .saturating_add(Weight::from_parts(0, 3568)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unnote_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `3556` - // Minimum execution time: 23_217_000 picoseconds. - Weight::from_parts(24_246_000, 0) + // Minimum execution time: 24_267_000 picoseconds. + Weight::from_parts(27_063_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `260` // Estimated: `3556` - // Minimum execution time: 21_032_000 picoseconds. - Weight::from_parts(21_844_000, 0) + // Minimum execution time: 25_569_000 picoseconds. + Weight::from_parts(27_895_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_no_deposit_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `3556` - // Minimum execution time: 13_954_000 picoseconds. - Weight::from_parts(14_501_000, 0) + // Minimum execution time: 14_182_000 picoseconds. + Weight::from_parts(16_098_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_unnoted_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `114` // Estimated: `3556` - // Minimum execution time: 14_874_000 picoseconds. - Weight::from_parts(15_380_000, 0) + // Minimum execution time: 14_681_000 picoseconds. + Weight::from_parts(15_549_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn request_requested_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 10_199_000 picoseconds. - Weight::from_parts(10_493_000, 0) + // Minimum execution time: 9_577_000 picoseconds. + Weight::from_parts(10_146_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) - /// Storage: Preimage PreimageFor (r:0 w:1) - /// Proof: Preimage PreimageFor (max_values: None, max_size: Some(4194344), added: 4196819, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:0 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `MaxEncodedLen`) fn unrequest_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `3556` - // Minimum execution time: 21_772_000 picoseconds. - Weight::from_parts(22_554_000, 0) + // Minimum execution time: 21_003_000 picoseconds. + Weight::from_parts(23_549_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn unrequest_unnoted_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 10_115_000 picoseconds. - Weight::from_parts(10_452_000, 0) + // Minimum execution time: 9_507_000 picoseconds. + Weight::from_parts(10_013_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Preimage StatusFor (r:1 w:1) - /// Proof: Preimage StatusFor (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn unrequest_multi_referenced_preimage() -> Weight { // Proof Size summary in bytes: // Measured: `178` // Estimated: `3556` - // Minimum execution time: 10_031_000 picoseconds. - Weight::from_parts(10_310_000, 0) + // Minimum execution time: 9_293_000 picoseconds. + Weight::from_parts(10_055_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Preimage::StatusFor` (r:1023 w:1023) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1023 w:1023) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1023 w:1023) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:0 w:1023) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 1024]`. + fn ensure_updated(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + n * (227 ±0)` + // Estimated: `990 + n * (2603 ±0)` + // Minimum execution time: 48_846_000 picoseconds. + Weight::from_parts(49_378_000, 0) + .saturating_add(Weight::from_parts(0, 990)) + // Standard Error: 38_493 + .saturating_add(Weight::from_parts(47_418_285, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2603).saturating_mul(n.into())) + } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_proxy.rs b/polkadot/runtime/rococo/src/weights/pallet_proxy.rs index d9737a85c05..c9202593095 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_proxy.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_proxy.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_proxy` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_proxy // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,172 +50,176 @@ use core::marker::PhantomData; /// Weight functions for `pallet_proxy`. pub struct WeightInfo(PhantomData); impl pallet_proxy::WeightInfo for WeightInfo { - /// Storage: Proxy Proxies (r:1 w:0) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) /// The range of component `p` is `[1, 31]`. fn proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `227 + p * (37 ±0)` + // Measured: `89 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 15_956_000 picoseconds. - Weight::from_parts(16_300_358, 0) + // Minimum execution time: 11_267_000 picoseconds. + Weight::from_parts(11_798_007, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 652 - .saturating_add(Weight::from_parts(30_807, 0).saturating_mul(p.into())) + // Standard Error: 858 + .saturating_add(Weight::from_parts(43_735, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1)) } - /// Storage: Proxy Proxies (r:1 w:0) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) - /// Storage: Proxy Announcements (r:1 w:1) - /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn proxy_announced(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `554 + a * (68 ±0) + p * (37 ±0)` + // Measured: `416 + a * (68 ±0) + p * (37 ±0)` // Estimated: `5698` - // Minimum execution time: 37_584_000 picoseconds. - Weight::from_parts(37_858_207, 0) + // Minimum execution time: 32_791_000 picoseconds. + Weight::from_parts(32_776_904, 0) .saturating_add(Weight::from_parts(0, 5698)) - // Standard Error: 1_868 - .saturating_add(Weight::from_parts(148_967, 0).saturating_mul(a.into())) - // Standard Error: 1_930 - .saturating_add(Weight::from_parts(13_017, 0).saturating_mul(p.into())) + // Standard Error: 2_382 + .saturating_add(Weight::from_parts(143_857, 0).saturating_mul(a.into())) + // Standard Error: 2_461 + .saturating_add(Weight::from_parts(40_024, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Proxy Announcements (r:1 w:1) - /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. - fn remove_announcement(a: u32, _p: u32, ) -> Weight { + fn remove_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `469 + a * (68 ±0)` + // Measured: `331 + a * (68 ±0)` // Estimated: `5698` - // Minimum execution time: 24_642_000 picoseconds. - Weight::from_parts(25_526_588, 0) + // Minimum execution time: 21_831_000 picoseconds. + Weight::from_parts(22_479_938, 0) .saturating_add(Weight::from_parts(0, 5698)) - // Standard Error: 1_138 - .saturating_add(Weight::from_parts(131_157, 0).saturating_mul(a.into())) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(146_532, 0).saturating_mul(a.into())) + // Standard Error: 1_796 + .saturating_add(Weight::from_parts(7_499, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Proxy Announcements (r:1 w:1) - /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. - fn reject_announcement(a: u32, _p: u32, ) -> Weight { + fn reject_announcement(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `469 + a * (68 ±0)` + // Measured: `331 + a * (68 ±0)` // Estimated: `5698` - // Minimum execution time: 24_377_000 picoseconds. - Weight::from_parts(25_464_033, 0) + // Minimum execution time: 21_776_000 picoseconds. + Weight::from_parts(22_762_843, 0) .saturating_add(Weight::from_parts(0, 5698)) - // Standard Error: 1_116 - .saturating_add(Weight::from_parts(130_722, 0).saturating_mul(a.into())) + // Standard Error: 1_402 + .saturating_add(Weight::from_parts(137_512, 0).saturating_mul(a.into())) + // Standard Error: 1_449 + .saturating_add(Weight::from_parts(3_645, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Proxy Proxies (r:1 w:0) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) - /// Storage: Proxy Announcements (r:1 w:1) - /// Proof: Proxy Announcements (max_values: None, max_size: Some(2233), added: 4708, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `a` is `[0, 31]`. /// The range of component `p` is `[1, 31]`. fn announce(a: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `486 + a * (68 ±0) + p * (37 ±0)` + // Measured: `348 + a * (68 ±0) + p * (37 ±0)` // Estimated: `5698` - // Minimum execution time: 34_202_000 picoseconds. - Weight::from_parts(34_610_079, 0) + // Minimum execution time: 29_108_000 picoseconds. + Weight::from_parts(29_508_910, 0) .saturating_add(Weight::from_parts(0, 5698)) - // Standard Error: 1_234 - .saturating_add(Weight::from_parts(134_197, 0).saturating_mul(a.into())) - // Standard Error: 1_275 - .saturating_add(Weight::from_parts(15_970, 0).saturating_mul(p.into())) + // Standard Error: 2_268 + .saturating_add(Weight::from_parts(144_770, 0).saturating_mul(a.into())) + // Standard Error: 2_343 + .saturating_add(Weight::from_parts(25_851, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) /// The range of component `p` is `[1, 31]`. fn add_proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `227 + p * (37 ±0)` + // Measured: `89 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 25_492_000 picoseconds. - Weight::from_parts(25_984_867, 0) + // Minimum execution time: 18_942_000 picoseconds. + Weight::from_parts(19_518_812, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 893 - .saturating_add(Weight::from_parts(51_868, 0).saturating_mul(p.into())) + // Standard Error: 1_078 + .saturating_add(Weight::from_parts(46_147, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) /// The range of component `p` is `[1, 31]`. fn remove_proxy(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `227 + p * (37 ±0)` + // Measured: `89 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 25_492_000 picoseconds. - Weight::from_parts(26_283_445, 0) + // Minimum execution time: 18_993_000 picoseconds. + Weight::from_parts(19_871_741, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 1_442 - .saturating_add(Weight::from_parts(53_504, 0).saturating_mul(p.into())) + // Standard Error: 1_883 + .saturating_add(Weight::from_parts(46_033, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) /// The range of component `p` is `[1, 31]`. fn remove_proxies(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `227 + p * (37 ±0)` + // Measured: `89 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 22_083_000 picoseconds. - Weight::from_parts(22_688_835, 0) + // Minimum execution time: 17_849_000 picoseconds. + Weight::from_parts(18_776_170, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 994 - .saturating_add(Weight::from_parts(32_994, 0).saturating_mul(p.into())) + // Standard Error: 1_239 + .saturating_add(Weight::from_parts(27_960, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) /// The range of component `p` is `[1, 31]`. fn create_pure(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `239` + // Measured: `101` // Estimated: `4706` - // Minimum execution time: 27_042_000 picoseconds. - Weight::from_parts(27_624_587, 0) + // Minimum execution time: 20_049_000 picoseconds. + Weight::from_parts(20_881_515, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 671 - .saturating_add(Weight::from_parts(5_888, 0).saturating_mul(p.into())) + // Standard Error: 952 + .saturating_add(Weight::from_parts(5_970, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Proxy Proxies (r:1 w:1) - /// Proof: Proxy Proxies (max_values: None, max_size: Some(1241), added: 3716, mode: MaxEncodedLen) + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 30]`. fn kill_pure(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `264 + p * (37 ±0)` + // Measured: `126 + p * (37 ±0)` // Estimated: `4706` - // Minimum execution time: 23_396_000 picoseconds. - Weight::from_parts(24_003_080, 0) + // Minimum execution time: 18_528_000 picoseconds. + Weight::from_parts(19_384_189, 0) .saturating_add(Weight::from_parts(0, 4706)) - // Standard Error: 684 - .saturating_add(Weight::from_parts(29_878, 0).saturating_mul(p.into())) + // Standard Error: 1_106 + .saturating_add(Weight::from_parts(35_698, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_ranked_collective.rs b/polkadot/runtime/rococo/src/weights/pallet_ranked_collective.rs index ce9d5fcc0c7..fa2decb1671 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_ranked_collective.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_ranked_collective.rs @@ -16,24 +16,26 @@ //! Autogenerated weights for `pallet_ranked_collective` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-grjcggob-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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_ranked_collective // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_ranked_collective -// --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ @@ -60,8 +62,8 @@ impl pallet_ranked_collective::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 13_480_000 picoseconds. - Weight::from_parts(13_786_000, 0) + // Minimum execution time: 13_428_000 picoseconds. + Weight::from_parts(14_019_000, 0) .saturating_add(Weight::from_parts(0, 3507)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) @@ -79,11 +81,11 @@ impl pallet_ranked_collective::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `516 + r * (281 ±0)` // Estimated: `3519 + r * (2529 ±0)` - // Minimum execution time: 28_771_000 picoseconds. - Weight::from_parts(29_256_825, 0) + // Minimum execution time: 28_566_000 picoseconds. + Weight::from_parts(29_346_952, 0) .saturating_add(Weight::from_parts(0, 3519)) - // Standard Error: 21_594 - .saturating_add(Weight::from_parts(14_649_527, 0).saturating_mul(r.into())) + // Standard Error: 21_068 + .saturating_add(Weight::from_parts(14_471_237, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) .saturating_add(T::DbWeight::get().writes(6)) @@ -103,11 +105,11 @@ impl pallet_ranked_collective::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `214 + r * (17 ±0)` // Estimated: `3507` - // Minimum execution time: 16_117_000 picoseconds. - Weight::from_parts(16_978_453, 0) + // Minimum execution time: 16_161_000 picoseconds. + Weight::from_parts(16_981_334, 0) .saturating_add(Weight::from_parts(0, 3507)) - // Standard Error: 4_511 - .saturating_add(Weight::from_parts(324_261, 0).saturating_mul(r.into())) + // Standard Error: 4_596 + .saturating_add(Weight::from_parts(313_386, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -124,11 +126,11 @@ impl pallet_ranked_collective::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `532 + r * (72 ±0)` // Estimated: `3519` - // Minimum execution time: 28_995_000 picoseconds. - Weight::from_parts(31_343_215, 0) + // Minimum execution time: 28_406_000 picoseconds. + Weight::from_parts(31_178_557, 0) .saturating_add(Weight::from_parts(0, 3519)) - // Standard Error: 16_438 - .saturating_add(Weight::from_parts(637_462, 0).saturating_mul(r.into())) + // Standard Error: 17_737 + .saturating_add(Weight::from_parts(627_757, 0).saturating_mul(r.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(6)) } @@ -140,15 +142,17 @@ impl pallet_ranked_collective::WeightInfo for WeightInf /// Proof: `FellowshipCollective::Voting` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn vote() -> Weight { // Proof Size summary in bytes: // Measured: `603` // Estimated: `83866` - // Minimum execution time: 38_820_000 picoseconds. - Weight::from_parts(40_240_000, 0) + // Minimum execution time: 41_164_000 picoseconds. + Weight::from_parts(42_163_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:0) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -161,11 +165,11 @@ impl pallet_ranked_collective::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `400 + n * (50 ±0)` // Estimated: `4365 + n * (2540 ±0)` - // Minimum execution time: 12_972_000 picoseconds. - Weight::from_parts(15_829_333, 0) + // Minimum execution time: 13_183_000 picoseconds. + Weight::from_parts(15_604_064, 0) .saturating_add(Weight::from_parts(0, 4365)) - // Standard Error: 1_754 - .saturating_add(Weight::from_parts(1_116_520, 0).saturating_mul(n.into())) + // Standard Error: 2_018 + .saturating_add(Weight::from_parts(1_101_088, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -183,8 +187,8 @@ impl pallet_ranked_collective::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `337` // Estimated: `6048` - // Minimum execution time: 44_601_000 picoseconds. - Weight::from_parts(45_714_000, 0) + // Minimum execution time: 43_603_000 picoseconds. + Weight::from_parts(44_809_000, 0) .saturating_add(Weight::from_parts(0, 6048)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(10)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_recovery.rs b/polkadot/runtime/rococo/src/weights/pallet_recovery.rs new file mode 100644 index 00000000000..ed79aa2b1f1 --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_recovery.rs @@ -0,0 +1,186 @@ +// 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 . + +//! Autogenerated weights for `pallet_recovery` +//! +//! 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: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// benchmark +// pallet +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_recovery +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_recovery`. +pub struct WeightInfo(PhantomData); +impl pallet_recovery::WeightInfo for WeightInfo { + /// Storage: `Recovery::Proxy` (r:1 w:0) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + fn as_recovered() -> Weight { + // Proof Size summary in bytes: + // Measured: `215` + // Estimated: `3545` + // Minimum execution time: 7_899_000 picoseconds. + Weight::from_parts(8_205_000, 0) + .saturating_add(Weight::from_parts(0, 3545)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Recovery::Proxy` (r:0 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + fn set_recovered() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_258_000 picoseconds. + Weight::from_parts(6_494_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Recoverable` (r:1 w:1) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 9]`. + fn create_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3816` + // Minimum execution time: 19_369_000 picoseconds. + Weight::from_parts(20_185_132, 0) + .saturating_add(Weight::from_parts(0, 3816)) + // Standard Error: 4_275 + .saturating_add(Weight::from_parts(78_024, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + fn initiate_recovery() -> Weight { + // Proof Size summary in bytes: + // Measured: `206` + // Estimated: `3854` + // Minimum execution time: 22_425_000 picoseconds. + Weight::from_parts(23_171_000, 0) + .saturating_add(Weight::from_parts(0, 3854)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 9]`. + fn vouch_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `294 + n * (64 ±0)` + // Estimated: `3854` + // Minimum execution time: 17_308_000 picoseconds. + Weight::from_parts(18_118_782, 0) + .saturating_add(Weight::from_parts(0, 3854)) + // Standard Error: 4_309 + .saturating_add(Weight::from_parts(126_278, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Recoverable` (r:1 w:0) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:0) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// Storage: `Recovery::Proxy` (r:1 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 9]`. + fn claim_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `326 + n * (64 ±0)` + // Estimated: `3854` + // Minimum execution time: 20_755_000 picoseconds. + Weight::from_parts(21_821_713, 0) + .saturating_add(Weight::from_parts(0, 3854)) + // Standard Error: 4_550 + .saturating_add(Weight::from_parts(101_916, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:1) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 9]`. + fn close_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `447 + n * (32 ±0)` + // Estimated: `3854` + // Minimum execution time: 29_957_000 picoseconds. + Weight::from_parts(31_010_309, 0) + .saturating_add(Weight::from_parts(0, 3854)) + // Standard Error: 5_913 + .saturating_add(Weight::from_parts(110_070, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Recovery::ActiveRecoveries` (r:1 w:0) + /// Proof: `Recovery::ActiveRecoveries` (`max_values`: None, `max_size`: Some(389), added: 2864, mode: `MaxEncodedLen`) + /// Storage: `Recovery::Recoverable` (r:1 w:1) + /// Proof: `Recovery::Recoverable` (`max_values`: None, `max_size`: Some(351), added: 2826, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 9]`. + fn remove_recovery(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `204 + n * (32 ±0)` + // Estimated: `3854` + // Minimum execution time: 24_430_000 picoseconds. + Weight::from_parts(24_462_856, 0) + .saturating_add(Weight::from_parts(0, 3854)) + // Standard Error: 13_646 + .saturating_add(Weight::from_parts(507_715, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Recovery::Proxy` (r:1 w:1) + /// Proof: `Recovery::Proxy` (`max_values`: None, `max_size`: Some(80), added: 2555, mode: `MaxEncodedLen`) + fn cancel_recovered() -> Weight { + // Proof Size summary in bytes: + // Measured: `215` + // Estimated: `3545` + // Minimum execution time: 9_686_000 picoseconds. + Weight::from_parts(10_071_000, 0) + .saturating_add(Weight::from_parts(0, 3545)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/pallet_referenda_fellowship_referenda.rs b/polkadot/runtime/rococo/src/weights/pallet_referenda_fellowship_referenda.rs index 96f172230e1..6dfcea2b832 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_referenda_fellowship_referenda.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_referenda_fellowship_referenda.rs @@ -16,27 +16,28 @@ //! Autogenerated weights for `pallet_referenda` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-xerhrdyb-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: `Some(Wasm)`, WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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_referenda // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=pallet_referenda -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -59,10 +60,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `327` + // Measured: `292` // Estimated: `42428` - // Minimum execution time: 29_909_000 picoseconds. - Weight::from_parts(30_645_000, 0) + // Minimum execution time: 24_053_000 picoseconds. + Weight::from_parts(25_121_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) @@ -71,15 +72,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `438` + // Measured: `403` // Estimated: `83866` - // Minimum execution time: 54_405_000 picoseconds. - Weight::from_parts(55_583_000, 0) + // Minimum execution time: 45_064_000 picoseconds. + Weight::from_parts(46_112_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -89,15 +92,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `2076` + // Measured: `2041` // Estimated: `42428` - // Minimum execution time: 110_477_000 picoseconds. - Weight::from_parts(119_187_000, 0) + // Minimum execution time: 94_146_000 picoseconds. + Weight::from_parts(98_587_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -107,15 +112,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `2117` + // Measured: `2082` // Estimated: `42428` - // Minimum execution time: 111_467_000 picoseconds. - Weight::from_parts(117_758_000, 0) + // Minimum execution time: 93_002_000 picoseconds. + Weight::from_parts(96_924_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -125,15 +132,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `774` + // Measured: `739` // Estimated: `83866` - // Minimum execution time: 191_135_000 picoseconds. - Weight::from_parts(210_535_000, 0) + // Minimum execution time: 160_918_000 picoseconds. + Weight::from_parts(175_603_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -143,24 +152,26 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipCollective::MemberCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `639` + // Measured: `604` // Estimated: `83866` - // Minimum execution time: 67_168_000 picoseconds. - Weight::from_parts(68_895_000, 0) + // Minimum execution time: 55_253_000 picoseconds. + Weight::from_parts(56_488_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) fn refund_decision_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `351` + // Measured: `317` // Estimated: `4365` - // Minimum execution time: 31_298_000 picoseconds. - Weight::from_parts(32_570_000, 0) + // Minimum execution time: 24_497_000 picoseconds. + Weight::from_parts(25_280_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -169,10 +180,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) fn refund_submission_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `201` + // Measured: `167` // Estimated: `4365` - // Minimum execution time: 15_674_000 picoseconds. - Weight::from_parts(16_190_000, 0) + // Minimum execution time: 11_374_000 picoseconds. + Weight::from_parts(11_817_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -181,15 +192,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `348` // Estimated: `83866` - // Minimum execution time: 38_927_000 picoseconds. - Weight::from_parts(40_545_000, 0) + // Minimum execution time: 31_805_000 picoseconds. + Weight::from_parts(32_622_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) @@ -197,15 +210,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `FellowshipReferenda::MetadataOf` (r:1 w:0) /// Proof: `FellowshipReferenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn kill() -> Weight { // Proof Size summary in bytes: - // Measured: `484` + // Measured: `449` // Estimated: `83866` - // Minimum execution time: 80_209_000 picoseconds. - Weight::from_parts(82_084_000, 0) + // Minimum execution time: 62_364_000 picoseconds. + Weight::from_parts(63_798_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `FellowshipReferenda::TrackQueue` (r:1 w:0) /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) @@ -213,10 +228,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `140` // Estimated: `4277` - // Minimum execution time: 9_520_000 picoseconds. - Weight::from_parts(10_088_000, 0) + // Minimum execution time: 8_811_000 picoseconds. + Weight::from_parts(9_224_000, 0) .saturating_add(Weight::from_parts(0, 4277)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -231,10 +246,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `2376` + // Measured: `2341` // Estimated: `42428` - // Minimum execution time: 93_893_000 picoseconds. - Weight::from_parts(101_065_000, 0) + // Minimum execution time: 83_292_000 picoseconds. + Weight::from_parts(89_114_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -249,10 +264,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `2362` + // Measured: `2327` // Estimated: `42428` - // Minimum execution time: 98_811_000 picoseconds. - Weight::from_parts(103_590_000, 0) + // Minimum execution time: 84_648_000 picoseconds. + Weight::from_parts(89_332_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -263,10 +278,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: - // Measured: `1841` + // Measured: `1807` // Estimated: `4365` - // Minimum execution time: 43_230_000 picoseconds. - Weight::from_parts(46_120_000, 0) + // Minimum execution time: 40_529_000 picoseconds. + Weight::from_parts(45_217_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -277,10 +292,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: - // Measured: `1808` + // Measured: `1774` // Estimated: `4365` - // Minimum execution time: 43_092_000 picoseconds. - Weight::from_parts(46_018_000, 0) + // Minimum execution time: 40_894_000 picoseconds. + Weight::from_parts(45_726_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -293,10 +308,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `1824` + // Measured: `1790` // Estimated: `4365` - // Minimum execution time: 49_697_000 picoseconds. - Weight::from_parts(53_795_000, 0) + // Minimum execution time: 48_187_000 picoseconds. + Weight::from_parts(52_655_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -309,10 +324,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::TrackQueue` (`max_values`: None, `max_size`: Some(812), added: 3287, mode: `MaxEncodedLen`) fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `1865` + // Measured: `1831` // Estimated: `4365` - // Minimum execution time: 50_417_000 picoseconds. - Weight::from_parts(53_214_000, 0) + // Minimum execution time: 47_548_000 picoseconds. + Weight::from_parts(51_547_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -323,10 +338,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `335` + // Measured: `300` // Estimated: `42428` - // Minimum execution time: 25_688_000 picoseconds. - Weight::from_parts(26_575_000, 0) + // Minimum execution time: 20_959_000 picoseconds. + Weight::from_parts(21_837_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -337,10 +352,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `383` + // Measured: `348` // Estimated: `42428` - // Minimum execution time: 26_230_000 picoseconds. - Weight::from_parts(27_235_000, 0) + // Minimum execution time: 21_628_000 picoseconds. + Weight::from_parts(22_192_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -349,10 +364,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) fn nudge_referendum_timed_out() -> Weight { // Proof Size summary in bytes: - // Measured: `242` + // Measured: `208` // Estimated: `4365` - // Minimum execution time: 17_585_000 picoseconds. - Weight::from_parts(18_225_000, 0) + // Minimum execution time: 12_309_000 picoseconds. + Weight::from_parts(12_644_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -367,10 +382,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `584` + // Measured: `549` // Estimated: `42428` - // Minimum execution time: 38_243_000 picoseconds. - Weight::from_parts(39_959_000, 0) + // Minimum execution time: 31_871_000 picoseconds. + Weight::from_parts(33_123_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -385,10 +400,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `719` + // Measured: `684` // Estimated: `42428` - // Minimum execution time: 88_424_000 picoseconds. - Weight::from_parts(92_969_000, 0) + // Minimum execution time: 73_715_000 picoseconds. + Weight::from_parts(79_980_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) @@ -401,10 +416,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `770` + // Measured: `735` // Estimated: `42428` - // Minimum execution time: 138_207_000 picoseconds. - Weight::from_parts(151_726_000, 0) + // Minimum execution time: 128_564_000 picoseconds. + Weight::from_parts(138_536_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -417,10 +432,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `755` + // Measured: `720` // Estimated: `42428` - // Minimum execution time: 131_001_000 picoseconds. - Weight::from_parts(148_651_000, 0) + // Minimum execution time: 129_775_000 picoseconds. + Weight::from_parts(139_001_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -433,10 +448,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `770` + // Measured: `735` // Estimated: `42428` - // Minimum execution time: 109_612_000 picoseconds. - Weight::from_parts(143_626_000, 0) + // Minimum execution time: 128_233_000 picoseconds. + Weight::from_parts(135_796_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -449,10 +464,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `776` + // Measured: `741` // Estimated: `42428` - // Minimum execution time: 71_754_000 picoseconds. - Weight::from_parts(77_329_000, 0) + // Minimum execution time: 66_995_000 picoseconds. + Weight::from_parts(72_678_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -467,10 +482,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `776` + // Measured: `741` // Estimated: `83866` - // Minimum execution time: 153_244_000 picoseconds. - Weight::from_parts(169_961_000, 0) + // Minimum execution time: 137_764_000 picoseconds. + Weight::from_parts(152_260_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -483,10 +498,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: - // Measured: `772` + // Measured: `737` // Estimated: `42428` - // Minimum execution time: 137_997_000 picoseconds. - Weight::from_parts(157_862_000, 0) + // Minimum execution time: 119_992_000 picoseconds. + Weight::from_parts(134_805_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -495,16 +510,18 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(900), added: 3375, mode: `MaxEncodedLen`) /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `FellowshipReferenda::MetadataOf` (r:0 w:1) /// Proof: `FellowshipReferenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_some_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `458` + // Measured: `424` // Estimated: `4365` - // Minimum execution time: 21_794_000 picoseconds. - Weight::from_parts(22_341_000, 0) + // Minimum execution time: 20_927_000 picoseconds. + Weight::from_parts(21_802_000, 0) .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `FellowshipReferenda::ReferendumInfoFor` (r:1 w:0) @@ -513,10 +530,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `FellowshipReferenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `319` + // Measured: `285` // Estimated: `4365` - // Minimum execution time: 18_458_000 picoseconds. - Weight::from_parts(19_097_000, 0) + // Minimum execution time: 14_253_000 picoseconds. + Weight::from_parts(15_031_000, 0) .saturating_add(Weight::from_parts(0, 4365)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_referenda_referenda.rs b/polkadot/runtime/rococo/src/weights/pallet_referenda_referenda.rs index b7cc5df28b9..c35925198f9 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_referenda_referenda.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_referenda_referenda.rs @@ -16,27 +16,28 @@ //! Autogenerated weights for `pallet_referenda` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-xerhrdyb-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: `Some(Wasm)`, WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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_referenda // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=pallet_referenda -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -57,10 +58,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn submit() -> Weight { // Proof Size summary in bytes: - // Measured: `324` + // Measured: `185` // Estimated: `42428` - // Minimum execution time: 39_852_000 picoseconds. - Weight::from_parts(41_610_000, 0) + // Minimum execution time: 28_612_000 picoseconds. + Weight::from_parts(30_060_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(3)) @@ -69,15 +70,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `577` + // Measured: `438` // Estimated: `83866` - // Minimum execution time: 52_588_000 picoseconds. - Weight::from_parts(54_154_000, 0) + // Minimum execution time: 42_827_000 picoseconds. + Weight::from_parts(44_072_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) @@ -87,15 +90,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3334` + // Measured: `3225` // Estimated: `42428` - // Minimum execution time: 70_483_000 picoseconds. - Weight::from_parts(72_731_000, 0) + // Minimum execution time: 56_475_000 picoseconds. + Weight::from_parts(58_888_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) @@ -105,60 +110,62 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3354` + // Measured: `3245` // Estimated: `42428` - // Minimum execution time: 68_099_000 picoseconds. - Weight::from_parts(71_560_000, 0) + // Minimum execution time: 56_542_000 picoseconds. + Weight::from_parts(58_616_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Referenda::DecidingCount` (r:1 w:1) /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `577` + // Measured: `438` // Estimated: `83866` - // Minimum execution time: 64_357_000 picoseconds. - Weight::from_parts(66_081_000, 0) + // Minimum execution time: 51_218_000 picoseconds. + Weight::from_parts(53_148_000, 0) .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Referenda::DecidingCount` (r:1 w:1) /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn place_decision_deposit_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `577` + // Measured: `438` // Estimated: `83866` - // Minimum execution time: 62_709_000 picoseconds. - Weight::from_parts(64_534_000, 0) + // Minimum execution time: 49_097_000 picoseconds. + Weight::from_parts(50_796_000, 0) .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn refund_decision_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `417` + // Measured: `279` // Estimated: `4401` - // Minimum execution time: 31_296_000 picoseconds. - Weight::from_parts(32_221_000, 0) + // Minimum execution time: 23_720_000 picoseconds. + Weight::from_parts(24_327_000, 0) .saturating_add(Weight::from_parts(0, 4401)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -167,10 +174,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn refund_submission_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `407` + // Measured: `269` // Estimated: `4401` - // Minimum execution time: 31_209_000 picoseconds. - Weight::from_parts(32_168_000, 0) + // Minimum execution time: 24_089_000 picoseconds. + Weight::from_parts(24_556_000, 0) .saturating_add(Weight::from_parts(0, 4401)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -179,15 +186,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn cancel() -> Weight { // Proof Size summary in bytes: - // Measured: `485` + // Measured: `346` // Estimated: `83866` - // Minimum execution time: 38_887_000 picoseconds. - Weight::from_parts(40_193_000, 0) + // Minimum execution time: 29_022_000 picoseconds. + Weight::from_parts(29_590_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) @@ -195,15 +204,17 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `Referenda::MetadataOf` (r:1 w:0) /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) fn kill() -> Weight { // Proof Size summary in bytes: - // Measured: `726` + // Measured: `587` // Estimated: `83866` - // Minimum execution time: 106_054_000 picoseconds. - Weight::from_parts(108_318_000, 0) + // Minimum execution time: 81_920_000 picoseconds. + Weight::from_parts(84_492_000, 0) .saturating_add(Weight::from_parts(0, 83866)) .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::TrackQueue` (r:1 w:0) /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) @@ -211,10 +222,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) fn one_fewer_deciding_queue_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `240` + // Measured: `102` // Estimated: `5477` - // Minimum execution time: 9_263_000 picoseconds. - Weight::from_parts(9_763_000, 0) + // Minimum execution time: 8_134_000 picoseconds. + Weight::from_parts(8_574_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -223,36 +234,32 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn one_fewer_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `3254` + // Measured: `3115` // Estimated: `42428` - // Minimum execution time: 50_080_000 picoseconds. - Weight::from_parts(51_858_000, 0) + // Minimum execution time: 39_932_000 picoseconds. + Weight::from_parts(42_086_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Referenda::TrackQueue` (r:1 w:1) /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn one_fewer_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `3254` + // Measured: `3115` // Estimated: `42428` - // Minimum execution time: 53_889_000 picoseconds. - Weight::from_parts(55_959_000, 0) + // Minimum execution time: 42_727_000 picoseconds. + Weight::from_parts(44_280_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) @@ -261,10 +268,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_insertion() -> Weight { // Proof Size summary in bytes: - // Measured: `3077` + // Measured: `2939` // Estimated: `5477` - // Minimum execution time: 23_266_000 picoseconds. - Weight::from_parts(24_624_000, 0) + // Minimum execution time: 20_918_000 picoseconds. + Weight::from_parts(22_180_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -275,10 +282,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_requeued_slide() -> Weight { // Proof Size summary in bytes: - // Measured: `3077` + // Measured: `2939` // Estimated: `5477` - // Minimum execution time: 22_846_000 picoseconds. - Weight::from_parts(24_793_000, 0) + // Minimum execution time: 20_943_000 picoseconds. + Weight::from_parts(21_932_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -291,10 +298,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3081` + // Measured: `2943` // Estimated: `5477` - // Minimum execution time: 28_284_000 picoseconds. - Weight::from_parts(29_940_000, 0) + // Minimum execution time: 25_197_000 picoseconds. + Weight::from_parts(26_083_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -307,10 +314,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) fn nudge_referendum_not_queued() -> Weight { // Proof Size summary in bytes: - // Measured: `3101` + // Measured: `2963` // Estimated: `5477` - // Minimum execution time: 28_133_000 picoseconds. - Weight::from_parts(29_638_000, 0) + // Minimum execution time: 24_969_000 picoseconds. + Weight::from_parts(26_096_000, 0) .saturating_add(Weight::from_parts(0, 5477)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -321,10 +328,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_no_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `437` + // Measured: `298` // Estimated: `42428` - // Minimum execution time: 25_710_000 picoseconds. - Weight::from_parts(26_500_000, 0) + // Minimum execution time: 18_050_000 picoseconds. + Weight::from_parts(18_790_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -335,10 +342,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_preparing() -> Weight { // Proof Size summary in bytes: - // Measured: `485` + // Measured: `346` // Estimated: `42428` - // Minimum execution time: 25_935_000 picoseconds. - Weight::from_parts(26_803_000, 0) + // Minimum execution time: 18_357_000 picoseconds. + Weight::from_parts(18_957_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -347,10 +354,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) fn nudge_referendum_timed_out() -> Weight { // Proof Size summary in bytes: - // Measured: `344` + // Measured: `206` // Estimated: `4401` - // Minimum execution time: 17_390_000 picoseconds. - Weight::from_parts(18_042_000, 0) + // Minimum execution time: 11_479_000 picoseconds. + Weight::from_parts(11_968_000, 0) .saturating_add(Weight::from_parts(0, 4401)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -359,150 +366,136 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Referenda::DecidingCount` (r:1 w:1) /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_failing() -> Weight { // Proof Size summary in bytes: - // Measured: `485` + // Measured: `346` // Estimated: `42428` - // Minimum execution time: 35_141_000 picoseconds. - Weight::from_parts(36_318_000, 0) + // Minimum execution time: 24_471_000 picoseconds. + Weight::from_parts(25_440_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Referenda::DecidingCount` (r:1 w:1) /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_deciding_passing() -> Weight { // Proof Size summary in bytes: - // Measured: `485` + // Measured: `346` // Estimated: `42428` - // Minimum execution time: 37_815_000 picoseconds. - Weight::from_parts(39_243_000, 0) + // Minimum execution time: 26_580_000 picoseconds. + Weight::from_parts(27_570_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_begin_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `538` + // Measured: `399` // Estimated: `42428` - // Minimum execution time: 30_779_000 picoseconds. - Weight::from_parts(31_845_000, 0) + // Minimum execution time: 24_331_000 picoseconds. + Weight::from_parts(25_291_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_end_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `521` + // Measured: `382` // Estimated: `42428` - // Minimum execution time: 31_908_000 picoseconds. - Weight::from_parts(33_253_000, 0) + // Minimum execution time: 24_768_000 picoseconds. + Weight::from_parts(25_746_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_continue_not_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `538` + // Measured: `399` // Estimated: `42428` - // Minimum execution time: 28_951_000 picoseconds. - Weight::from_parts(30_004_000, 0) + // Minimum execution time: 23_171_000 picoseconds. + Weight::from_parts(24_161_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_continue_confirming() -> Weight { // Proof Size summary in bytes: - // Measured: `542` + // Measured: `403` // Estimated: `42428` - // Minimum execution time: 27_750_000 picoseconds. - Weight::from_parts(28_588_000, 0) + // Minimum execution time: 22_263_000 picoseconds. + Weight::from_parts(23_062_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:2 w:2) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Lookup` (r:1 w:1) /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) fn nudge_referendum_approved() -> Weight { // Proof Size summary in bytes: - // Measured: `542` + // Measured: `403` // Estimated: `83866` - // Minimum execution time: 43_950_000 picoseconds. - Weight::from_parts(46_164_000, 0) + // Minimum execution time: 33_710_000 picoseconds. + Weight::from_parts(34_871_000, 0) .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) - /// Storage: `Balances::InactiveIssuance` (r:1 w:0) - /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) fn nudge_referendum_rejected() -> Weight { // Proof Size summary in bytes: - // Measured: `538` + // Measured: `399` // Estimated: `42428` - // Minimum execution time: 31_050_000 picoseconds. - Weight::from_parts(32_169_000, 0) + // Minimum execution time: 24_260_000 picoseconds. + Weight::from_parts(25_104_000, 0) .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// Storage: `Referenda::MetadataOf` (r:0 w:1) /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_some_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `560` + // Measured: `422` // Estimated: `4401` - // Minimum execution time: 21_193_000 picoseconds. - Weight::from_parts(22_116_000, 0) + // Minimum execution time: 19_821_000 picoseconds. + Weight::from_parts(20_641_000, 0) .saturating_add(Weight::from_parts(0, 4401)) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) @@ -511,10 +504,10 @@ impl pallet_referenda::WeightInfo for WeightInfo { /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn clear_metadata() -> Weight { // Proof Size summary in bytes: - // Measured: `421` + // Measured: `283` // Estimated: `4401` - // Minimum execution time: 18_065_000 picoseconds. - Weight::from_parts(18_781_000, 0) + // Minimum execution time: 13_411_000 picoseconds. + Weight::from_parts(14_070_000, 0) .saturating_add(Weight::from_parts(0, 4401)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_scheduler.rs b/polkadot/runtime/rococo/src/weights/pallet_scheduler.rs index 0f36dbd384d..5f6b41d2b54 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_scheduler.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_scheduler.rs @@ -16,24 +16,26 @@ //! Autogenerated weights for `pallet_scheduler` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-01-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-grjcggob-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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_scheduler // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_scheduler -// --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ @@ -54,8 +56,8 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `68` // Estimated: `1489` - // Minimum execution time: 2_869_000 picoseconds. - Weight::from_parts(3_109_000, 0) + // Minimum execution time: 3_114_000 picoseconds. + Weight::from_parts(3_245_000, 0) .saturating_add(Weight::from_parts(0, 1489)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -67,11 +69,11 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `115 + s * (177 ±0)` // Estimated: `42428` - // Minimum execution time: 3_326_000 picoseconds. - Weight::from_parts(5_818_563, 0) + // Minimum execution time: 3_430_000 picoseconds. + Weight::from_parts(6_250_920, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 1_261 - .saturating_add(Weight::from_parts(336_446, 0).saturating_mul(s.into())) + // Standard Error: 1_350 + .saturating_add(Weight::from_parts(333_245, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -79,8 +81,8 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_007_000 picoseconds. - Weight::from_parts(3_197_000, 0) + // Minimum execution time: 3_166_000 picoseconds. + Weight::from_parts(3_295_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `Preimage::PreimageFor` (r:1 w:1) @@ -94,11 +96,11 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `251 + s * (1 ±0)` // Estimated: `3716 + s * (1 ±0)` - // Minimum execution time: 16_590_000 picoseconds. - Weight::from_parts(16_869_000, 0) + // Minimum execution time: 17_072_000 picoseconds. + Weight::from_parts(17_393_000, 0) .saturating_add(Weight::from_parts(0, 3716)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(1_308, 0).saturating_mul(s.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(1_204, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(s.into())) @@ -109,8 +111,8 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_320_000 picoseconds. - Weight::from_parts(4_594_000, 0) + // Minimum execution time: 4_566_000 picoseconds. + Weight::from_parts(4_775_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -118,24 +120,24 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_956_000 picoseconds. - Weight::from_parts(3_216_000, 0) + // Minimum execution time: 3_180_000 picoseconds. + Weight::from_parts(3_339_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn execute_dispatch_signed() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_824_000 picoseconds. - Weight::from_parts(1_929_000, 0) + // Minimum execution time: 1_656_000 picoseconds. + Weight::from_parts(1_829_000, 0) .saturating_add(Weight::from_parts(0, 0)) } fn execute_dispatch_unsigned() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_749_000 picoseconds. - Weight::from_parts(1_916_000, 0) + // Minimum execution time: 1_628_000 picoseconds. + Weight::from_parts(1_840_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `Scheduler::Agenda` (r:1 w:1) @@ -145,16 +147,18 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `115 + s * (177 ±0)` // Estimated: `42428` - // Minimum execution time: 9_086_000 picoseconds. - Weight::from_parts(11_733_696, 0) + // Minimum execution time: 9_523_000 picoseconds. + Weight::from_parts(12_482_434, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 1_362 - .saturating_add(Weight::from_parts(375_266, 0).saturating_mul(s.into())) + // Standard Error: 1_663 + .saturating_add(Weight::from_parts(370_122, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Lookup` (r:0 w:1) /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 50]`. @@ -162,13 +166,13 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `115 + s * (177 ±0)` // Estimated: `42428` - // Minimum execution time: 12_716_000 picoseconds. - Weight::from_parts(12_529_180, 0) + // Minimum execution time: 14_649_000 picoseconds. + Weight::from_parts(14_705_132, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 867 - .saturating_add(Weight::from_parts(548_188, 0).saturating_mul(s.into())) + // Standard Error: 1_126 + .saturating_add(Weight::from_parts(547_438, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `Scheduler::Lookup` (r:1 w:1) /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) @@ -179,11 +183,11 @@ impl pallet_scheduler::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `292 + s * (185 ±0)` // Estimated: `42428` - // Minimum execution time: 12_053_000 picoseconds. - Weight::from_parts(15_358_056, 0) + // Minimum execution time: 12_335_000 picoseconds. + Weight::from_parts(16_144_217, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 3_176 - .saturating_add(Weight::from_parts(421_589, 0).saturating_mul(s.into())) + // Standard Error: 3_533 + .saturating_add(Weight::from_parts(413_823, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -191,49 +195,48 @@ impl pallet_scheduler::WeightInfo for WeightInfo { /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 50]`. fn cancel_named(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `318 + s * (185 ±0)` // Estimated: `42428` - // Minimum execution time: 14_803_000 picoseconds. - Weight::from_parts(15_805_714, 0) + // Minimum execution time: 16_906_000 picoseconds. + Weight::from_parts(17_846_662, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 2_597 - .saturating_add(Weight::from_parts(611_053, 0).saturating_mul(s.into())) + // Standard Error: 2_687 + .saturating_add(Weight::from_parts(613_356, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: `Scheduler::Retries` (r:1 w:2) - /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Agenda` (r:1 w:1) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) - /// Storage: `Scheduler::Lookup` (r:0 w:1) - /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Retries` (r:0 w:1) + /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// The range of component `s` is `[1, 50]`. fn schedule_retry(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `196` + // Measured: `155` // Estimated: `42428` - // Minimum execution time: 13_156_000 picoseconds. - Weight::from_parts(13_801_287, 0) + // Minimum execution time: 8_988_000 picoseconds. + Weight::from_parts(9_527_838, 0) .saturating_add(Weight::from_parts(0, 42428)) - // Standard Error: 568 - .saturating_add(Weight::from_parts(35_441, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(4)) + // Standard Error: 523 + .saturating_add(Weight::from_parts(25_453, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Scheduler::Agenda` (r:1 w:0) /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Retries` (r:0 w:1) /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// The range of component `s` is `[1, 50]`. fn set_retry() -> Weight { // Proof Size summary in bytes: - // Measured: `115 + s * (177 ±0)` + // Measured: `8965` // Estimated: `42428` - // Minimum execution time: 7_912_000 picoseconds. - Weight::from_parts(8_081_460, 0) + // Minimum execution time: 23_337_000 picoseconds. + Weight::from_parts(24_255_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -244,13 +247,12 @@ impl pallet_scheduler::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Retries` (r:0 w:1) /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// The range of component `s` is `[1, 50]`. fn set_retry_named() -> Weight { // Proof Size summary in bytes: - // Measured: `324 + s * (185 ±0)` + // Measured: `9643` // Estimated: `42428` - // Minimum execution time: 10_673_000 picoseconds. - Weight::from_parts(12_212_185, 0) + // Minimum execution time: 30_704_000 picoseconds. + Weight::from_parts(31_646_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -259,13 +261,12 @@ impl pallet_scheduler::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Retries` (r:0 w:1) /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// The range of component `s` is `[1, 50]`. fn cancel_retry() -> Weight { // Proof Size summary in bytes: - // Measured: `115 + s * (177 ±0)` + // Measured: `8977` // Estimated: `42428` - // Minimum execution time: 7_912_000 picoseconds. - Weight::from_parts(8_081_460, 0) + // Minimum execution time: 22_279_000 picoseconds. + Weight::from_parts(23_106_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -276,13 +277,12 @@ impl pallet_scheduler::WeightInfo for WeightInfo { /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) /// Storage: `Scheduler::Retries` (r:0 w:1) /// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// The range of component `s` is `[1, 50]`. fn cancel_retry_named() -> Weight { // Proof Size summary in bytes: - // Measured: `324 + s * (185 ±0)` + // Measured: `9655` // Estimated: `42428` - // Minimum execution time: 10_673_000 picoseconds. - Weight::from_parts(12_212_185, 0) + // Minimum execution time: 29_649_000 picoseconds. + Weight::from_parts(30_472_000, 0) .saturating_add(Weight::from_parts(0, 42428)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_sudo.rs b/polkadot/runtime/rococo/src/weights/pallet_sudo.rs index 694174954fc..ecc31dc3fa9 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_sudo.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_sudo.rs @@ -16,24 +16,26 @@ //! Autogenerated weights for `pallet_sudo` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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_sudo // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_sudo -// --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ @@ -54,8 +56,8 @@ impl pallet_sudo::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `132` // Estimated: `1517` - // Minimum execution time: 8_432_000 picoseconds. - Weight::from_parts(8_757_000, 0) + // Minimum execution time: 8_336_000 picoseconds. + Weight::from_parts(8_569_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -66,8 +68,8 @@ impl pallet_sudo::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `132` // Estimated: `1517` - // Minimum execution time: 9_167_000 picoseconds. - Weight::from_parts(9_397_000, 0) + // Minimum execution time: 8_858_000 picoseconds. + Weight::from_parts(9_238_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) } @@ -77,8 +79,8 @@ impl pallet_sudo::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `132` // Estimated: `1517` - // Minimum execution time: 9_133_000 picoseconds. - Weight::from_parts(9_573_000, 0) + // Minimum execution time: 8_921_000 picoseconds. + Weight::from_parts(9_324_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) } @@ -88,10 +90,21 @@ impl pallet_sudo::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `132` // Estimated: `1517` - // Minimum execution time: 7_374_000 picoseconds. - Weight::from_parts(7_702_000, 0) + // Minimum execution time: 7_398_000 picoseconds. + Weight::from_parts(7_869_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn check_only_sudo_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `132` + // Estimated: `1517` + // Minimum execution time: 3_146_000 picoseconds. + Weight::from_parts(3_314_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_timestamp.rs b/polkadot/runtime/rococo/src/weights/pallet_timestamp.rs index 1bb2e227ab7..7d79621b9e6 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_timestamp.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_timestamp.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_timestamp` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_timestamp // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,26 +50,26 @@ use core::marker::PhantomData; /// Weight functions for `pallet_timestamp`. pub struct WeightInfo(PhantomData); impl pallet_timestamp::WeightInfo for WeightInfo { - /// Storage: Timestamp Now (r:1 w:1) - /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe CurrentSlot (r:1 w:0) - /// Proof: Babe CurrentSlot (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Timestamp::Now` (r:1 w:1) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::CurrentSlot` (r:1 w:0) + /// Proof: `Babe::CurrentSlot` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn set() -> Weight { // Proof Size summary in bytes: - // Measured: `311` + // Measured: `137` // Estimated: `1493` - // Minimum execution time: 10_103_000 picoseconds. - Weight::from_parts(10_597_000, 0) + // Minimum execution time: 5_596_000 picoseconds. + Weight::from_parts(5_823_000, 0) .saturating_add(Weight::from_parts(0, 1493)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } fn on_finalize() -> Weight { // Proof Size summary in bytes: - // Measured: `94` + // Measured: `57` // Estimated: `0` - // Minimum execution time: 4_718_000 picoseconds. - Weight::from_parts(4_839_000, 0) + // Minimum execution time: 2_777_000 picoseconds. + Weight::from_parts(2_900_000, 0) .saturating_add(Weight::from_parts(0, 0)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_transaction_payment.rs b/polkadot/runtime/rococo/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..44dfab289fb --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,68 @@ +// 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 . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! 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: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// benchmark +// pallet +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_transaction_payment +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `252` + // Estimated: `1737` + // Minimum execution time: 33_070_000 picoseconds. + Weight::from_parts(33_730_000, 0) + .saturating_add(Weight::from_parts(0, 1737)) + .saturating_add(T::DbWeight::get().reads(3)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/pallet_treasury.rs b/polkadot/runtime/rococo/src/weights/pallet_treasury.rs index 06246ada72f..42d7b260764 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_treasury.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_treasury.rs @@ -16,25 +16,28 @@ //! Autogenerated weights for `pallet_treasury` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-07, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cob`, CPU: `` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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/debug/polkadot +// ./target/production/polkadot // benchmark // pallet // --chain=rococo-dev // --steps=50 -// --repeat=2 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_treasury // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --output=./runtime/rococo/src/weights/ -// --header=./file_header.txt +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,18 +50,18 @@ use core::marker::PhantomData; /// Weight functions for `pallet_treasury`. pub struct WeightInfo(PhantomData); impl pallet_treasury::WeightInfo for WeightInfo { - /// Storage: Treasury ProposalCount (r:1 w:1) - /// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:0 w:1) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + /// Storage: `Treasury::ProposalCount` (r:1 w:1) + /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:0 w:1) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) fn spend_local() -> Weight { // Proof Size summary in bytes: - // Measured: `42` + // Measured: `142` // Estimated: `1887` - // Minimum execution time: 177_000_000 picoseconds. - Weight::from_parts(191_000_000, 0) + // Minimum execution time: 9_928_000 picoseconds. + Weight::from_parts(10_560_000, 0) .saturating_add(Weight::from_parts(0, 1887)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(3)) @@ -67,111 +70,103 @@ impl pallet_treasury::WeightInfo for WeightInfo { /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) fn remove_approval() -> Weight { // Proof Size summary in bytes: - // Measured: `127` + // Measured: `227` // Estimated: `1887` - // Minimum execution time: 80_000_000 picoseconds. - Weight::from_parts(82_000_000, 0) + // Minimum execution time: 5_386_000 picoseconds. + Weight::from_parts(5_585_000, 0) .saturating_add(Weight::from_parts(0, 1887)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Treasury Deactivated (r:1 w:1) - /// Proof: Treasury Deactivated (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:1) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Treasury Approvals (r:1 w:1) - /// Proof: Treasury Approvals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) - /// Storage: Treasury Proposals (r:99 w:99) - /// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) - /// Storage: System Account (r:199 w:199) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Bounties BountyApprovals (r:1 w:1) - /// Proof: Bounties BountyApprovals (max_values: Some(1), max_size: Some(402), added: 897, mode: MaxEncodedLen) + /// Storage: `Treasury::Deactivated` (r:1 w:1) + /// Proof: `Treasury::Deactivated` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Approvals` (r:1 w:1) + /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Proposals` (r:99 w:99) + /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:199 w:199) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Bounties::BountyApprovals` (r:1 w:1) + /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) /// The range of component `p` is `[0, 99]`. fn on_initialize_proposals(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `331 + p * (251 ±0)` + // Measured: `431 + p * (251 ±0)` // Estimated: `3593 + p * (5206 ±0)` - // Minimum execution time: 887_000_000 picoseconds. - Weight::from_parts(828_616_021, 0) + // Minimum execution time: 43_737_000 picoseconds. + Weight::from_parts(39_883_021, 0) .saturating_add(Weight::from_parts(0, 3593)) - // Standard Error: 695_351 - .saturating_add(Weight::from_parts(566_114_524, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Standard Error: 12_917 + .saturating_add(Weight::from_parts(31_796_205, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into()))) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(p.into()))) .saturating_add(Weight::from_parts(0, 5206).saturating_mul(p.into())) } - /// Storage: AssetRate ConversionRateToNative (r:1 w:0) - /// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen) - /// Storage: Treasury SpendCount (r:1 w:1) - /// Proof: Treasury SpendCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Treasury Spends (r:0 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen) + /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:0) + /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`) + /// Storage: `Treasury::SpendCount` (r:1 w:1) + /// Proof: `Treasury::SpendCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Treasury::Spends` (r:0 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`) fn spend() -> Weight { // Proof Size summary in bytes: - // Measured: `114` - // Estimated: `4702` - // Minimum execution time: 208_000_000 picoseconds. - Weight::from_parts(222_000_000, 0) - .saturating_add(Weight::from_parts(0, 4702)) + // Measured: `215` + // Estimated: `4703` + // Minimum execution time: 16_829_000 picoseconds. + Weight::from_parts(17_251_000, 0) + .saturating_add(Weight::from_parts(0, 4703)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen) - /// Storage: XcmPallet QueryCounter (r:1 w:1) - /// Proof Skipped: XcmPallet QueryCounter (max_values: Some(1), max_size: None, mode: Measured) - /// 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 Queries (r:0 w:1) - /// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`) + /// Storage: `XcmPallet::QueryCounter` (r:1 w:1) + /// Proof: `XcmPallet::QueryCounter` (`max_values`: Some(1), `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`) + /// Storage: `XcmPallet::Queries` (r:0 w:1) + /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn payout() -> Weight { // Proof Size summary in bytes: - // Measured: `737` - // Estimated: `5313` - // Minimum execution time: 551_000_000 picoseconds. - Weight::from_parts(569_000_000, 0) - .saturating_add(Weight::from_parts(0, 5313)) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(6)) + // Measured: `458` + // Estimated: `5318` + // Minimum execution time: 41_554_000 picoseconds. + Weight::from_parts(42_451_000, 0) + .saturating_add(Weight::from_parts(0, 5318)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen) - /// Storage: XcmPallet Queries (r:1 w:1) - /// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`) + /// Storage: `XcmPallet::Queries` (r:1 w:1) + /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) fn check_status() -> Weight { // Proof Size summary in bytes: - // Measured: `442` - // Estimated: `5313` - // Minimum execution time: 245_000_000 picoseconds. - Weight::from_parts(281_000_000, 0) - .saturating_add(Weight::from_parts(0, 5313)) + // Measured: `306` + // Estimated: `5318` + // Minimum execution time: 22_546_000 picoseconds. + Weight::from_parts(23_151_000, 0) + .saturating_add(Weight::from_parts(0, 5318)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Treasury Spends (r:1 w:1) - /// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen) + /// Storage: `Treasury::Spends` (r:1 w:1) + /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`) fn void_spend() -> Weight { // Proof Size summary in bytes: - // Measured: `172` - // Estimated: `5313` - // Minimum execution time: 147_000_000 picoseconds. - Weight::from_parts(160_000_000, 0) - .saturating_add(Weight::from_parts(0, 5313)) + // Measured: `278` + // Estimated: `5318` + // Minimum execution time: 12_169_000 picoseconds. + Weight::from_parts(12_484_000, 0) + .saturating_add(Weight::from_parts(0, 5318)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_utility.rs b/polkadot/runtime/rococo/src/weights/pallet_utility.rs index f50f60eaad7..6f2a374247f 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_utility.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_utility.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_utility` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_utility // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -52,18 +55,18 @@ impl pallet_utility::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_738_000 picoseconds. - Weight::from_parts(2_704_821, 0) + // Minimum execution time: 4_041_000 picoseconds. + Weight::from_parts(5_685_496, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_999 - .saturating_add(Weight::from_parts(4_627_278, 0).saturating_mul(c.into())) + // Standard Error: 810 + .saturating_add(Weight::from_parts(3_177_197, 0).saturating_mul(c.into())) } fn as_derivative() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_294_000 picoseconds. - Weight::from_parts(5_467_000, 0) + // Minimum execution time: 3_667_000 picoseconds. + Weight::from_parts(3_871_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `c` is `[0, 1000]`. @@ -71,18 +74,18 @@ impl pallet_utility::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_828_000 picoseconds. - Weight::from_parts(4_650_678, 0) + // Minimum execution time: 4_116_000 picoseconds. + Weight::from_parts(6_453_932, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2_789 - .saturating_add(Weight::from_parts(4_885_004, 0).saturating_mul(c.into())) + // Standard Error: 825 + .saturating_add(Weight::from_parts(3_366_112, 0).saturating_mul(c.into())) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_020_000 picoseconds. - Weight::from_parts(9_205_000, 0) + // Minimum execution time: 5_630_000 picoseconds. + Weight::from_parts(5_956_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// The range of component `c` is `[0, 1000]`. @@ -90,10 +93,10 @@ impl pallet_utility::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_852_000 picoseconds. - Weight::from_parts(20_703_134, 0) + // Minimum execution time: 4_165_000 picoseconds. + Weight::from_parts(5_442_561, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3_924 - .saturating_add(Weight::from_parts(4_604_529, 0).saturating_mul(c.into())) + // Standard Error: 460 + .saturating_add(Weight::from_parts(3_173_577, 0).saturating_mul(c.into())) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_vesting.rs b/polkadot/runtime/rococo/src/weights/pallet_vesting.rs index 2596207d583..c21ab087774 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_vesting.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_vesting.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_vesting` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=pallet_vesting // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,143 +50,143 @@ use core::marker::PhantomData; /// Weight functions for `pallet_vesting`. pub struct WeightInfo(PhantomData); impl pallet_vesting::WeightInfo for WeightInfo { - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `277 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 32_820_000 picoseconds. - Weight::from_parts(31_640_992, 0) + // Minimum execution time: 29_288_000 picoseconds. + Weight::from_parts(29_095_507, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 449 - .saturating_add(Weight::from_parts(45_254, 0).saturating_mul(l.into())) - // Standard Error: 800 - .saturating_add(Weight::from_parts(72_178, 0).saturating_mul(s.into())) + // Standard Error: 1_679 + .saturating_add(Weight::from_parts(33_164, 0).saturating_mul(l.into())) + // Standard Error: 2_988 + .saturating_add(Weight::from_parts(67_092, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `277 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 36_054_000 picoseconds. - Weight::from_parts(35_825_428, 0) + // Minimum execution time: 31_003_000 picoseconds. + Weight::from_parts(30_528_438, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 749 - .saturating_add(Weight::from_parts(31_738, 0).saturating_mul(l.into())) - // Standard Error: 1_333 - .saturating_add(Weight::from_parts(40_580, 0).saturating_mul(s.into())) + // Standard Error: 1_586 + .saturating_add(Weight::from_parts(35_429, 0).saturating_mul(l.into())) + // Standard Error: 2_823 + .saturating_add(Weight::from_parts(76_505, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `380 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 35_440_000 picoseconds. - Weight::from_parts(34_652_647, 0) + // Minimum execution time: 31_269_000 picoseconds. + Weight::from_parts(30_661_898, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 517 - .saturating_add(Weight::from_parts(41_942, 0).saturating_mul(l.into())) - // Standard Error: 920 - .saturating_add(Weight::from_parts(66_074, 0).saturating_mul(s.into())) + // Standard Error: 1_394 + .saturating_add(Weight::from_parts(39_300, 0).saturating_mul(l.into())) + // Standard Error: 2_480 + .saturating_add(Weight::from_parts(78_849, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `380 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 38_880_000 picoseconds. - Weight::from_parts(39_625_819, 0) + // Minimum execution time: 33_040_000 picoseconds. + Weight::from_parts(32_469_674, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 1_032 - .saturating_add(Weight::from_parts(29_856, 0).saturating_mul(l.into())) - // Standard Error: 1_837 - .saturating_add(Weight::from_parts(6_210, 0).saturating_mul(s.into())) + // Standard Error: 1_418 + .saturating_add(Weight::from_parts(44_206, 0).saturating_mul(l.into())) + // Standard Error: 2_523 + .saturating_add(Weight::from_parts(74_224, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `451 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 68_294_000 picoseconds. - Weight::from_parts(68_313_394, 0) + // Minimum execution time: 62_032_000 picoseconds. + Weight::from_parts(63_305_621, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 983 - .saturating_add(Weight::from_parts(48_156, 0).saturating_mul(l.into())) - // Standard Error: 1_750 - .saturating_add(Weight::from_parts(87_719, 0).saturating_mul(s.into())) + // Standard Error: 2_277 + .saturating_add(Weight::from_parts(42_767, 0).saturating_mul(l.into())) + // Standard Error: 4_051 + .saturating_add(Weight::from_parts(65_487, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `554 + l * (25 ±0) + s * (36 ±0)` // Estimated: `6196` - // Minimum execution time: 70_529_000 picoseconds. - Weight::from_parts(70_619_962, 0) + // Minimum execution time: 63_303_000 picoseconds. + Weight::from_parts(65_180_847, 0) .saturating_add(Weight::from_parts(0, 6196)) - // Standard Error: 1_259 - .saturating_add(Weight::from_parts(50_685, 0).saturating_mul(l.into())) - // Standard Error: 2_241 - .saturating_add(Weight::from_parts(91_444, 0).saturating_mul(s.into())) + // Standard Error: 2_220 + .saturating_add(Weight::from_parts(28_829, 0).saturating_mul(l.into())) + // Standard Error: 3_951 + .saturating_add(Weight::from_parts(84_970, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -192,59 +195,70 @@ impl pallet_vesting::WeightInfo for WeightInfo { /// Storage: `Balances::Locks` (r:1 w:1) /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. - fn force_remove_vesting_schedule(l: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `555 + l * (25 ±0) + s * (36 ±0)` - // Estimated: `4764` - // Minimum execution time: 41_497_000 picoseconds. - Weight::from_parts(38_763_834, 4764) - // Standard Error: 2_030 - .saturating_add(Weight::from_parts(99_580, 0).saturating_mul(l.into())) - // Standard Error: 3_750 - .saturating_add(Weight::from_parts(132_188, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `378 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 36_428_000 picoseconds. - Weight::from_parts(35_604_430, 0) + // Minimum execution time: 31_440_000 picoseconds. + Weight::from_parts(30_773_053, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 504 - .saturating_add(Weight::from_parts(43_191, 0).saturating_mul(l.into())) - // Standard Error: 931 - .saturating_add(Weight::from_parts(66_795, 0).saturating_mul(s.into())) + // Standard Error: 1_474 + .saturating_add(Weight::from_parts(43_019, 0).saturating_mul(l.into())) + // Standard Error: 2_723 + .saturating_add(Weight::from_parts(73_360, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `378 + l * (25 ±0) + s * (36 ±0)` // Estimated: `4764` - // Minimum execution time: 40_696_000 picoseconds. - Weight::from_parts(39_741_284, 0) + // Minimum execution time: 34_221_000 picoseconds. + Weight::from_parts(33_201_125, 0) + .saturating_add(Weight::from_parts(0, 4764)) + // Standard Error: 1_751 + .saturating_add(Weight::from_parts(44_088, 0).saturating_mul(l.into())) + // Standard Error: 3_234 + .saturating_add(Weight::from_parts(86_228, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `l` is `[0, 49]`. + /// The range of component `s` is `[2, 28]`. + fn force_remove_vesting_schedule(l: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `451 + l * (25 ±0) + s * (36 ±0)` + // Estimated: `4764` + // Minimum execution time: 35_553_000 picoseconds. + Weight::from_parts(34_974_083, 0) .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 478 - .saturating_add(Weight::from_parts(43_792, 0).saturating_mul(l.into())) - // Standard Error: 883 - .saturating_add(Weight::from_parts(66_540, 0).saturating_mul(s.into())) + // Standard Error: 1_560 + .saturating_add(Weight::from_parts(34_615, 0).saturating_mul(l.into())) + // Standard Error: 2_882 + .saturating_add(Weight::from_parts(83_419, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/runtime/rococo/src/weights/pallet_whitelist.rs b/polkadot/runtime/rococo/src/weights/pallet_whitelist.rs index 7c307deec4c..ec67268d144 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_whitelist.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_whitelist.rs @@ -16,26 +16,28 @@ //! Autogenerated weights for `pallet_whitelist` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-aahe6cbd-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-bn-ce5rx-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_whitelist // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=pallet_whitelist -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,67 +52,75 @@ pub struct WeightInfo(PhantomData); impl pallet_whitelist::WeightInfo for WeightInfo { /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) - /// Storage: `Preimage::StatusFor` (r:1 w:1) + /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn whitelist_call() -> Weight { // Proof Size summary in bytes: // Measured: `223` // Estimated: `3556` - // Minimum execution time: 20_035_000 picoseconds. - Weight::from_parts(20_452_000, 0) + // Minimum execution time: 16_686_000 picoseconds. + Weight::from_parts(17_042_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) - /// Storage: `Preimage::StatusFor` (r:1 w:1) + /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) fn remove_whitelisted_call() -> Weight { // Proof Size summary in bytes: // Measured: `352` // Estimated: `3556` - // Minimum execution time: 20_247_000 picoseconds. - Weight::from_parts(20_808_000, 0) + // Minimum execution time: 18_250_000 picoseconds. + Weight::from_parts(19_026_000, 0) .saturating_add(Weight::from_parts(0, 3556)) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) /// Storage: `Preimage::PreimageFor` (r:1 w:1) /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) - /// Storage: `Preimage::StatusFor` (r:1 w:1) + /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4194294]`. fn dispatch_whitelisted_call(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `428 + n * (1 ±0)` // Estimated: `3892 + n * (1 ±0)` - // Minimum execution time: 32_633_000 picoseconds. - Weight::from_parts(32_855_000, 0) + // Minimum execution time: 28_741_000 picoseconds. + Weight::from_parts(29_024_000, 0) .saturating_add(Weight::from_parts(0, 3892)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_223, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(3)) + // Standard Error: 7 + .saturating_add(Weight::from_parts(1_305, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) - /// Storage: `Preimage::StatusFor` (r:1 w:1) + /// Storage: `Preimage::StatusFor` (r:1 w:0) /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 10000]`. fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `352` // Estimated: `3556` - // Minimum execution time: 23_833_000 picoseconds. - Weight::from_parts(24_698_994, 0) + // Minimum execution time: 21_670_000 picoseconds. + Weight::from_parts(22_561_364, 0) .saturating_add(Weight::from_parts(0, 3556)) // Standard Error: 4 - .saturating_add(Weight::from_parts(1_454, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(Weight::from_parts(1_468, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } } diff --git a/polkadot/runtime/rococo/src/weights/pallet_xcm.rs b/polkadot/runtime/rococo/src/weights/pallet_xcm.rs index 5544ca44658..d5cf33515e6 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_xcm.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_xcm.rs @@ -17,23 +17,25 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-02-29, 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` //! 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_xcm // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_xcm -// --chain=rococo-dev // --header=./polkadot/file_header.txt // --output=./polkadot/runtime/rococo/src/weights/ @@ -60,8 +62,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 25_043_000 picoseconds. - Weight::from_parts(25_682_000, 0) + // Minimum execution time: 25_521_000 picoseconds. + Weight::from_parts(25_922_000, 0) .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) @@ -80,8 +82,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 107_570_000 picoseconds. - Weight::from_parts(109_878_000, 0) + // Minimum execution time: 112_185_000 picoseconds. + Weight::from_parts(115_991_000, 0) .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) @@ -100,8 +102,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `232` // Estimated: `3697` - // Minimum execution time: 106_341_000 picoseconds. - Weight::from_parts(109_135_000, 0) + // Minimum execution time: 108_693_000 picoseconds. + Weight::from_parts(111_853_000, 0) .saturating_add(Weight::from_parts(0, 3697)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) @@ -120,8 +122,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 108_372_000 picoseconds. - Weight::from_parts(112_890_000, 0) + // Minimum execution time: 113_040_000 picoseconds. + Weight::from_parts(115_635_000, 0) .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) @@ -130,8 +132,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_957_000 picoseconds. - Weight::from_parts(7_417_000, 0) + // Minimum execution time: 6_979_000 picoseconds. + Weight::from_parts(7_342_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::SupportedVersion` (r:0 w:1) @@ -140,8 +142,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_053_000 picoseconds. - Weight::from_parts(7_462_000, 0) + // Minimum execution time: 7_144_000 picoseconds. + Weight::from_parts(7_297_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -149,8 +151,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_918_000 picoseconds. - Weight::from_parts(2_037_000, 0) + // Minimum execution time: 1_886_000 picoseconds. + Weight::from_parts(1_995_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: `XcmPallet::VersionNotifiers` (r:1 w:1) @@ -171,8 +173,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3645` - // Minimum execution time: 30_417_000 picoseconds. - Weight::from_parts(31_191_000, 0) + // Minimum execution time: 31_238_000 picoseconds. + Weight::from_parts(31_955_000, 0) .saturating_add(Weight::from_parts(0, 3645)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) @@ -193,8 +195,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `360` // Estimated: `3825` - // Minimum execution time: 36_666_000 picoseconds. - Weight::from_parts(37_779_000, 0) + // Minimum execution time: 37_237_000 picoseconds. + Weight::from_parts(38_569_000, 0) .saturating_add(Weight::from_parts(0, 3825)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -205,8 +207,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_869_000 picoseconds. - Weight::from_parts(2_003_000, 0) + // Minimum execution time: 1_884_000 picoseconds. + Weight::from_parts(2_028_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -216,8 +218,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `22` // Estimated: `13387` - // Minimum execution time: 16_188_000 picoseconds. - Weight::from_parts(16_435_000, 0) + // Minimum execution time: 16_048_000 picoseconds. + Weight::from_parts(16_617_000, 0) .saturating_add(Weight::from_parts(0, 13387)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -228,8 +230,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `26` // Estimated: `13391` - // Minimum execution time: 16_431_000 picoseconds. - Weight::from_parts(16_935_000, 0) + // Minimum execution time: 16_073_000 picoseconds. + Weight::from_parts(16_672_000, 0) .saturating_add(Weight::from_parts(0, 13391)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -240,8 +242,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `40` // Estimated: `15880` - // Minimum execution time: 18_460_000 picoseconds. - Weight::from_parts(18_885_000, 0) + // Minimum execution time: 18_422_000 picoseconds. + Weight::from_parts(18_900_000, 0) .saturating_add(Weight::from_parts(0, 15880)) .saturating_add(T::DbWeight::get().reads(6)) } @@ -259,8 +261,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `216` // Estimated: `6156` - // Minimum execution time: 29_623_000 picoseconds. - Weight::from_parts(30_661_000, 0) + // Minimum execution time: 30_373_000 picoseconds. + Weight::from_parts(30_972_000, 0) .saturating_add(Weight::from_parts(0, 6156)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) @@ -271,8 +273,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `69` // Estimated: `10959` - // Minimum execution time: 12_043_000 picoseconds. - Weight::from_parts(12_360_000, 0) + // Minimum execution time: 11_863_000 picoseconds. + Weight::from_parts(12_270_000, 0) .saturating_add(Weight::from_parts(0, 10959)) .saturating_add(T::DbWeight::get().reads(4)) } @@ -282,8 +284,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `33` // Estimated: `13398` - // Minimum execution time: 16_511_000 picoseconds. - Weight::from_parts(17_011_000, 0) + // Minimum execution time: 16_733_000 picoseconds. + Weight::from_parts(17_094_000, 0) .saturating_add(Weight::from_parts(0, 13398)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) @@ -302,8 +304,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `216` // Estimated: `13581` - // Minimum execution time: 39_041_000 picoseconds. - Weight::from_parts(39_883_000, 0) + // Minimum execution time: 39_236_000 picoseconds. + Weight::from_parts(40_587_000, 0) .saturating_add(Weight::from_parts(0, 13581)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) @@ -316,8 +318,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1485` - // Minimum execution time: 2_030_000 picoseconds. - Weight::from_parts(2_150_000, 0) + // Minimum execution time: 2_145_000 picoseconds. + Weight::from_parts(2_255_000, 0) .saturating_add(Weight::from_parts(0, 1485)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(2)) @@ -328,8 +330,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `7576` // Estimated: `11041` - // Minimum execution time: 22_615_000 picoseconds. - Weight::from_parts(23_008_000, 0) + // Minimum execution time: 22_518_000 picoseconds. + Weight::from_parts(22_926_000, 0) .saturating_add(Weight::from_parts(0, 11041)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_fungible.rs new file mode 100644 index 00000000000..dc5e5d8ca4b --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_fungible.rs @@ -0,0 +1,191 @@ +// 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 . + +//! Autogenerated weights for `pallet_xcm_benchmarks::fungible` +//! +//! 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: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// benchmark +// pallet +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_xcm_benchmarks::fungible +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_fungible.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm_benchmarks::fungible`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_benchmarks::fungible::WeightInfo for WeightInfo { + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn withdraw_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3593` + // Minimum execution time: 27_223_000 picoseconds. + Weight::from_parts(27_947_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn transfer_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `6196` + // Minimum execution time: 36_502_000 picoseconds. + Weight::from_parts(37_023_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// 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`) + fn transfer_reserve_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `6196` + // Minimum execution time: 85_152_000 picoseconds. + Weight::from_parts(86_442_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn reserve_asset_deposited() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// 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`) + fn initiate_reserve_withdraw() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 56_571_000 picoseconds. + Weight::from_parts(58_163_000, 0) + .saturating_add(Weight::from_parts(0, 3746)) + .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`) + fn receive_teleported_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `103` + // Estimated: `3593` + // Minimum execution time: 27_411_000 picoseconds. + Weight::from_parts(27_953_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn deposit_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 20_776_000 picoseconds. + Weight::from_parts(21_145_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// 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`) + fn deposit_reserve_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 51_738_000 picoseconds. + Weight::from_parts(53_251_000, 0) + .saturating_add(Weight::from_parts(0, 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`) + fn initiate_teleport() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 39_333_000 picoseconds. + Weight::from_parts(40_515_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_generic.rs b/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_generic.rs new file mode 100644 index 00000000000..b62f36172ba --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_generic.rs @@ -0,0 +1,347 @@ +// 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 . + +//! Autogenerated weights for `pallet_xcm_benchmarks::generic` +//! +//! 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: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// benchmark +// pallet +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=pallet_xcm_benchmarks::generic +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/pallet_xcm_benchmarks_generic.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm_benchmarks::generic`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_benchmarks::generic::WeightInfo for WeightInfo { + /// 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`) + fn report_holding() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 55_210_000 picoseconds. + Weight::from_parts(56_613_000, 0) + .saturating_add(Weight::from_parts(0, 3746)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + fn buy_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_246_000 picoseconds. + Weight::from_parts(1_339_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `XcmPallet::Queries` (r:1 w:0) + /// Proof: `XcmPallet::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn query_response() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3465` + // Minimum execution time: 5_377_000 picoseconds. + Weight::from_parts(5_549_000, 0) + .saturating_add(Weight::from_parts(0, 3465)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn transact() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_008_000 picoseconds. + Weight::from_parts(7_361_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn refund_surplus() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_700_000 picoseconds. + Weight::from_parts(1_848_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn set_error_handler() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_198_000 picoseconds. + Weight::from_parts(1_265_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn set_appendix() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_197_000 picoseconds. + Weight::from_parts(1_267_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn clear_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_193_000 picoseconds. + Weight::from_parts(1_258_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn descend_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_268_000 picoseconds. + Weight::from_parts(1_342_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn clear_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_173_000 picoseconds. + Weight::from_parts(1_248_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// 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`) + fn report_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 53_715_000 picoseconds. + Weight::from_parts(54_860_000, 0) + .saturating_add(Weight::from_parts(0, 3746)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `XcmPallet::AssetTraps` (r:1 w:1) + /// Proof: `XcmPallet::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn claim_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `23` + // Estimated: `3488` + // Minimum execution time: 8_621_000 picoseconds. + Weight::from_parts(8_903_000, 0) + .saturating_add(Weight::from_parts(0, 3488)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn trap() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_211_000 picoseconds. + Weight::from_parts(1_281_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// 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`) + fn subscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 26_448_000 picoseconds. + Weight::from_parts(27_057_000, 0) + .saturating_add(Weight::from_parts(0, 3645)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `XcmPallet::VersionNotifyTargets` (r:0 w:1) + /// Proof: `XcmPallet::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn unsubscribe_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 3_498_000 picoseconds. + Weight::from_parts(3_614_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn burn_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_575_000 picoseconds. + Weight::from_parts(1_698_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn expect_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_334_000 picoseconds. + Weight::from_parts(1_435_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn expect_origin() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_244_000 picoseconds. + Weight::from_parts(1_337_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn expect_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_244_000 picoseconds. + Weight::from_parts(1_331_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn expect_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_407_000 picoseconds. + Weight::from_parts(1_522_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// 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`) + fn query_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 62_963_000 picoseconds. + Weight::from_parts(64_556_000, 0) + .saturating_add(Weight::from_parts(0, 3746)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + fn expect_pallet() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_458_000 picoseconds. + Weight::from_parts(8_741_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// 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`) + fn report_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 54_068_000 picoseconds. + Weight::from_parts(55_665_000, 0) + .saturating_add(Weight::from_parts(0, 3746)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + fn clear_transact_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_290_000 picoseconds. + Weight::from_parts(1_348_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn set_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_189_000 picoseconds. + Weight::from_parts(1_268_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn clear_topic() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_197_000 picoseconds. + Weight::from_parts(1_276_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn set_fees_mode() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_197_000 picoseconds. + Weight::from_parts(1_253_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn unpaid_execution() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 1_250_000 picoseconds. + Weight::from_parts(1_354_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_assigned_slots.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_assigned_slots.rs index 2aaf282c59d..fd13c2ac946 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_assigned_slots.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_assigned_slots.rs @@ -16,26 +16,28 @@ //! Autogenerated weights for `runtime_common::assigned_slots` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ynta1nyy-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-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=runtime_common::assigned_slots // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json -// --pallet=runtime_common::assigned_slots -// --chain=rococo-dev -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/ +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_assigned_slots.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -68,15 +70,15 @@ impl polkadot_runtime_common::assigned_slots::WeightInf /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn assign_perm_parachain_slot() -> Weight { // Proof Size summary in bytes: - // Measured: `673` - // Estimated: `4138` - // Minimum execution time: 84_646_000 picoseconds. - Weight::from_parts(91_791_000, 0) - .saturating_add(Weight::from_parts(0, 4138)) + // Measured: `730` + // Estimated: `4195` + // Minimum execution time: 71_337_000 picoseconds. + Weight::from_parts(80_807_000, 0) + .saturating_add(Weight::from_parts(0, 4195)) .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes(5)) } - /// Storage: `Registrar::Paras` (r:1 w:1) + /// Storage: `Registrar::Paras` (r:1 w:0) /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::ParaLifecycles` (r:1 w:1) /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -98,13 +100,13 @@ impl polkadot_runtime_common::assigned_slots::WeightInf /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn assign_temp_parachain_slot() -> Weight { // Proof Size summary in bytes: - // Measured: `673` - // Estimated: `4138` - // Minimum execution time: 68_091_000 picoseconds. - Weight::from_parts(77_310_000, 0) - .saturating_add(Weight::from_parts(0, 4138)) + // Measured: `730` + // Estimated: `4195` + // Minimum execution time: 60_188_000 picoseconds. + Weight::from_parts(63_932_000, 0) + .saturating_add(Weight::from_parts(0, 4195)) .saturating_add(T::DbWeight::get().reads(10)) - .saturating_add(T::DbWeight::get().writes(7)) + .saturating_add(T::DbWeight::get().writes(6)) } /// Storage: `AssignedSlots::PermanentSlots` (r:1 w:0) /// Proof: `AssignedSlots::PermanentSlots` (`max_values`: None, `max_size`: Some(20), added: 2495, mode: `MaxEncodedLen`) @@ -118,11 +120,11 @@ impl polkadot_runtime_common::assigned_slots::WeightInf /// Proof: `AssignedSlots::TemporarySlotCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn unassign_parachain_slot() -> Weight { // Proof Size summary in bytes: - // Measured: `823` - // Estimated: `4288` - // Minimum execution time: 38_081_000 picoseconds. - Weight::from_parts(40_987_000, 0) - .saturating_add(Weight::from_parts(0, 4288)) + // Measured: `856` + // Estimated: `4321` + // Minimum execution time: 35_764_000 picoseconds. + Weight::from_parts(38_355_000, 0) + .saturating_add(Weight::from_parts(0, 4321)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -132,8 +134,8 @@ impl polkadot_runtime_common::assigned_slots::WeightInf // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_182_000 picoseconds. - Weight::from_parts(7_437_000, 0) + // Minimum execution time: 4_634_000 picoseconds. + Weight::from_parts(4_852_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -143,8 +145,8 @@ impl polkadot_runtime_common::assigned_slots::WeightInf // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_153_000 picoseconds. - Weight::from_parts(7_456_000, 0) + // Minimum execution time: 4_563_000 picoseconds. + Weight::from_parts(4_829_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_auctions.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_auctions.rs index 897dc1c1752..acf2da8cab9 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_auctions.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_auctions.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::auctions` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::auctions // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_auctions.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_auctions.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -55,84 +58,82 @@ impl polkadot_runtime_common::auctions::WeightInfo for // Proof Size summary in bytes: // Measured: `4` // Estimated: `1493` - // Minimum execution time: 12_805_000 picoseconds. - Weight::from_parts(13_153_000, 0) + // Minimum execution time: 7_307_000 picoseconds. + Weight::from_parts(7_680_000, 0) .saturating_add(Weight::from_parts(0, 1493)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Paras ParaLifecycles (r:1 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions AuctionCounter (r:1 w:0) - /// Proof: Auctions AuctionCounter (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Auctions AuctionInfo (r:1 w:0) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Slots Leases (r:1 w:0) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions Winning (r:1 w:1) - /// Proof: Auctions Winning (max_values: None, max_size: Some(1920), added: 4395, mode: MaxEncodedLen) - /// Storage: Auctions ReservedAmounts (r:2 w:2) - /// Proof: Auctions ReservedAmounts (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::AuctionCounter` (r:1 w:0) + /// Proof: `Auctions::AuctionCounter` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Auctions::AuctionInfo` (r:1 w:0) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Slots::Leases` (r:1 w:0) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::Winning` (r:1 w:1) + /// Proof: `Auctions::Winning` (`max_values`: None, `max_size`: Some(1920), added: 4395, mode: `MaxEncodedLen`) + /// Storage: `Auctions::ReservedAmounts` (r:2 w:2) + /// Proof: `Auctions::ReservedAmounts` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn bid() -> Weight { // Proof Size summary in bytes: - // Measured: `728` + // Measured: `761` // Estimated: `6060` - // Minimum execution time: 77_380_000 picoseconds. - Weight::from_parts(80_503_000, 0) + // Minimum execution time: 75_448_000 picoseconds. + Weight::from_parts(78_716_000, 0) .saturating_add(Weight::from_parts(0, 6060)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Auctions AuctionInfo (r:1 w:1) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Babe NextRandomness (r:1 w:0) - /// Proof: Babe NextRandomness (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: Babe EpochStart (r:1 w:0) - /// Proof: Babe EpochStart (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Auctions AuctionCounter (r:1 w:0) - /// Proof: Auctions AuctionCounter (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Auctions Winning (r:3600 w:3600) - /// Proof: Auctions Winning (max_values: None, max_size: Some(1920), added: 4395, mode: MaxEncodedLen) - /// Storage: Auctions ReservedAmounts (r:37 w:36) - /// Proof: Auctions ReservedAmounts (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) - /// Storage: System Account (r:36 w:36) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Slots Leases (r:2 w:2) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) + /// Storage: `Auctions::AuctionInfo` (r:1 w:1) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Babe::NextRandomness` (r:1 w:0) + /// Proof: `Babe::NextRandomness` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `Babe::EpochStart` (r:1 w:0) + /// Proof: `Babe::EpochStart` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Auctions::AuctionCounter` (r:1 w:0) + /// Proof: `Auctions::AuctionCounter` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Auctions::Winning` (r:3600 w:3600) + /// Proof: `Auctions::Winning` (`max_values`: None, `max_size`: Some(1920), added: 4395, mode: `MaxEncodedLen`) + /// Storage: `Auctions::ReservedAmounts` (r:37 w:36) + /// Proof: `Auctions::ReservedAmounts` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:36 w:36) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Slots::Leases` (r:2 w:2) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn on_initialize() -> Weight { // Proof Size summary in bytes: - // Measured: `6947789` + // Measured: `6947017` // Estimated: `15822990` - // Minimum execution time: 6_311_055_000 picoseconds. - Weight::from_parts(6_409_142_000, 0) + // Minimum execution time: 7_120_207_000 picoseconds. + Weight::from_parts(7_273_496_000, 0) .saturating_add(Weight::from_parts(0, 15822990)) - .saturating_add(T::DbWeight::get().reads(3683)) - .saturating_add(T::DbWeight::get().writes(3678)) + .saturating_add(T::DbWeight::get().reads(3682)) + .saturating_add(T::DbWeight::get().writes(3677)) } - /// Storage: Auctions ReservedAmounts (r:37 w:36) - /// Proof: Auctions ReservedAmounts (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) - /// Storage: System Account (r:36 w:36) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Auctions Winning (r:3600 w:3600) - /// Proof: Auctions Winning (max_values: None, max_size: Some(1920), added: 4395, mode: MaxEncodedLen) - /// Storage: Auctions AuctionInfo (r:0 w:1) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: `Auctions::ReservedAmounts` (r:37 w:36) + /// Proof: `Auctions::ReservedAmounts` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:36 w:36) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Auctions::Winning` (r:3600 w:3600) + /// Proof: `Auctions::Winning` (`max_values`: None, `max_size`: Some(1920), added: 4395, mode: `MaxEncodedLen`) + /// Storage: `Auctions::AuctionInfo` (r:0 w:1) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) fn cancel_auction() -> Weight { // Proof Size summary in bytes: // Measured: `177732` // Estimated: `15822990` - // Minimum execution time: 4_849_561_000 picoseconds. - Weight::from_parts(4_955_226_000, 0) + // Minimum execution time: 5_536_281_000 picoseconds. + Weight::from_parts(5_675_163_000, 0) .saturating_add(Weight::from_parts(0, 15822990)) .saturating_add(T::DbWeight::get().reads(3673)) .saturating_add(T::DbWeight::get().writes(3673)) diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_claims.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_claims.rs index 8fbc798dbd4..3871310678e 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_claims.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_claims.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::claims` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::claims // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_claims.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_claims.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -67,100 +70,113 @@ impl polkadot_runtime_common::claims::WeightInfo for We // Proof Size summary in bytes: // Measured: `558` // Estimated: `4764` - // Minimum execution time: 144_931_000 picoseconds. - Weight::from_parts(156_550_000, 0) + // Minimum execution time: 181_028_000 picoseconds. + Weight::from_parts(194_590_000, 0) .saturating_add(Weight::from_parts(0, 4764)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Claims Total (r:1 w:1) - /// Proof Skipped: Claims Total (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Claims Vesting (r:0 w:1) - /// Proof Skipped: Claims Vesting (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Claims (r:0 w:1) - /// Proof Skipped: Claims Claims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Signing (r:0 w:1) - /// Proof Skipped: Claims Signing (max_values: None, max_size: None, mode: Measured) + /// Storage: `Claims::Total` (r:1 w:1) + /// Proof: `Claims::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Vesting` (r:0 w:1) + /// Proof: `Claims::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Claims` (r:0 w:1) + /// Proof: `Claims::Claims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:0 w:1) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) fn mint_claim() -> Weight { // Proof Size summary in bytes: // Measured: `216` // Estimated: `1701` - // Minimum execution time: 11_300_000 picoseconds. - Weight::from_parts(11_642_000, 0) + // Minimum execution time: 11_224_000 picoseconds. + Weight::from_parts(13_342_000, 0) .saturating_add(Weight::from_parts(0, 1701)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Claims Claims (r:1 w:1) - /// Proof Skipped: Claims Claims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Signing (r:1 w:1) - /// Proof Skipped: Claims Signing (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Total (r:1 w:1) - /// Proof Skipped: Claims Total (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Claims Vesting (r:1 w:1) - /// Proof Skipped: Claims Vesting (max_values: None, max_size: None, mode: Measured) - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Claims::Claims` (r:1 w:1) + /// Proof: `Claims::Claims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:1 w:1) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Total` (r:1 w:1) + /// Proof: `Claims::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Vesting` (r:1 w:1) + /// Proof: `Claims::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) fn claim_attest() -> Weight { // Proof Size summary in bytes: // Measured: `558` // Estimated: `4764` - // Minimum execution time: 149_112_000 picoseconds. - Weight::from_parts(153_872_000, 0) + // Minimum execution time: 187_964_000 picoseconds. + Weight::from_parts(202_553_000, 0) .saturating_add(Weight::from_parts(0, 4764)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Claims Preclaims (r:1 w:1) - /// Proof Skipped: Claims Preclaims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Signing (r:1 w:1) - /// Proof Skipped: Claims Signing (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Claims (r:1 w:1) - /// Proof Skipped: Claims Claims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Total (r:1 w:1) - /// Proof Skipped: Claims Total (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Claims Vesting (r:1 w:1) - /// Proof Skipped: Claims Vesting (max_values: None, max_size: None, mode: Measured) - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:0) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + /// Storage: `Claims::Preclaims` (r:1 w:1) + /// Proof: `Claims::Preclaims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:1 w:1) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Claims` (r:1 w:1) + /// Proof: `Claims::Claims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Total` (r:1 w:1) + /// Proof: `Claims::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Vesting` (r:1 w:1) + /// Proof: `Claims::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) fn attest() -> Weight { // Proof Size summary in bytes: // Measured: `632` // Estimated: `4764` - // Minimum execution time: 69_619_000 picoseconds. - Weight::from_parts(79_242_000, 0) + // Minimum execution time: 78_210_000 picoseconds. + Weight::from_parts(84_581_000, 0) .saturating_add(Weight::from_parts(0, 4764)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } - /// Storage: Claims Claims (r:1 w:2) - /// Proof Skipped: Claims Claims (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Vesting (r:1 w:2) - /// Proof Skipped: Claims Vesting (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Signing (r:1 w:2) - /// Proof Skipped: Claims Signing (max_values: None, max_size: None, mode: Measured) - /// Storage: Claims Preclaims (r:1 w:1) - /// Proof Skipped: Claims Preclaims (max_values: None, max_size: None, mode: Measured) + /// Storage: `Claims::Claims` (r:1 w:2) + /// Proof: `Claims::Claims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Vesting` (r:1 w:2) + /// Proof: `Claims::Vesting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:1 w:2) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Preclaims` (r:1 w:1) + /// Proof: `Claims::Preclaims` (`max_values`: None, `max_size`: None, mode: `Measured`) fn move_claim() -> Weight { // Proof Size summary in bytes: // Measured: `440` // Estimated: `3905` - // Minimum execution time: 22_066_000 picoseconds. - Weight::from_parts(22_483_000, 0) + // Minimum execution time: 33_940_000 picoseconds. + Weight::from_parts(48_438_000, 0) .saturating_add(Weight::from_parts(0, 3905)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(7)) } + /// Storage: `Claims::Preclaims` (r:1 w:0) + /// Proof: `Claims::Preclaims` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Claims::Signing` (r:1 w:0) + /// Proof: `Claims::Signing` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn prevalidate_attests() -> Weight { + // Proof Size summary in bytes: + // Measured: `296` + // Estimated: `3761` + // Minimum execution time: 9_025_000 picoseconds. + Weight::from_parts(10_563_000, 0) + .saturating_add(Weight::from_parts(0, 3761)) + .saturating_add(T::DbWeight::get().reads(2)) + } } diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_crowdloan.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_crowdloan.rs index b75ff8d42e7..2a01de67acc 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_crowdloan.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_crowdloan.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::crowdloan` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::crowdloan // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_crowdloan.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_crowdloan.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -61,158 +64,154 @@ impl polkadot_runtime_common::crowdloan::WeightInfo for // Proof Size summary in bytes: // Measured: `438` // Estimated: `3903` - // Minimum execution time: 50_399_000 picoseconds. - Weight::from_parts(51_641_000, 0) + // Minimum execution time: 46_095_000 picoseconds. + Weight::from_parts(48_111_000, 0) .saturating_add(Weight::from_parts(0, 3903)) .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Crowdloan Funds (r:1 w:1) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: Slots Leases (r:1 w:0) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions AuctionInfo (r:1 w:0) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances InactiveIssuance (r:1 w:1) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: Crowdloan EndingsCount (r:1 w:0) - /// Proof Skipped: Crowdloan EndingsCount (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Crowdloan NewRaise (r:1 w:1) - /// Proof Skipped: Crowdloan NewRaise (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: unknown `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) - /// Proof Skipped: unknown `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) + /// Storage: `Crowdloan::Funds` (r:1 w:1) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Slots::Leases` (r:1 w:0) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::AuctionInfo` (r:1 w:0) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Crowdloan::EndingsCount` (r:1 w:0) + /// Proof: `Crowdloan::EndingsCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::NewRaise` (r:1 w:1) + /// Proof: `Crowdloan::NewRaise` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) fn contribute() -> Weight { // Proof Size summary in bytes: - // Measured: `530` - // Estimated: `3995` - // Minimum execution time: 128_898_000 picoseconds. - Weight::from_parts(130_277_000, 0) - .saturating_add(Weight::from_parts(0, 3995)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(5)) + // Measured: `563` + // Estimated: `4028` + // Minimum execution time: 133_059_000 picoseconds. + Weight::from_parts(136_515_000, 0) + .saturating_add(Weight::from_parts(0, 4028)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Crowdloan Funds (r:1 w:1) - /// Proof Skipped: Crowdloan Funds (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: Balances InactiveIssuance (r:1 w:1) - /// Proof: Balances InactiveIssuance (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) - /// Storage: unknown `0xc85982571aa615c788ef9b2c16f54f25773fd439e8ee1ed2aa3ae43d48e880f0` (r:1 w:1) - /// Proof Skipped: unknown `0xc85982571aa615c788ef9b2c16f54f25773fd439e8ee1ed2aa3ae43d48e880f0` (r:1 w:1) + /// Storage: `Crowdloan::Funds` (r:1 w:1) + /// Proof: `Crowdloan::Funds` (`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: UNKNOWN KEY `0xc85982571aa615c788ef9b2c16f54f25773fd439e8ee1ed2aa3ae43d48e880f0` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xc85982571aa615c788ef9b2c16f54f25773fd439e8ee1ed2aa3ae43d48e880f0` (r:1 w:1) fn withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `689` + // Measured: `687` // Estimated: `6196` - // Minimum execution time: 69_543_000 picoseconds. - Weight::from_parts(71_522_000, 0) + // Minimum execution time: 71_733_000 picoseconds. + Weight::from_parts(74_034_000, 0) .saturating_add(Weight::from_parts(0, 6196)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Skipped Metadata (r:0 w:0) - /// Proof Skipped: Skipped Metadata (max_values: None, max_size: None, mode: Measured) + /// Storage: `Skipped::Metadata` (r:0 w:0) + /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `k` is `[0, 1000]`. fn refund(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `127 + k * (189 ±0)` - // Estimated: `140 + k * (189 ±0)` - // Minimum execution time: 50_735_000 picoseconds. - Weight::from_parts(52_282_000, 0) - .saturating_add(Weight::from_parts(0, 140)) - // Standard Error: 21_607 - .saturating_add(Weight::from_parts(38_955_985, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `125 + k * (189 ±0)` + // Estimated: `138 + k * (189 ±0)` + // Minimum execution time: 46_016_000 picoseconds. + Weight::from_parts(48_260_000, 0) + .saturating_add(Weight::from_parts(0, 138)) + // Standard Error: 21_140 + .saturating_add(Weight::from_parts(39_141_925, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(k.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 189).saturating_mul(k.into())) } - /// Storage: Crowdloan Funds (r:1 w:1) - /// Proof Skipped: Crowdloan Funds (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: `Crowdloan::Funds` (r:1 w:1) + /// Proof: `Crowdloan::Funds` (`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`) fn dissolve() -> Weight { // Proof Size summary in bytes: - // Measured: `515` + // Measured: `514` // Estimated: `6196` - // Minimum execution time: 43_100_000 picoseconds. - Weight::from_parts(44_272_000, 0) + // Minimum execution time: 44_724_000 picoseconds. + Weight::from_parts(47_931_000, 0) .saturating_add(Weight::from_parts(0, 6196)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Crowdloan Funds (r:1 w:1) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) + /// Storage: `Crowdloan::Funds` (r:1 w:1) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) fn edit() -> Weight { // Proof Size summary in bytes: - // Measured: `235` - // Estimated: `3700` - // Minimum execution time: 18_702_000 picoseconds. - Weight::from_parts(19_408_000, 0) - .saturating_add(Weight::from_parts(0, 3700)) + // Measured: `234` + // Estimated: `3699` + // Minimum execution time: 19_512_000 picoseconds. + Weight::from_parts(21_129_000, 0) + .saturating_add(Weight::from_parts(0, 3699)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Crowdloan Funds (r:1 w:0) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: unknown `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) - /// Proof Skipped: unknown `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) + /// Storage: `Crowdloan::Funds` (r:1 w:0) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: UNKNOWN KEY `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) + /// Proof: UNKNOWN KEY `0xd861ea1ebf4800d4b89f4ff787ad79ee96d9a708c85b57da7eb8f9ddeda61291` (r:1 w:1) fn add_memo() -> Weight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `3877` - // Minimum execution time: 25_568_000 picoseconds. - Weight::from_parts(26_203_000, 0) + // Minimum execution time: 33_529_000 picoseconds. + Weight::from_parts(37_082_000, 0) .saturating_add(Weight::from_parts(0, 3877)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Crowdloan Funds (r:1 w:0) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: Crowdloan NewRaise (r:1 w:1) - /// Proof Skipped: Crowdloan NewRaise (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `Crowdloan::Funds` (r:1 w:0) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::NewRaise` (r:1 w:1) + /// Proof: `Crowdloan::NewRaise` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn poke() -> Weight { // Proof Size summary in bytes: - // Measured: `239` - // Estimated: `3704` - // Minimum execution time: 17_832_000 picoseconds. - Weight::from_parts(18_769_000, 0) - .saturating_add(Weight::from_parts(0, 3704)) + // Measured: `238` + // Estimated: `3703` + // Minimum execution time: 23_153_000 picoseconds. + Weight::from_parts(24_181_000, 0) + .saturating_add(Weight::from_parts(0, 3703)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Auctions AuctionInfo (r:1 w:0) - /// Proof: Auctions AuctionInfo (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: Crowdloan EndingsCount (r:1 w:1) - /// Proof Skipped: Crowdloan EndingsCount (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Crowdloan NewRaise (r:1 w:1) - /// Proof Skipped: Crowdloan NewRaise (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Crowdloan Funds (r:100 w:0) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions AuctionCounter (r:1 w:0) - /// Proof: Auctions AuctionCounter (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Paras ParaLifecycles (r:100 w:0) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Slots Leases (r:100 w:0) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Auctions Winning (r:1 w:1) - /// Proof: Auctions Winning (max_values: None, max_size: Some(1920), added: 4395, mode: MaxEncodedLen) - /// Storage: Auctions ReservedAmounts (r:100 w:100) - /// Proof: Auctions ReservedAmounts (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen) - /// Storage: System Account (r:100 w:100) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Auctions::AuctionInfo` (r:1 w:0) + /// Proof: `Auctions::AuctionInfo` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `Crowdloan::EndingsCount` (r:1 w:1) + /// Proof: `Crowdloan::EndingsCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::NewRaise` (r:1 w:1) + /// Proof: `Crowdloan::NewRaise` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::Funds` (r:100 w:0) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::AuctionCounter` (r:1 w:0) + /// Proof: `Auctions::AuctionCounter` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Paras::ParaLifecycles` (r:100 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Slots::Leases` (r:100 w:0) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Auctions::Winning` (r:1 w:1) + /// Proof: `Auctions::Winning` (`max_values`: None, `max_size`: Some(1920), added: 4395, mode: `MaxEncodedLen`) + /// Storage: `Auctions::ReservedAmounts` (r:100 w:100) + /// Proof: `Auctions::ReservedAmounts` (`max_values`: None, `max_size`: Some(60), added: 2535, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:100 w:100) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `n` is `[2, 100]`. fn on_initialize(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `197 + n * (356 ±0)` + // Measured: `229 + n * (356 ±0)` // Estimated: `5385 + n * (2832 ±0)` - // Minimum execution time: 128_319_000 picoseconds. - Weight::from_parts(130_877_000, 0) + // Minimum execution time: 120_164_000 picoseconds. + Weight::from_parts(3_390_119, 0) .saturating_add(Weight::from_parts(0, 5385)) - // Standard Error: 61_381 - .saturating_add(Weight::from_parts(60_209_202, 0).saturating_mul(n.into())) + // Standard Error: 41_727 + .saturating_add(Weight::from_parts(54_453_016, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3)) diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_identity_migrator.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_identity_migrator.rs index 4ea6f679680..3df3c6c8dd9 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_identity_migrator.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_identity_migrator.rs @@ -1,36 +1,43 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Polkadot. -// 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. +// 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 . //! Autogenerated weights for `runtime_common::identity_migrator` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-07, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `sbtb`, CPU: `13th Gen Intel(R) Core(TM) i7-1365U` +//! HOSTNAME: `runner-bn-ce5rx-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/release/polkadot +// ./target/production/polkadot // benchmark // pallet // --chain=rococo-dev -// --steps=2 -// --repeat=1 +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::identity_migrator // --extrinsic=* -// --output=./migrator-release.rs +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_identity_migrator.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -44,7 +51,7 @@ use core::marker::PhantomData; pub struct WeightInfo(PhantomData); impl polkadot_runtime_common::identity_migrator::WeightInfo for WeightInfo { /// Storage: `Identity::IdentityOf` (r:1 w:1) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7538), added: 10013, mode: `MaxEncodedLen`) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// Storage: `Identity::SubsOf` (r:1 w:1) /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:2 w:2) @@ -63,34 +70,34 @@ impl polkadot_runtime_common::identity_migrator::Weight /// The range of component `s` is `[0, 100]`. fn reap_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `7292 + r * (8 ±0) + s * (32 ±0)` - // Estimated: `11003 + r * (8 ±0) + s * (33 ±0)` - // Minimum execution time: 163_756_000 picoseconds. - Weight::from_parts(158_982_500, 0) - .saturating_add(Weight::from_parts(0, 11003)) - // Standard Error: 1_143_629 - .saturating_add(Weight::from_parts(238_675, 0).saturating_mul(r.into())) - // Standard Error: 228_725 - .saturating_add(Weight::from_parts(1_529_645, 0).saturating_mul(s.into())) + // Measured: `7457 + r * (5 ±0) + s * (32 ±0)` + // Estimated: `11037 + r * (7 ±0) + s * (32 ±0)` + // Minimum execution time: 157_343_000 picoseconds. + Weight::from_parts(159_289_236, 0) + .saturating_add(Weight::from_parts(0, 11037)) + // Standard Error: 16_439 + .saturating_add(Weight::from_parts(224_293, 0).saturating_mul(r.into())) + // Standard Error: 3_367 + .saturating_add(Weight::from_parts(1_383_637, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes(6)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) - .saturating_add(Weight::from_parts(0, 8).saturating_mul(r.into())) - .saturating_add(Weight::from_parts(0, 33).saturating_mul(s.into())) + .saturating_add(Weight::from_parts(0, 7).saturating_mul(r.into())) + .saturating_add(Weight::from_parts(0, 32).saturating_mul(s.into())) } /// Storage: `Identity::IdentityOf` (r:1 w:1) - /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7538), added: 10013, mode: `MaxEncodedLen`) + /// Proof: `Identity::IdentityOf` (`max_values`: None, `max_size`: Some(7572), added: 10047, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Identity::SubsOf` (r:1 w:1) /// Proof: `Identity::SubsOf` (`max_values`: None, `max_size`: Some(3258), added: 5733, mode: `MaxEncodedLen`) fn poke_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `7229` - // Estimated: `11003` - // Minimum execution time: 137_570_000 picoseconds. - Weight::from_parts(137_570_000, 0) - .saturating_add(Weight::from_parts(0, 11003)) + // Measured: `7242` + // Estimated: `11037` + // Minimum execution time: 114_384_000 picoseconds. + Weight::from_parts(115_741_000, 0) + .saturating_add(Weight::from_parts(0, 11037)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_paras_registrar.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_paras_registrar.rs index 0ce09d1be2a..ad261a7f774 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_paras_registrar.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_paras_registrar.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::paras_registrar` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::paras_registrar // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_paras_registrar.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -55,167 +58,161 @@ impl polkadot_runtime_common::paras_registrar::WeightIn /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) fn reserve() -> Weight { // Proof Size summary in bytes: - // Measured: `97` - // Estimated: `3562` - // Minimum execution time: 29_948_000 picoseconds. - Weight::from_parts(30_433_000, 0) - .saturating_add(Weight::from_parts(0, 3562)) + // Measured: `96` + // Estimated: `3561` + // Minimum execution time: 24_109_000 picoseconds. + Weight::from_parts(24_922_000, 0) + .saturating_add(Weight::from_parts(0, 3561)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:1 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CodeByHashRefs (r:1 w:1) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:0 w:1) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpcomingParasGenesis (r:0 w:1) - /// Proof Skipped: Paras UpcomingParasGenesis (max_values: None, max_size: None, mode: Measured) + /// Storage: `Registrar::Paras` (r:1 w:1) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:1 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:1) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:0 w:1) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpcomingParasGenesis` (r:0 w:1) + /// Proof: `Paras::UpcomingParasGenesis` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `616` - // Estimated: `4081` - // Minimum execution time: 6_332_113_000 picoseconds. - Weight::from_parts(6_407_158_000, 0) - .saturating_add(Weight::from_parts(0, 4081)) - .saturating_add(T::DbWeight::get().reads(8)) + // Measured: `352` + // Estimated: `3817` + // Minimum execution time: 7_207_580_000 picoseconds. + Weight::from_parts(7_298_567_000, 0) + .saturating_add(Weight::from_parts(0, 3817)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(8)) } - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:1 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CodeByHashRefs (r:1 w:1) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:0 w:1) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpcomingParasGenesis (r:0 w:1) - /// Proof Skipped: Paras UpcomingParasGenesis (max_values: None, max_size: None, mode: Measured) + /// Storage: `Registrar::Paras` (r:1 w:1) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:1 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:1) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:0 w:1) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpcomingParasGenesis` (r:0 w:1) + /// Proof: `Paras::UpcomingParasGenesis` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_register() -> Weight { // Proof Size summary in bytes: - // Measured: `533` - // Estimated: `3998` - // Minimum execution time: 6_245_403_000 picoseconds. - Weight::from_parts(6_289_575_000, 0) - .saturating_add(Weight::from_parts(0, 3998)) - .saturating_add(T::DbWeight::get().reads(8)) + // Measured: `269` + // Estimated: `3734` + // Minimum execution time: 7_196_460_000 picoseconds. + Weight::from_parts(7_385_729_000, 0) + .saturating_add(Weight::from_parts(0, 3734)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(8)) } - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras FutureCodeHash (r:1 w:0) - /// Proof Skipped: Paras FutureCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: MessageQueue BookStateFor (r:1 w:0) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(55), added: 2530, mode: MaxEncodedLen) - /// Storage: Registrar PendingSwap (r:0 w:1) - /// Proof Skipped: Registrar PendingSwap (max_values: None, max_size: None, mode: Measured) + /// Storage: `Registrar::Paras` (r:1 w:1) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeHash` (r:1 w:0) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `MessageQueue::BookStateFor` (r:1 w:0) + /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) + /// Storage: `Registrar::PendingSwap` (r:0 w:1) + /// Proof: `Registrar::PendingSwap` (`max_values`: None, `max_size`: None, mode: `Measured`) fn deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `476` - // Estimated: `3941` - // Minimum execution time: 49_822_000 picoseconds. - Weight::from_parts(50_604_000, 0) - .saturating_add(Weight::from_parts(0, 3941)) + // Measured: `499` + // Estimated: `3964` + // Minimum execution time: 54_761_000 picoseconds. + Weight::from_parts(57_931_000, 0) + .saturating_add(Weight::from_parts(0, 3964)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Registrar Paras (r:1 w:0) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:2 w:2) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: Registrar PendingSwap (r:1 w:1) - /// Proof Skipped: Registrar PendingSwap (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: Crowdloan Funds (r:2 w:2) - /// Proof Skipped: Crowdloan Funds (max_values: None, max_size: None, mode: Measured) - /// Storage: Slots Leases (r:2 w:2) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) + /// Storage: `Registrar::Paras` (r:1 w:0) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:2 w:2) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::PendingSwap` (r:1 w:1) + /// Proof: `Registrar::PendingSwap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Crowdloan::Funds` (r:2 w:2) + /// Proof: `Crowdloan::Funds` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Slots::Leases` (r:2 w:2) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap() -> Weight { // Proof Size summary in bytes: - // Measured: `780` - // Estimated: `6720` - // Minimum execution time: 55_166_000 picoseconds. - Weight::from_parts(56_913_000, 0) - .saturating_add(Weight::from_parts(0, 6720)) + // Measured: `837` + // Estimated: `6777` + // Minimum execution time: 59_564_000 picoseconds. + Weight::from_parts(62_910_000, 0) + .saturating_add(Weight::from_parts(0, 6777)) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(8)) } - /// Storage: Paras FutureCodeHash (r:1 w:1) - /// Proof Skipped: Paras FutureCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeRestrictionSignal (r:1 w:1) - /// Proof Skipped: Paras UpgradeRestrictionSignal (max_values: None, max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:1 w:0) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeCooldowns (r:1 w:1) - /// Proof Skipped: Paras UpgradeCooldowns (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:1 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CodeByHashRefs (r:1 w:1) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// The range of component `b` is `[1, 3145728]`. + /// Storage: `Paras::FutureCodeHash` (r:1 w:1) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:1 w:1) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeCooldowns` (r:1 w:1) + /// Proof: `Paras::UpgradeCooldowns` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:1 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:1) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `b` is `[9, 3145728]`. fn schedule_code_upgrade(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `464` - // Estimated: `3929` - // Minimum execution time: 43_650_000 picoseconds. - Weight::from_parts(43_918_000, 0) - .saturating_add(Weight::from_parts(0, 3929)) - // Standard Error: 6 - .saturating_add(Weight::from_parts(2_041, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `201` + // Estimated: `3666` + // Minimum execution time: 33_106_000 picoseconds. + Weight::from_parts(33_526_000, 0) + .saturating_add(Weight::from_parts(0, 3666)) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_334, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `b` is `[1, 1048576]`. fn set_current_head(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_666_000 picoseconds. - Weight::from_parts(8_893_000, 0) + // Minimum execution time: 5_992_000 picoseconds. + Weight::from_parts(12_059_689, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2 - .saturating_add(Weight::from_parts(855, 0).saturating_mul(b.into())) + // Standard Error: 0 + .saturating_add(Weight::from_parts(959, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().writes(1)) } } diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_slots.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_slots.rs index 8c601aa8486..b99ee1f9a0d 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_slots.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_common_slots.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_common::slots` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_common::slots // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_common_slots.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_slots.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -53,80 +56,76 @@ impl polkadot_runtime_common::slots::WeightInfo for Wei /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn force_lease() -> Weight { // Proof Size summary in bytes: - // Measured: `287` - // Estimated: `3752` - // Minimum execution time: 29_932_000 picoseconds. - Weight::from_parts(30_334_000, 0) - .saturating_add(Weight::from_parts(0, 3752)) + // Measured: `320` + // Estimated: `3785` + // Minimum execution time: 26_570_000 picoseconds. + Weight::from_parts(27_619_000, 0) + .saturating_add(Weight::from_parts(0, 3785)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: Paras Parachains (r:1 w:0) - /// Proof Skipped: Paras Parachains (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Slots Leases (r:101 w:100) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:200 w:200) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: Registrar Paras (r:100 w:100) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::Parachains` (r:1 w:0) + /// Proof: `Paras::Parachains` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Slots::Leases` (r:101 w:100) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:200 w:200) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[0, 100]`. /// The range of component `t` is `[0, 100]`. fn manage_lease_period_start(c: u32, t: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `26 + c * (47 ±0) + t * (308 ±0)` - // Estimated: `2800 + c * (2526 ±0) + t * (2789 ±0)` - // Minimum execution time: 634_547_000 picoseconds. - Weight::from_parts(643_045_000, 0) - .saturating_add(Weight::from_parts(0, 2800)) - // Standard Error: 81_521 - .saturating_add(Weight::from_parts(2_705_219, 0).saturating_mul(c.into())) - // Standard Error: 81_521 - .saturating_add(Weight::from_parts(11_464_132, 0).saturating_mul(t.into())) + // Measured: `594 + c * (20 ±0) + t * (234 ±0)` + // Estimated: `4065 + c * (2496 ±0) + t * (2709 ±0)` + // Minimum execution time: 729_793_000 picoseconds. + Weight::from_parts(740_820_000, 0) + .saturating_add(Weight::from_parts(0, 4065)) + // Standard Error: 88_206 + .saturating_add(Weight::from_parts(2_793_142, 0).saturating_mul(c.into())) + // Standard Error: 88_206 + .saturating_add(Weight::from_parts(8_933_065, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(t.into()))) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2526).saturating_mul(c.into())) - .saturating_add(Weight::from_parts(0, 2789).saturating_mul(t.into())) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(t.into()))) + .saturating_add(Weight::from_parts(0, 2496).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 2709).saturating_mul(t.into())) } - /// Storage: Slots Leases (r:1 w:1) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: System Account (r:8 w:8) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Slots::Leases` (r:1 w:1) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:8 w:8) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn clear_all_leases() -> Weight { // Proof Size summary in bytes: - // Measured: `2759` + // Measured: `2792` // Estimated: `21814` - // Minimum execution time: 129_756_000 picoseconds. - Weight::from_parts(131_810_000, 0) + // Minimum execution time: 123_888_000 picoseconds. + Weight::from_parts(131_245_000, 0) .saturating_add(Weight::from_parts(0, 21814)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(9)) } - /// Storage: Slots Leases (r:1 w:0) - /// Proof Skipped: Slots Leases (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras ParaLifecycles (r:1 w:1) - /// Proof Skipped: Paras ParaLifecycles (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) - /// Storage: Registrar Paras (r:1 w:1) - /// Proof Skipped: Registrar Paras (max_values: None, max_size: None, mode: Measured) + /// Storage: `Slots::Leases` (r:1 w:0) + /// Proof: `Slots::Leases` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:1) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn trigger_onboard() -> Weight { // Proof Size summary in bytes: - // Measured: `707` - // Estimated: `4172` - // Minimum execution time: 29_527_000 picoseconds. - Weight::from_parts(30_055_000, 0) - .saturating_add(Weight::from_parts(0, 4172)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(3)) + // Measured: `612` + // Estimated: `4077` + // Minimum execution time: 27_341_000 picoseconds. + Weight::from_parts(28_697_000, 0) + .saturating_add(Weight::from_parts(0, 4077)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) } } diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_configuration.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_configuration.rs index 5592a85c90f..3ca49aaa165 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_configuration.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_configuration.rs @@ -17,25 +17,27 @@ //! Autogenerated weights for `runtime_parachains::configuration` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-02-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-02-29, 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` //! 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=runtime_parachains::configuration // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=runtime_parachains::configuration -// --chain=rococo-dev // --header=./polkadot/file_header.txt -// --output=./polkadot/runtime/rococo/src/weights/ +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_configuration.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -58,8 +60,8 @@ impl polkadot_runtime_parachains::configuration::Weight // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_789_000 picoseconds. - Weight::from_parts(8_269_000, 0) + // Minimum execution time: 7_689_000 picoseconds. + Weight::from_parts(8_089_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -74,8 +76,8 @@ impl polkadot_runtime_parachains::configuration::Weight // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_851_000 picoseconds. - Weight::from_parts(8_152_000, 0) + // Minimum execution time: 7_735_000 picoseconds. + Weight::from_parts(8_150_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -90,8 +92,8 @@ impl polkadot_runtime_parachains::configuration::Weight // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_960_000 picoseconds. - Weight::from_parts(8_276_000, 0) + // Minimum execution time: 7_902_000 picoseconds. + Weight::from_parts(8_196_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -116,8 +118,8 @@ impl polkadot_runtime_parachains::configuration::Weight // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_912_000 picoseconds. - Weight::from_parts(8_164_000, 0) + // Minimum execution time: 7_634_000 picoseconds. + Weight::from_parts(7_983_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -132,8 +134,8 @@ impl polkadot_runtime_parachains::configuration::Weight // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 9_782_000 picoseconds. - Weight::from_parts(10_373_000, 0) + // Minimum execution time: 9_580_000 picoseconds. + Weight::from_parts(9_989_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -148,8 +150,8 @@ impl polkadot_runtime_parachains::configuration::Weight // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_870_000 picoseconds. - Weight::from_parts(8_274_000, 0) + // Minimum execution time: 7_787_000 picoseconds. + Weight::from_parts(8_008_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -164,8 +166,8 @@ impl polkadot_runtime_parachains::configuration::Weight // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 9_960_000 picoseconds. - Weight::from_parts(10_514_000, 0) + // Minimum execution time: 9_557_000 picoseconds. + Weight::from_parts(9_994_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -180,8 +182,8 @@ impl polkadot_runtime_parachains::configuration::Weight // Proof Size summary in bytes: // Measured: `151` // Estimated: `1636` - // Minimum execution time: 7_913_000 picoseconds. - Weight::from_parts(8_338_000, 0) + // Minimum execution time: 7_775_000 picoseconds. + Weight::from_parts(7_989_000, 0) .saturating_add(Weight::from_parts(0, 1636)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_disputes.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_disputes.rs index a20515502b1..6f86d6a1259 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_disputes.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_disputes.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::disputes` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_parachains::disputes // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_disputes.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_disputes.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -53,8 +56,8 @@ impl polkadot_runtime_parachains::disputes::WeightInfo // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_937_000 picoseconds. - Weight::from_parts(3_082_000, 0) + // Minimum execution time: 1_855_000 picoseconds. + Weight::from_parts(2_015_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_initializer.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_initializer.rs index 6065c32b174..b915c4ec0f3 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_initializer.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_initializer.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::initializer` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_parachains::initializer // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_initializer.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_initializer.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -54,11 +57,11 @@ impl polkadot_runtime_parachains::initializer::WeightIn // Proof Size summary in bytes: // Measured: `0 + d * (11 ±0)` // Estimated: `1480 + d * (11 ±0)` - // Minimum execution time: 3_771_000 picoseconds. - Weight::from_parts(6_491_437, 0) + // Minimum execution time: 2_634_000 picoseconds. + Weight::from_parts(2_728_000, 0) .saturating_add(Weight::from_parts(0, 1480)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(1_356, 0).saturating_mul(d.into())) + // Standard Error: 19 + .saturating_add(Weight::from_parts(2_499, 0).saturating_mul(d.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(Weight::from_parts(0, 11).saturating_mul(d.into())) diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_on_demand.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_on_demand.rs index 0c36eeaf7d4..1dd62d129f9 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_on_demand.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_on_demand.rs @@ -23,12 +23,18 @@ //! 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=runtime_parachains::assigner_on_demand // --extrinsic=* +// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras.rs index 2dcabb7c36b..c463552b6ad 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `runtime_parachains::paras` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! 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: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm5`, 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-bn-ce5rx-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 @@ -29,12 +29,15 @@ // --chain=rococo-dev // --steps=50 // --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares // --pallet=runtime_parachains::paras // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/rococo/src/weights/runtime_parachains_paras.rs +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_parachains_paras.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -64,230 +67,231 @@ impl polkadot_runtime_parachains::paras::WeightInfo for // Proof Size summary in bytes: // Measured: `8309` // Estimated: `11774` - // Minimum execution time: 31_941_000 picoseconds. - Weight::from_parts(32_139_000, 0) + // Minimum execution time: 27_488_000 picoseconds. + Weight::from_parts(27_810_000, 0) .saturating_add(Weight::from_parts(0, 11774)) - // Standard Error: 5 - .saturating_add(Weight::from_parts(2_011, 0).saturating_mul(c.into())) + // Standard Error: 8 + .saturating_add(Weight::from_parts(2_189, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `s` is `[1, 1048576]`. fn force_set_current_head(s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_275_000 picoseconds. - Weight::from_parts(8_321_000, 0) + // Minimum execution time: 5_793_000 picoseconds. + Weight::from_parts(7_987_606, 0) .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 2 - .saturating_add(Weight::from_parts(858, 0).saturating_mul(s.into())) + // Standard Error: 1 + .saturating_add(Weight::from_parts(971, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: Paras Heads (r:0 w:1) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_set_most_recent_context() -> Weight { - Weight::from_parts(10_155_000, 0) - // Standard Error: 0 - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_733_000 picoseconds. + Weight::from_parts(2_954_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras FutureCodeHash (r:1 w:1) - /// Proof Skipped: Paras FutureCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CurrentCodeHash (r:1 w:0) - /// Proof Skipped: Paras CurrentCodeHash (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeCooldowns (r:1 w:1) - /// Proof Skipped: Paras UpgradeCooldowns (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:1 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras CodeByHashRefs (r:1 w:1) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeRestrictionSignal (r:0 w:1) - /// Proof Skipped: Paras UpgradeRestrictionSignal (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::FutureCodeHash` (r:1 w:1) + /// Proof: `Paras::FutureCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) + /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeCooldowns` (r:1 w:1) + /// Proof: `Paras::UpgradeCooldowns` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:1 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:1) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeRestrictionSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 3145728]`. fn force_schedule_code_upgrade(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `8715` - // Estimated: `12180` - // Minimum execution time: 49_923_000 picoseconds. - Weight::from_parts(50_688_000, 0) - .saturating_add(Weight::from_parts(0, 12180)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_976, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(7)) + // Measured: `8452` + // Estimated: `11917` + // Minimum execution time: 6_072_000 picoseconds. + Weight::from_parts(6_128_000, 0) + .saturating_add(Weight::from_parts(0, 11917)) + // Standard Error: 2 + .saturating_add(Weight::from_parts(2_334, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(6)) } - /// Storage: Paras FutureCodeUpgrades (r:1 w:0) - /// Proof Skipped: Paras FutureCodeUpgrades (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras UpgradeGoAheadSignal (r:0 w:1) - /// Proof Skipped: Paras UpgradeGoAheadSignal (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Registrar::Paras` (r:1 w:0) + /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::Heads` (r:0 w:1) + /// Proof: `Paras::Heads` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpgradeGoAheadSignal` (r:0 w:1) + /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::MostRecentContext` (r:0 w:1) + /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `s` is `[1, 1048576]`. fn force_note_new_head(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `95` - // Estimated: `3560` - // Minimum execution time: 14_408_000 picoseconds. - Weight::from_parts(14_647_000, 0) - .saturating_add(Weight::from_parts(0, 3560)) - // Standard Error: 2 - .saturating_add(Weight::from_parts(858, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `163` + // Estimated: `3628` + // Minimum execution time: 15_166_000 picoseconds. + Weight::from_parts(21_398_053, 0) + .saturating_add(Weight::from_parts(0, 3628)) + // Standard Error: 1 + .saturating_add(Weight::from_parts(976, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn force_queue_action() -> Weight { // Proof Size summary in bytes: - // Measured: `4288` - // Estimated: `7753` - // Minimum execution time: 20_009_000 picoseconds. - Weight::from_parts(20_518_000, 0) - .saturating_add(Weight::from_parts(0, 7753)) + // Measured: `4312` + // Estimated: `7777` + // Minimum execution time: 16_345_000 picoseconds. + Weight::from_parts(16_712_000, 0) + .saturating_add(Weight::from_parts(0, 7777)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `c` is `[1, 3145728]`. fn add_trusted_validation_code(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `946` - // Estimated: `4411` - // Minimum execution time: 80_626_000 picoseconds. - Weight::from_parts(52_721_755, 0) - .saturating_add(Weight::from_parts(0, 4411)) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_443, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `683` + // Estimated: `4148` + // Minimum execution time: 78_076_000 picoseconds. + Weight::from_parts(123_193_814, 0) + .saturating_add(Weight::from_parts(0, 4148)) + // Standard Error: 5 + .saturating_add(Weight::from_parts(1_770, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: Paras CodeByHashRefs (r:1 w:0) - /// Proof Skipped: Paras CodeByHashRefs (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras CodeByHash (r:0 w:1) - /// Proof Skipped: Paras CodeByHash (max_values: None, max_size: None, mode: Measured) + /// Storage: `Paras::CodeByHashRefs` (r:1 w:0) + /// Proof: `Paras::CodeByHashRefs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::CodeByHash` (r:0 w:1) + /// Proof: `Paras::CodeByHash` (`max_values`: None, `max_size`: None, mode: `Measured`) fn poke_unused_validation_code() -> Weight { // Proof Size summary in bytes: // Measured: `28` // Estimated: `3493` - // Minimum execution time: 6_692_000 picoseconds. - Weight::from_parts(7_009_000, 0) + // Minimum execution time: 5_184_000 picoseconds. + Weight::from_parts(5_430_000, 0) .saturating_add(Weight::from_parts(0, 3493)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) fn include_pvf_check_statement() -> Weight { // Proof Size summary in bytes: - // Measured: `26682` - // Estimated: `30147` - // Minimum execution time: 87_994_000 picoseconds. - Weight::from_parts(89_933_000, 0) - .saturating_add(Weight::from_parts(0, 30147)) + // Measured: `26706` + // Estimated: `30171` + // Minimum execution time: 102_995_000 picoseconds. + Weight::from_parts(108_977_000, 0) + .saturating_add(Weight::from_parts(0, 30171)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras UpcomingUpgrades (r:1 w:1) - /// Proof Skipped: Paras UpcomingUpgrades (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: System Digest (r:1 w:1) - /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras FutureCodeUpgrades (r:0 w:100) - /// Proof Skipped: Paras FutureCodeUpgrades (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::UpcomingUpgrades` (r:1 w:1) + /// Proof: `Paras::UpcomingUpgrades` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `System::Digest` (r:1 w:1) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::FutureCodeUpgrades` (r:0 w:100) + /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) fn include_pvf_check_statement_finalize_upgrade_accept() -> Weight { // Proof Size summary in bytes: - // Measured: `27523` - // Estimated: `30988` - // Minimum execution time: 783_222_000 picoseconds. - Weight::from_parts(794_959_000, 0) - .saturating_add(Weight::from_parts(0, 30988)) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `27360` + // Estimated: `30825` + // Minimum execution time: 709_433_000 picoseconds. + Weight::from_parts(725_074_000, 0) + .saturating_add(Weight::from_parts(0, 30825)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(104)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) fn include_pvf_check_statement_finalize_upgrade_reject() -> Weight { // Proof Size summary in bytes: - // Measured: `27214` - // Estimated: `30679` - // Minimum execution time: 87_424_000 picoseconds. - Weight::from_parts(88_737_000, 0) - .saturating_add(Weight::from_parts(0, 30679)) + // Measured: `27338` + // Estimated: `30803` + // Minimum execution time: 98_973_000 picoseconds. + Weight::from_parts(104_715_000, 0) + .saturating_add(Weight::from_parts(0, 30803)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteList (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteList (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Configuration ActiveConfig (r:1 w:0) - /// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras ActionsQueue (r:1 w:1) - /// Proof Skipped: Paras ActionsQueue (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteList` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteList` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ActionsQueue` (r:1 w:1) + /// Proof: `Paras::ActionsQueue` (`max_values`: None, `max_size`: None, mode: `Measured`) fn include_pvf_check_statement_finalize_onboarding_accept() -> Weight { // Proof Size summary in bytes: - // Measured: `26991` - // Estimated: `30456` - // Minimum execution time: 612_485_000 picoseconds. - Weight::from_parts(621_670_000, 0) - .saturating_add(Weight::from_parts(0, 30456)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `26728` + // Estimated: `30193` + // Minimum execution time: 550_958_000 picoseconds. + Weight::from_parts(564_497_000, 0) + .saturating_add(Weight::from_parts(0, 30193)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } - /// Storage: ParasShared ActiveValidatorKeys (r:1 w:0) - /// Proof Skipped: ParasShared ActiveValidatorKeys (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: ParasShared CurrentSessionIndex (r:1 w:0) - /// Proof Skipped: ParasShared CurrentSessionIndex (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Paras PvfActiveVoteMap (r:1 w:1) - /// Proof Skipped: Paras PvfActiveVoteMap (max_values: None, max_size: None, mode: Measured) + /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) + /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::PvfActiveVoteMap` (r:1 w:1) + /// Proof: `Paras::PvfActiveVoteMap` (`max_values`: None, `max_size`: None, mode: `Measured`) fn include_pvf_check_statement_finalize_onboarding_reject() -> Weight { // Proof Size summary in bytes: - // Measured: `26682` - // Estimated: `30147` - // Minimum execution time: 86_673_000 picoseconds. - Weight::from_parts(87_424_000, 0) - .saturating_add(Weight::from_parts(0, 30147)) + // Measured: `26706` + // Estimated: `30171` + // Minimum execution time: 97_088_000 picoseconds. + Weight::from_parts(103_617_000, 0) + .saturating_add(Weight::from_parts(0, 30171)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_coretime.rs b/polkadot/runtime/rococo/src/weights/runtime_common_coretime.rs new file mode 100644 index 00000000000..d068f07e759 --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/runtime_common_coretime.rs @@ -0,0 +1,86 @@ +// 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 . + +//! Autogenerated weights for `runtime_common::coretime` +//! +//! 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: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-bn-ce5rx-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 +// benchmark +// pallet +// --chain=rococo-dev +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --pallet=runtime_common::coretime +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/runtime_common_coretime.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `runtime_common::coretime`. +pub struct WeightInfo(PhantomData); +impl runtime_common::coretime::WeightInfo for WeightInfo { + /// Storage: `Configuration::PendingConfigs` (r:1 w:1) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::BypassConsistencyCheck` (r:1 w:0) + /// Proof: `Configuration::BypassConsistencyCheck` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn request_core_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `151` + // Estimated: `1636` + // Minimum execution time: 7_543_000 picoseconds. + Weight::from_parts(7_745_000, 0) + .saturating_add(Weight::from_parts(0, 1636)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreSchedules` (r:0 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreSchedules` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `s` is `[1, 100]`. + fn assign_core(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 9_367_000 picoseconds. + Weight::from_parts(9_932_305, 0) + .saturating_add(Weight::from_parts(0, 3645)) + // Standard Error: 231 + .saturating_add(Weight::from_parts(12_947, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/polkadot/runtime/test-runtime/Cargo.toml b/polkadot/runtime/test-runtime/Cargo.toml index ac379b69e3f..90a0285cd17 100644 --- a/polkadot/runtime/test-runtime/Cargo.toml +++ b/polkadot/runtime/test-runtime/Cargo.toml @@ -144,6 +144,7 @@ runtime-benchmarks = [ "pallet-staking/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 6ba8e6ad318..96104ace7d7 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -82,8 +82,8 @@ use sp_runtime::{ curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ - BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic as ExtrinsicT, OpaqueKeys, - SaturatedConversion, StaticLookup, Verify, + BlakeTwo256, Block as BlockT, ConvertInto, OpaqueKeys, SaturatedConversion, StaticLookup, + Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, @@ -167,14 +167,34 @@ impl frame_system::Config for Runtime { type MaxConsumers = frame_support::traits::ConstU32<16>; } -impl frame_system::offchain::SendTransactionTypes for Runtime +impl frame_system::offchain::CreateTransactionBase for Runtime where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; type Extrinsic = UncheckedExtrinsic; } +impl frame_system::offchain::CreateInherent for Runtime +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + UncheckedExtrinsic::new_bare(call) + } +} + +impl frame_system::offchain::CreateTransaction for Runtime +where + RuntimeCall: From, +{ + type Extension = TxExtension; + + fn create_transaction(call: Self::RuntimeCall, extension: Self::Extension) -> Self::Extrinsic { + UncheckedExtrinsic::new_transaction(call, extension) + } +} + parameter_types! { pub storage EpochDuration: u64 = EPOCH_DURATION_IN_SLOTS as u64; pub storage ExpectedBlockTime: Moment = MILLISECS_PER_BLOCK; @@ -251,6 +271,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = frame_support::weights::ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = (); } parameter_types! { @@ -395,18 +416,20 @@ impl frame_system::offchain::CreateSignedTransaction for R where RuntimeCall: From, { - fn create_transaction>( + fn create_signed_transaction< + C: frame_system::offchain::AppCrypto, + >( call: RuntimeCall, public: ::Signer, account: AccountId, nonce: ::Nonce, - ) -> Option<(RuntimeCall, ::SignaturePayload)> { + ) -> Option { let period = BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; let current_block = System::block_number().saturated_into::().saturating_sub(1); let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -418,16 +441,18 @@ where frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); - let raw_payload = SignedPayload::new(call, extra) + ) + .into(); + let raw_payload = SignedPayload::new(call, tx_ext) .map_err(|e| { log::warn!("Unable to create signed payload: {:?}", e); }) .ok()?; let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; - let (call, extra, _) = raw_payload.deconstruct(); + let (call, tx_ext, _) = raw_payload.deconstruct(); let address = Indices::unlookup(account); - Some((call, (address, signature, extra))) + let transaction = UncheckedExtrinsic::new_signed(call, address, signature, tx_ext); + Some(transaction) } } @@ -746,8 +771,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// `BlockId` type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The `SignedExtension` to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -759,7 +784,10 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; +/// Unchecked signature payload type as expected by this runtime. +pub type UncheckedSignaturePayload = + generic::UncheckedSignaturePayload; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -770,7 +798,7 @@ pub type Executive = frame_executive::Executive< AllPalletsWithSystem, >; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; pub type Hash = ::Hash; pub type Extrinsic = ::Extrinsic; diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index 31c04c26ece..fcb5719de89 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -277,6 +277,7 @@ runtime-benchmarks = [ "pallet-state-trie-migration/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 0216ccaf491..b7dae533224 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -97,8 +97,8 @@ use sp_core::{ConstU8, OpaqueMetadata, RuntimeDebug, H256}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ - AccountIdConversion, BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic as ExtrinsicT, - IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify, + AccountIdConversion, BlakeTwo256, Block as BlockT, ConvertInto, IdentityLookup, Keccak256, + OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, Percent, Permill, @@ -218,6 +218,7 @@ impl frame_system::Config for Runtime { type Version = Version; type AccountData = pallet_balances::AccountData; type SystemWeightInfo = weights::frame_system::WeightInfo; + type ExtensionsWeightInfo = weights::frame_system_extensions::WeightInfo; type SS58Prefix = SS58Prefix; type MaxConsumers = frame_support::traits::ConstU32<16>; } @@ -480,6 +481,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type WeightInfo = weights::pallet_transaction_payment::WeightInfo; } parameter_types! { @@ -853,18 +855,44 @@ impl pallet_grandpa::Config for Runtime { pallet_grandpa::EquivocationReportSystem; } +impl frame_system::offchain::SigningTypes for Runtime { + type Public = ::Signer; + type Signature = Signature; +} + +impl frame_system::offchain::CreateTransactionBase for Runtime +where + RuntimeCall: From, +{ + type RuntimeCall = RuntimeCall; + type Extrinsic = UncheckedExtrinsic; +} + +impl frame_system::offchain::CreateTransaction for Runtime +where + RuntimeCall: From, +{ + type Extension = TxExtension; + + fn create_transaction(call: RuntimeCall, extension: TxExtension) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_transaction(call, extension) + } +} + /// Submits a transaction with the node's public and signature type. Adheres to the signed extension /// format of the chain. impl frame_system::offchain::CreateSignedTransaction for Runtime where RuntimeCall: From, { - fn create_transaction>( + fn create_signed_transaction< + C: frame_system::offchain::AppCrypto, + >( call: RuntimeCall, public: ::Signer, account: AccountId, nonce: ::Nonce, - ) -> Option<(RuntimeCall, ::SignaturePayload)> { + ) -> Option { use sp_runtime::traits::StaticLookup; // take the biggest period possible. let period = @@ -876,7 +904,7 @@ where // so the actual block number is `n`. .saturating_sub(1); let tip = 0; - let extra: SignedExtra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -889,30 +917,28 @@ where frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), frame_metadata_hash_extension::CheckMetadataHash::::new(true), - ); - let raw_payload = SignedPayload::new(call, extra) + ) + .into(); + let raw_payload = SignedPayload::new(call, tx_ext) .map_err(|e| { log::warn!("Unable to create signed payload: {:?}", e); }) .ok()?; let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; - let (call, extra, _) = raw_payload.deconstruct(); + let (call, tx_ext, _) = raw_payload.deconstruct(); let address = ::Lookup::unlookup(account); - Some((call, (address, signature, extra))) + let transaction = UncheckedExtrinsic::new_signed(call, address, signature, tx_ext); + Some(transaction) } } -impl frame_system::offchain::SigningTypes for Runtime { - type Public = ::Signer; - type Signature = Signature; -} - -impl frame_system::offchain::SendTransactionTypes for Runtime +impl frame_system::offchain::CreateInherent for Runtime where - RuntimeCall: From, + RuntimeCall: From, { - type OverarchingCall = RuntimeCall; - type Extrinsic = UncheckedExtrinsic; + fn create_inherent(call: RuntimeCall) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_bare(call) + } } parameter_types! { @@ -1742,8 +1768,8 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// `BlockId` type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The `SignedExtension` to the basic transaction logic. -pub type SignedExtra = ( +/// The extension to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -1784,7 +1810,11 @@ pub mod migrations { /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; +/// Unchecked signature payload type as expected by this runtime. +pub type UncheckedSignaturePayload = + generic::UncheckedSignaturePayload; + /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -1795,7 +1825,7 @@ pub type Executive = frame_executive::Executive< Migrations, >; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; #[cfg(feature = "runtime-benchmarks")] mod benches { @@ -1844,7 +1874,9 @@ mod benches { [pallet_staking, Staking] [pallet_sudo, Sudo] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_timestamp, Timestamp] + [pallet_transaction_payment, TransactionPayment] [pallet_treasury, Treasury] [pallet_utility, Utility] [pallet_vesting, Vesting] @@ -2508,6 +2540,7 @@ sp_api::impl_runtime_apis! { use pallet_election_provider_support_benchmarking::Pallet as ElectionProviderBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; @@ -2536,6 +2569,7 @@ sp_api::impl_runtime_apis! { use pallet_election_provider_support_benchmarking::Pallet as ElectionProviderBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; impl pallet_session_benchmarking::Config for Runtime {} diff --git a/polkadot/runtime/westend/src/tests.rs b/polkadot/runtime/westend/src/tests.rs index c1b396a4cd2..02fd6b61496 100644 --- a/polkadot/runtime/westend/src/tests.rs +++ b/polkadot/runtime/westend/src/tests.rs @@ -68,7 +68,7 @@ fn sanity_check_teleport_assets_weight() { weight_limit: Unlimited, } .get_dispatch_info() - .weight; + .call_weight; assert!((weight * 50).all_lt(BlockWeights::get().max_block)); } diff --git a/polkadot/runtime/westend/src/weights/frame_system_extensions.rs b/polkadot/runtime/westend/src/weights/frame_system_extensions.rs new file mode 100644 index 00000000000..048f23fbcb9 --- /dev/null +++ b/polkadot/runtime/westend/src/weights/frame_system_extensions.rs @@ -0,0 +1,131 @@ +// 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 . + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-09-12, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/polkadot +// benchmark +// pallet +// --steps=2 +// --repeat=2 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --pallet=frame-system-extensions +// --chain=westend-dev +// --output=./polkadot/runtime/westend/src/weights/ +// --header=./polkadot/file_header.txt + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `frame_system_extensions`. +pub struct WeightInfo(PhantomData); +impl frame_system::ExtensionsWeightInfo for WeightInfo { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 75_764_000 picoseconds. + Weight::from_parts(85_402_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 118_233_000 picoseconds. + Weight::from_parts(126_539_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 118_233_000 picoseconds. + Weight::from_parts(126_539_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 7_885_000 picoseconds. + Weight::from_parts(12_784_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3593` + // Minimum execution time: 104_237_000 picoseconds. + Weight::from_parts(110_910_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_141_000 picoseconds. + Weight::from_parts(11_502_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 6_192_000 picoseconds. + Weight::from_parts(11_481_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 87_616_000 picoseconds. + Weight::from_parts(93_607_000, 0) + .saturating_add(Weight::from_parts(0, 1489)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/polkadot/runtime/westend/src/weights/mod.rs b/polkadot/runtime/westend/src/weights/mod.rs index ae11f6ccba4..8c12c1adb9c 100644 --- a/polkadot/runtime/westend/src/weights/mod.rs +++ b/polkadot/runtime/westend/src/weights/mod.rs @@ -17,6 +17,7 @@ pub mod frame_election_provider_support; pub mod frame_system; +pub mod frame_system_extensions; pub mod pallet_asset_rate; pub mod pallet_bags_list; pub mod pallet_balances; @@ -39,6 +40,7 @@ pub mod pallet_session; pub mod pallet_staking; pub mod pallet_sudo; pub mod pallet_timestamp; +pub mod pallet_transaction_payment; pub mod pallet_treasury; pub mod pallet_utility; pub mod pallet_vesting; diff --git a/polkadot/runtime/westend/src/weights/pallet_sudo.rs b/polkadot/runtime/westend/src/weights/pallet_sudo.rs index e9ab3ad37a4..649c43e031d 100644 --- a/polkadot/runtime/westend/src/weights/pallet_sudo.rs +++ b/polkadot/runtime/westend/src/weights/pallet_sudo.rs @@ -94,4 +94,15 @@ impl pallet_sudo::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn check_only_sudo_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `132` + // Estimated: `1517` + // Minimum execution time: 2_875_000 picoseconds. + Weight::from_parts(6_803_000, 0) + .saturating_add(Weight::from_parts(0, 1517)) + .saturating_add(T::DbWeight::get().reads(1)) + } } diff --git a/polkadot/runtime/westend/src/weights/pallet_transaction_payment.rs b/polkadot/runtime/westend/src/weights/pallet_transaction_payment.rs new file mode 100644 index 00000000000..71a01b6a0c2 --- /dev/null +++ b/polkadot/runtime/westend/src/weights/pallet_transaction_payment.rs @@ -0,0 +1,68 @@ +// 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 . + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-09-12, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/debug/polkadot +// benchmark +// pallet +// --steps=2 +// --repeat=2 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --pallet=pallet-transaction-payment +// --chain=westend-dev +// --output=./polkadot/runtime/westend/src/weights/ +// --header=./polkadot/file_header.txt + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transaction_payment`. +pub struct WeightInfo(PhantomData); +impl pallet_transaction_payment::WeightInfo for WeightInfo { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `320` + // Estimated: `3593` + // Minimum execution time: 569_518_000 picoseconds. + Weight::from_parts(590_438_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs index 40a7da58a68..f1ec3f604d7 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs @@ -121,7 +121,7 @@ benchmarks! { let instruction = Instruction::Transact { origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: noop_call.get_dispatch_info().weight, + 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-benchmarks/src/lib.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs index 210b8f65637..5f8482bdcb8 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs @@ -62,7 +62,7 @@ const SEED: u32 = 0; /// The XCM executor to use for doing stuff. pub type ExecutorOf = xcm_executor::XcmExecutor<::XcmConfig>; /// The overarching call type. -pub type OverArchingCallOf = ::RuntimeCall; +pub type RuntimeCallOf = ::RuntimeCall; /// The asset transactor of our executor pub type AssetTransactorOf = <::XcmConfig as XcmConfig>::AssetTransactor; /// The call type of executor's config. Should eventually resolve to the same overarching call type. diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 951fb8553d5..254bc2c7b83 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -2744,7 +2744,7 @@ impl Pallet { .invert_target(&responder) .map_err(|()| XcmError::LocationNotInvertible)?; let notify: ::RuntimeCall = notify.into(); - let max_weight = notify.get_dispatch_info().weight; + let max_weight = notify.get_dispatch_info().call_weight; let query_id = Self::new_notify_query(responder, notify, timeout, Here); let response_info = QueryResponseInfo { destination, query_id, max_weight }; let report_error = Xcm(vec![ReportError(response_info)]); @@ -3278,7 +3278,7 @@ impl OnResponse for Pallet { ::RuntimeCall::decode(&mut bytes) }) { Queries::::remove(query_id); - let weight = call.get_dispatch_info().weight; + let weight = call.get_dispatch_info().call_weight; if weight.any_gt(max_weight) { let e = Event::NotifyOverweight { query_id, diff --git a/polkadot/xcm/xcm-builder/Cargo.toml b/polkadot/xcm/xcm-builder/Cargo.toml index 671f0181277..eaa115740f3 100644 --- a/polkadot/xcm/xcm-builder/Cargo.toml +++ b/polkadot/xcm/xcm-builder/Cargo.toml @@ -49,6 +49,7 @@ runtime-benchmarks = [ "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-salary/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs index 9f42aee87c9..bec7b253977 100644 --- a/polkadot/xcm/xcm-builder/src/tests/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs @@ -100,13 +100,13 @@ impl Dispatchable for TestCall { impl GetDispatchInfo for TestCall { fn get_dispatch_info(&self) -> DispatchInfo { - let weight = *match self { + let call_weight = *match self { TestCall::OnlyRoot(estimate, ..) | TestCall::OnlyParachain(estimate, ..) | TestCall::OnlySigned(estimate, ..) | TestCall::Any(estimate, ..) => estimate, }; - DispatchInfo { weight, ..Default::default() } + DispatchInfo { call_weight, ..Default::default() } } } diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index 9f2146fa30e..d76ff21b859 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -26,13 +26,21 @@ use frame_support::{ }; use frame_system::{EnsureRoot, EnsureSigned}; use polkadot_primitives::{AccountIndex, BlakeTwo256, Signature}; -use polkadot_test_runtime::SignedExtra; use sp_runtime::{generic, traits::MaybeEquivalence, AccountId32, BuildStorage}; use xcm_executor::{traits::ConvertLocation, XcmExecutor}; +pub type TxExtension = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckMortality, + frame_system::CheckNonce, + frame_system::CheckWeight, +); pub type Address = sp_runtime::MultiAddress; pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; pub type Header = generic::Header; pub type Block = generic::Block; diff --git a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs index 020a4a54285..e95473c5407 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs +++ b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs @@ -81,7 +81,7 @@ fn transact_recursion_limit_works() { BuyExecution { fees: (Here, 1).into(), weight_limit: Unlimited }, Transact { origin_kind: OriginKind::Native, - require_weight_at_most: call.get_dispatch_info().weight, + require_weight_at_most: call.get_dispatch_info().call_weight, call: call.encode().into(), }, ]) diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 71985360b7d..e3addfa3e79 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -861,7 +861,7 @@ impl XcmExecutor { "Dispatching with origin", ); - let weight = message_call.get_dispatch_info().weight; + let weight = message_call.get_dispatch_info().call_weight; if !weight.all_lte(require_weight_at_most) { tracing::trace!( diff --git a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs index 3e284c915bf..b3afa23503e 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs @@ -60,9 +60,16 @@ construct_runtime! { } } -pub type SignedExtra = (frame_system::CheckWeight,); -pub type TestXt = sp_runtime::testing::TestXt; -type Block = sp_runtime::testing::Block; +pub type TxExtension = (frame_system::CheckWeight,); + +// we only use the hash type from this, so using the mock should be fine. +pub(crate) type Extrinsic = sp_runtime::generic::UncheckedExtrinsic< + u64, + RuntimeCall, + sp_runtime::testing::UintAuthorityId, + TxExtension, +>; +type Block = sp_runtime::testing::Block; type Balance = u128; type AssetIdForAssetsPallet = u32; type AccountId = u64; diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs index 616329a2f06..fc650ae55a7 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -44,13 +44,13 @@ use xcm_builder::{ }; use xcm_executor::{Config, XcmExecutor}; -pub type SignedExtra = (frame_system::CheckNonZeroSender,); +pub type TxExtension = (frame_system::CheckNonZeroSender,); pub type BlockNumber = u64; pub type Address = MultiAddress; pub type Header = generic::Header; pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; pub type Block = generic::Block; pub type Signature = MultiSignature; diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 459d2640b6d..58687b47852 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -45,13 +45,13 @@ use xcm_builder::{ }; use xcm_executor::{Config, XcmExecutor}; -pub type SignedExtra = (frame_system::CheckNonZeroSender,); +pub type TxExtension = (frame_system::CheckNonZeroSender,); pub type BlockNumber = u64; pub type Address = MultiAddress; pub type Header = generic::Header; pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; pub type Block = generic::Block; pub type Signature = MultiSignature; diff --git a/prdoc/pr_3685.prdoc b/prdoc/pr_3685.prdoc new file mode 100644 index 00000000000..bd414c93a6f --- /dev/null +++ b/prdoc/pr_3685.prdoc @@ -0,0 +1,300 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: FRAME Reintroduce `TransactionExtension` as a replacement for `SignedExtension` + +doc: + - audience: [Runtime Dev, Runtime User, Node Operator, Node Dev] + description: | + Introduces a new trait `TransactionExtension` to replace `SignedExtension`. Introduce the + idea of transactions which obey the runtime's extensions and have according Extension data + (né Extra data) yet do not have hard-coded signatures. + + Deprecate the terminology of "Unsigned" when used for transactions/extrinsics owing to there + now being "proper" unsigned transactions which obey the extension framework and "old-style" + unsigned which do not. Instead we have `General` for the former and `Bare` for the latter. + Unsigned will be phased out as a type of transaction, and `Bare` will only be used for + Inherents. + + Types of extrinsic are now therefore + - Bare (no hardcoded signature, no Extra data; used to be known as "Unsigned") + - Bare transactions (deprecated) - Gossiped, validated with `ValidateUnsigned` + (deprecated) and the `_bare_compat` bits of `TransactionExtension` (deprecated). + - Inherents - Not gossiped, validated with `ProvideInherent`. + - Extended (Extra data) - Gossiped, validated via `TransactionExtension`. + - Signed transactions (with a hardcoded signature). + - General transactions (without a hardcoded signature). + + Notable information on `TransactionExtension` and the differences from `SignedExtension` + - `AdditionalSigned`/`additional_signed` is renamed to `Implicit`/`implicit`. It is encoded + for the entire transaction and passed in to each extension as a new argument to validate. + - `pre_dispatch` is renamed to `prepare`. + - `validate` runs transaction validation logic both off-chain and on-chain, and is + non-mutating. + - `prepare` runs on-chain pre-execution logic using information extracted during validation + and is mutating. + - `validate` and `prepare` are now passed an `Origin` rather than an `AccountId`. If the + extension logic presumes an `AccountId`, consider using the trait function + `AsSystemOriginSigner::as_system_origin_signer`. + - A signature on the underlying transaction may validly not be present. + - The origin may be altered during validation. + - Validation functionality present in `validate` should not be repeated in `prepare`. + Useful information obtained during `validate` should now be passed in to `prepare` using + the new user-specifiable type `Val`. + - Unsigned logic should be temporarily migrated from the old `*_unsigned` functions into the + regular versions of the new functions where the `Origin` is `None`, until the deprecation + of `ValidateUnsigned` in phase 2 of Extrinsic Horizon. + - The `Call` type defining the runtime call is now a type parameter. + - Extensions now track the weight they consume during validation, preparation and + post-dispatch through the `TransactionExtensionBase::weight` function. + - `TestXt` was removed and its usage in tests was replaced with `UncheckedExtrinsic` + instances. + + To fix the build issues introduced by this change, use the `AsTransactionExtension` adapter + to wrap existing `SignedExtension`s by converting them using the `From` + generic implementation for `AsTransactionExtension`. More details on migrating existing + `SignedExtension` implementations to `TransactionExtension` in the PR description. + +crates: + - name: bridge-runtime-common + bump: major + - name: bp-bridge-hub-cumulus + bump: major + - name: bp-bridge-hub-rococo + bump: major + - name: bp-bridge-hub-westend + bump: major + - name: bp-kusama + bump: major + - name: bp-polkadot-bulletin + bump: major + - name: bp-polkadot + bump: major + - name: bp-rococo + bump: major + - name: bp-westend + bump: major + - name: pallet-bridge-relayers + bump: major + - name: bp-polkadot-core + bump: major + - name: bp-relayers + bump: major + - name: bp-runtime + bump: major + - name: substrate-relay-helper + bump: major + - name: snowbridge-pallet-ethereum-client + bump: major + - name: snowbridge-pallet-inbound-queue + bump: major + - name: snowbridge-pallet-outbound-queue + bump: major + - name: snowbridge-pallet-system + bump: major + - name: snowbridge-runtime-test-common + bump: major + - name: parachain-template-runtime + 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: parachains-runtimes-test-utils + bump: major + - name: penpal-runtime + bump: major + - name: rococo-parachain-runtime + bump: major + - name: polkadot-omni-node + bump: major + - name: polkadot-parachain-bin + bump: major + - name: cumulus-primitives-storage-weight-reclaim + bump: major + - name: cumulus-test-client + bump: major + - name: cumulus-test-runtime + bump: major + - name: cumulus-test-service + bump: major + - name: polkadot-sdk-docs + bump: major + - name: polkadot-service + bump: major + - name: polkadot-test-service + bump: major + - name: polkadot-runtime-common + bump: major + - name: polkadot-runtime-parachains + bump: major + - name: rococo-runtime + bump: major + - name: polkadot-test-runtime + bump: major + - name: westend-runtime + bump: major + - name: pallet-xcm-benchmarks + bump: major + - name: pallet-xcm + bump: major + - name: staging-xcm-builder + bump: major + - name: staging-xcm-executor + bump: major + - name: xcm-runtime-apis + bump: major + - name: xcm-simulator + bump: major + - name: minimal-template-runtime + bump: major + - name: minimal-template-node + bump: major + - name: solochain-template-runtime + bump: major + - name: staging-node-cli + bump: major + - name: kitchensink-runtime + bump: major + - name: node-testing + bump: major + - name: sc-client-api + bump: major + - name: sc-client-db + bump: major + - name: sc-network-gossip + bump: major + - name: sc-network-sync + bump: major + - name: sc-transaction-pool + bump: major + - name: polkadot-sdk-frame + bump: major + - name: pallet-alliance + bump: major + - name: pallet-assets + bump: major + - name: pallet-asset-conversion + bump: major + - name: pallet-babe + bump: major + - name: pallet-balances + bump: major + - name: pallet-beefy + bump: major + - name: pallet-collective + bump: major + - name: pallet-contracts + bump: major + - name: pallet-election-provider-multi-phase + bump: major + - name: pallet-elections-phragmen + bump: major + - name: pallet-example-basic + bump: major + - name: pallet-example-tasks + bump: major + - name: pallet-example-offchain-worker + bump: major + - name: pallet-examples + bump: major + - name: frame-executive + bump: major + - name: pallet-grandpa + bump: major + - name: pallet-im-online + bump: major + - name: pallet-lottery + bump: major + - name: frame-metadata-hash-extension + bump: major + - name: pallet-mixnet + bump: major + - name: pallet-multisig + bump: major + - name: pallet-offences + bump: major + - name: pallet-proxy + bump: major + - name: pallet-recovery + bump: major + - name: pallet-revive + bump: major + - name: pallet-sassafras + bump: major + - name: pallet-scheduler + bump: major + - name: pallet-state-trie-migration + bump: major + - name: pallet-sudo + bump: major + - name: frame-support-procedural + bump: major + - name: frame-support + bump: major + - name: frame-support-test + bump: major + - name: frame-system + bump: major + - name: frame-system-benchmarking + bump: major + - name: pallet-transaction-payment + bump: major + - name: pallet-asset-conversion-tx-payment + bump: major + - name: pallet-asset-tx-payment + bump: major + - name: pallet-skip-feeless-payment + bump: major + - name: pallet-utility + bump: major + - name: pallet-whitelist + bump: major + - name: sp-inherents + bump: major + - name: sp-metadata-ir + bump: major + - name: sp-runtime + bump: major + - name: sp-storage + bump: major + - name: sp-test-primitives + bump: major + - name: substrate-test-runtime + bump: major + - name: substrate-test-utils + bump: major + - name: frame-benchmarking-cli + bump: major + - name: frame-remote-externalities + bump: major + - name: substrate-rpc-client + bump: major + - name: minimal-template-runtime + bump: major + - name: parachain-template-runtime + bump: major + - name: solochain-template-node + bump: major + - name: polkadot-sdk + bump: major diff --git a/substrate/.maintain/frame-weight-template.hbs b/substrate/.maintain/frame-weight-template.hbs index ecd384a5145..ec9eee205ce 100644 --- a/substrate/.maintain/frame-weight-template.hbs +++ b/substrate/.maintain/frame-weight-template.hbs @@ -33,7 +33,7 @@ pub trait WeightInfo { /// Weights for `{{pallet}}` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); -{{#if (eq pallet "frame_system")}} +{{#if (or (eq pallet "frame_system") (eq pallet "frame_system_extensions"))}} impl WeightInfo for SubstrateWeight { {{else}} impl WeightInfo for SubstrateWeight { diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index de883d1051f..d8041ed400c 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -120,10 +120,9 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { } fn extrinsic_set_time(now: u64) -> OpaqueExtrinsic { - kitchensink_runtime::UncheckedExtrinsic { - signature: None, - function: kitchensink_runtime::RuntimeCall::Timestamp(pallet_timestamp::Call::set { now }), - } + kitchensink_runtime::UncheckedExtrinsic::new_bare(kitchensink_runtime::RuntimeCall::Timestamp( + pallet_timestamp::Call::set { now }, + )) .into() } diff --git a/substrate/bin/node/cli/benches/executor.rs b/substrate/bin/node/cli/benches/executor.rs index fa4da5c13d4..412b7f0ba0f 100644 --- a/substrate/bin/node/cli/benches/executor.rs +++ b/substrate/bin/node/cli/benches/executor.rs @@ -31,7 +31,7 @@ use sp_core::{ storage::well_known_keys, traits::{CallContext, CodeExecutor, RuntimeCode}, }; -use sp_runtime::traits::BlakeTwo256; +use sp_runtime::{generic::ExtrinsicFormat, traits::BlakeTwo256}; use sp_state_machine::TestExternalities as CoreTestExternalities; use staging_node_cli::service::RuntimeExecutor; @@ -146,11 +146,11 @@ fn test_blocks( ) -> Vec<(Vec, Hash)> { let mut test_ext = new_test_ext(genesis_config); let mut block1_extrinsics = vec![CheckedExtrinsic { - signed: None, + format: ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: 0 }), }]; block1_extrinsics.extend((0..20).map(|i| CheckedExtrinsic { - signed: Some((alice(), signed_extra(i, 0))), + format: ExtrinsicFormat::Signed(alice(), tx_ext(i, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 1 * DOLLARS, diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 4eb1db185e9..00658b361df 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -121,7 +121,7 @@ pub fn create_extrinsic( .map(|c| c / 2) .unwrap_or(2) as u64; let tip = 0; - let extra: kitchensink_runtime::SignedExtra = + let tx_ext: kitchensink_runtime::TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), @@ -143,7 +143,7 @@ pub fn create_extrinsic( let raw_payload = kitchensink_runtime::SignedPayload::from_raw( function.clone(), - extra.clone(), + tx_ext.clone(), ( (), kitchensink_runtime::VERSION.spec_version, @@ -162,7 +162,7 @@ pub fn create_extrinsic( function, sp_runtime::AccountId32::from(sender.public()).into(), kitchensink_runtime::Signature::Sr25519(signature), - extra, + tx_ext, ) } @@ -866,7 +866,7 @@ mod tests { use codec::Encode; use kitchensink_runtime::{ constants::{currency::CENTS, time::SLOT_DURATION}, - Address, BalancesCall, RuntimeCall, UncheckedExtrinsic, + Address, BalancesCall, RuntimeCall, TxExtension, UncheckedExtrinsic, }; use node_primitives::{Block, DigestItem, Signature}; use polkadot_sdk::{sc_transaction_pool_api::MaintainedTransactionPool, *}; @@ -1070,7 +1070,7 @@ mod tests { pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::from(0, None), ); let metadata_hash = frame_metadata_hash_extension::CheckMetadataHash::new(false); - let extra = ( + let tx_ext: TxExtension = ( check_non_zero_sender, check_spec_version, check_tx_version, @@ -1083,7 +1083,7 @@ mod tests { ); let raw_payload = SignedPayload::from_raw( function, - extra, + tx_ext, ( (), spec_version, @@ -1097,9 +1097,9 @@ mod tests { ), ); let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let (function, extra, _) = raw_payload.deconstruct(); + let (function, tx_ext, _) = raw_payload.deconstruct(); index += 1; - UncheckedExtrinsic::new_signed(function, from.into(), signature.into(), extra) + UncheckedExtrinsic::new_signed(function, from.into(), signature.into(), tx_ext) .into() }, ); diff --git a/substrate/bin/node/cli/tests/basic.rs b/substrate/bin/node/cli/tests/basic.rs index 037ddbb1e47..616d813f78e 100644 --- a/substrate/bin/node/cli/tests/basic.rs +++ b/substrate/bin/node/cli/tests/basic.rs @@ -17,11 +17,11 @@ use codec::{Decode, Encode, Joiner}; use frame_support::{ - dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo}, + dispatch::{DispatchClass, GetDispatchInfo}, traits::Currency, weights::Weight, }; -use frame_system::{self, AccountInfo, EventRecord, Phase}; +use frame_system::{self, AccountInfo, DispatchEventInfo, EventRecord, Phase}; use polkadot_sdk::*; use sp_core::{storage::well_known_keys, traits::Externalities}; use sp_runtime::{ @@ -59,17 +59,23 @@ pub fn bloaty_code_unwrap() -> &'static [u8] { /// Note that reads the multiplier from storage directly, hence to get the fee of `extrinsic` /// at block `n`, it must be called prior to executing block `n` to do the calculation with the /// correct multiplier. -fn transfer_fee(extrinsic: &E) -> Balance { - TransactionPayment::compute_fee( - extrinsic.encode().len() as u32, - &default_transfer_call().get_dispatch_info(), - 0, - ) +fn transfer_fee(extrinsic: &UncheckedExtrinsic) -> Balance { + let mut info = default_transfer_call().get_dispatch_info(); + info.extension_weight = extrinsic.extension_weight(); + TransactionPayment::compute_fee(extrinsic.encode().len() as u32, &info, 0) +} + +/// Default transfer fee, same as `transfer_fee`, but with a weight refund factored in. +fn transfer_fee_with_refund(extrinsic: &UncheckedExtrinsic, weight_refund: Weight) -> Balance { + let mut info = default_transfer_call().get_dispatch_info(); + info.extension_weight = extrinsic.extension_weight(); + let post_info = (Some(info.total_weight().saturating_sub(weight_refund)), info.pays_fee).into(); + TransactionPayment::compute_actual_fee(extrinsic.encode().len() as u32, &info, &post_info, 0) } fn xt() -> UncheckedExtrinsic { sign(CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, 0)), function: RuntimeCall::Balances(default_transfer_call()), }) } @@ -86,11 +92,11 @@ fn changes_trie_block() -> (Vec, Hash) { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time }), }, CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 69 * DOLLARS, @@ -113,11 +119,11 @@ fn blocks() -> ((Vec, Hash), (Vec, Hash)) { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time1 }), }, CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 69 * DOLLARS, @@ -133,18 +139,18 @@ fn blocks() -> ((Vec, Hash), (Vec, Hash)) { block1.1, vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time2 }), }, CheckedExtrinsic { - signed: Some((bob(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(bob(), tx_ext(0, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: alice().into(), value: 5 * DOLLARS, }), }, CheckedExtrinsic { - signed: Some((alice(), signed_extra(1, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(1, 0)), function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest: bob().into(), value: 15 * DOLLARS, @@ -168,11 +174,11 @@ fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec, Hash) { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time * 1000 }), }, CheckedExtrinsic { - signed: Some((alice(), signed_extra(nonce, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(nonce, 0)), function: RuntimeCall::System(frame_system::Call::remark { remark: vec![0; size] }), }, ], @@ -257,13 +263,14 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { let r = executor_call(&mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32))).0; assert!(r.is_ok()); - let fees = t.execute_with(|| transfer_fee(&xt())); + let weight_refund = Weight::zero(); + let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund)); let r = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt())).0; assert!(r.is_ok()); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -297,13 +304,14 @@ fn successful_execution_with_foreign_code_gives_ok() { let r = executor_call(&mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32))).0; assert!(r.is_ok()); - let fees = t.execute_with(|| transfer_fee(&xt())); + let weight_refund = Weight::zero(); + let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund)); let r = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt())).0; assert!(r.is_ok()); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -316,15 +324,18 @@ fn full_native_block_import_works() { let mut alice_last_known_balance: Balance = Default::default(); let mut fees = t.execute_with(|| transfer_fee(&xt())); + let extension_weight = xt().extension_weight(); + let weight_refund = Weight::zero(); + let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund)); - let transfer_weight = default_transfer_call().get_dispatch_info().weight.saturating_add( + let transfer_weight = default_transfer_call().get_dispatch_info().call_weight.saturating_add( ::BlockWeights::get() .get(DispatchClass::Normal) .base_extrinsic, ); let timestamp_weight = pallet_timestamp::Call::set:: { now: Default::default() } .get_dispatch_info() - .weight + .call_weight .saturating_add( ::BlockWeights::get() .get(DispatchClass::Mandatory) @@ -334,17 +345,17 @@ fn full_native_block_import_works() { executor_call(&mut t, "Core_execute_block", &block1.0).0.unwrap(); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); alice_last_known_balance = Balances::total_balance(&alice()); let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), event: RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: timestamp_weight, class: DispatchClass::Mandatory, - ..Default::default() + pays_fee: Default::default(), }, }), topics: vec![], @@ -370,21 +381,21 @@ fn full_native_block_import_works() { phase: Phase::ApplyExtrinsic(1), event: RuntimeEvent::Balances(pallet_balances::Event::Deposit { who: pallet_treasury::Pallet::::account_id(), - amount: fees * 8 / 10, + amount: fees_after_refund * 8 / 10, }), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), event: RuntimeEvent::Treasury(pallet_treasury::Event::Deposit { - value: fees * 8 / 10, + value: fees_after_refund * 8 / 10, }), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), event: RuntimeEvent::Balances(pallet_balances::Event::Rescinded { - amount: fees * 2 / 10, + amount: fees_after_refund * 2 / 10, }), topics: vec![], }, @@ -393,7 +404,7 @@ fn full_native_block_import_works() { event: RuntimeEvent::TransactionPayment( pallet_transaction_payment::Event::TransactionFeePaid { who: alice().into(), - actual_fee: fees, + actual_fee: fees_after_refund, tip: 0, }, ), @@ -402,7 +413,11 @@ fn full_native_block_import_works() { EventRecord { phase: Phase::ApplyExtrinsic(1), event: RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { - dispatch_info: DispatchInfo { weight: transfer_weight, ..Default::default() }, + dispatch_info: DispatchEventInfo { + weight: transfer_weight + .saturating_add(extension_weight.saturating_sub(weight_refund)), + ..Default::default() + }, }), topics: vec![], }, @@ -412,15 +427,18 @@ fn full_native_block_import_works() { fees = t.execute_with(|| transfer_fee(&xt())); let pot = t.execute_with(|| Treasury::pot()); + let extension_weight = xt().extension_weight(); + let weight_refund = Weight::zero(); + let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund)); executor_call(&mut t, "Core_execute_block", &block2.0).0.unwrap(); t.execute_with(|| { assert_eq!( Balances::total_balance(&alice()), - alice_last_known_balance - 10 * DOLLARS - fees, + alice_last_known_balance - 10 * DOLLARS - fees_after_refund, ); - assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - fees); + assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - fees_after_refund); let events = vec![ EventRecord { phase: Phase::Initialization, @@ -433,10 +451,10 @@ fn full_native_block_import_works() { EventRecord { phase: Phase::ApplyExtrinsic(0), event: RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: timestamp_weight, class: DispatchClass::Mandatory, - ..Default::default() + pays_fee: Default::default(), }, }), topics: vec![], @@ -462,21 +480,21 @@ fn full_native_block_import_works() { phase: Phase::ApplyExtrinsic(1), event: RuntimeEvent::Balances(pallet_balances::Event::Deposit { who: pallet_treasury::Pallet::::account_id(), - amount: fees * 8 / 10, + amount: fees_after_refund * 8 / 10, }), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), event: RuntimeEvent::Treasury(pallet_treasury::Event::Deposit { - value: fees * 8 / 10, + value: fees_after_refund * 8 / 10, }), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), event: RuntimeEvent::Balances(pallet_balances::Event::Rescinded { - amount: fees - fees * 8 / 10, + amount: fees_after_refund - fees_after_refund * 8 / 10, }), topics: vec![], }, @@ -485,7 +503,7 @@ fn full_native_block_import_works() { event: RuntimeEvent::TransactionPayment( pallet_transaction_payment::Event::TransactionFeePaid { who: bob().into(), - actual_fee: fees, + actual_fee: fees_after_refund, tip: 0, }, ), @@ -494,7 +512,11 @@ fn full_native_block_import_works() { EventRecord { phase: Phase::ApplyExtrinsic(1), event: RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { - dispatch_info: DispatchInfo { weight: transfer_weight, ..Default::default() }, + dispatch_info: DispatchEventInfo { + weight: transfer_weight + .saturating_add(extension_weight.saturating_sub(weight_refund)), + ..Default::default() + }, }), topics: vec![], }, @@ -519,21 +541,21 @@ fn full_native_block_import_works() { phase: Phase::ApplyExtrinsic(2), event: RuntimeEvent::Balances(pallet_balances::Event::Deposit { who: pallet_treasury::Pallet::::account_id(), - amount: fees * 8 / 10, + amount: fees_after_refund * 8 / 10, }), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), event: RuntimeEvent::Treasury(pallet_treasury::Event::Deposit { - value: fees * 8 / 10, + value: fees_after_refund * 8 / 10, }), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), event: RuntimeEvent::Balances(pallet_balances::Event::Rescinded { - amount: fees - fees * 8 / 10, + amount: fees_after_refund - fees_after_refund * 8 / 10, }), topics: vec![], }, @@ -542,7 +564,7 @@ fn full_native_block_import_works() { event: RuntimeEvent::TransactionPayment( pallet_transaction_payment::Event::TransactionFeePaid { who: alice().into(), - actual_fee: fees, + actual_fee: fees_after_refund, tip: 0, }, ), @@ -551,7 +573,11 @@ fn full_native_block_import_works() { EventRecord { phase: Phase::ApplyExtrinsic(2), event: RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { - dispatch_info: DispatchInfo { weight: transfer_weight, ..Default::default() }, + dispatch_info: DispatchEventInfo { + weight: transfer_weight + .saturating_add(extension_weight.saturating_sub(weight_refund)), + ..Default::default() + }, }), topics: vec![], }, @@ -567,26 +593,28 @@ fn full_wasm_block_import_works() { let (block1, block2) = blocks(); let mut alice_last_known_balance: Balance = Default::default(); - let mut fees = t.execute_with(|| transfer_fee(&xt())); + let weight_refund = Weight::zero(); + let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund)); executor_call(&mut t, "Core_execute_block", &block1.0).0.unwrap(); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); alice_last_known_balance = Balances::total_balance(&alice()); }); - fees = t.execute_with(|| transfer_fee(&xt())); + let weight_refund = Weight::zero(); + let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund)); executor_call(&mut t, "Core_execute_block", &block2.0).0.unwrap(); t.execute_with(|| { assert_eq!( Balances::total_balance(&alice()), - alice_last_known_balance - 10 * DOLLARS - fees, + alice_last_known_balance - 10 * DOLLARS - fees_after_refund, ); - assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * fees); + assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * fees_after_refund); }); } @@ -700,11 +728,11 @@ fn deploying_wasm_contract_should_work() { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time }), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(0, 0)), function: RuntimeCall::Contracts(pallet_contracts::Call::instantiate_with_code::< Runtime, > { @@ -717,7 +745,7 @@ fn deploying_wasm_contract_should_work() { }), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(1, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(1, 0)), function: RuntimeCall::Contracts(pallet_contracts::Call::call:: { dest: sp_runtime::MultiAddress::Id(addr.clone()), value: 10, @@ -828,7 +856,8 @@ fn successful_execution_gives_ok() { assert_eq!(Balances::total_balance(&alice()), 111 * DOLLARS); }); - let fees = t.execute_with(|| transfer_fee(&xt())); + let weight_refund = Weight::zero(); + let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund)); let r = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt())) .0 @@ -839,7 +868,7 @@ fn successful_execution_gives_ok() { .expect("Extrinsic failed"); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } diff --git a/substrate/bin/node/cli/tests/fees.rs b/substrate/bin/node/cli/tests/fees.rs index b49af4c1055..59ade9b8547 100644 --- a/substrate/bin/node/cli/tests/fees.rs +++ b/substrate/bin/node/cli/tests/fees.rs @@ -55,11 +55,11 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() { GENESIS_HASH.into(), vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time1 }), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(0, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(0, 0)), function: RuntimeCall::Sudo(pallet_sudo::Call::sudo { call: Box::new(RuntimeCall::RootTesting( pallet_root_testing::Call::fill_block { ratio: Perbill::from_percent(60) }, @@ -78,11 +78,11 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() { block1.1, vec![ CheckedExtrinsic { - signed: None, + format: sp_runtime::generic::ExtrinsicFormat::Bare, function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time2 }), }, CheckedExtrinsic { - signed: Some((charlie(), signed_extra(1, 0))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(1, 0)), function: RuntimeCall::System(frame_system::Call::remark { remark: vec![0; 1] }), }, ], @@ -148,7 +148,7 @@ fn transaction_fee_is_correct() { let tip = 1_000_000; let xt = sign(CheckedExtrinsic { - signed: Some((alice(), signed_extra(0, tip))), + format: sp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, tip)), function: RuntimeCall::Balances(default_transfer_call()), }); @@ -174,7 +174,9 @@ fn transaction_fee_is_correct() { let length_fee = TransactionByteFee::get() * (xt.clone().encode().len() as Balance); balance_alice -= length_fee; - let weight = default_transfer_call().get_dispatch_info().weight; + let mut info = default_transfer_call().get_dispatch_info(); + info.extension_weight = xt.extension_weight(); + let weight = info.total_weight(); let weight_fee = IdentityFee::::weight_to_fee(&weight); // we know that weight to fee multiplier is effect-less in block 1. diff --git a/substrate/bin/node/cli/tests/submit_transaction.rs b/substrate/bin/node/cli/tests/submit_transaction.rs index 18826e7e90a..3b7d82dcab1 100644 --- a/substrate/bin/node/cli/tests/submit_transaction.rs +++ b/substrate/bin/node/cli/tests/submit_transaction.rs @@ -44,10 +44,9 @@ fn should_submit_unsigned_transaction() { }; let call = pallet_im_online::Call::heartbeat { heartbeat: heartbeat_data, signature }; - SubmitTransaction::>::submit_unsigned_transaction( - call.into(), - ) - .unwrap(); + let xt = UncheckedExtrinsic::new_bare(call.into()); + SubmitTransaction::>::submit_transaction(xt) + .unwrap(); assert_eq!(state.read().transactions.len(), 1) }); @@ -131,7 +130,7 @@ fn should_submit_signed_twice_from_the_same_account() { // now check that the transaction nonces are not equal let s = state.read(); fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce { - let extra = tx.signature.unwrap().2; + let extra = tx.preamble.to_signed().unwrap().2; extra.5 } let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap()); @@ -180,7 +179,7 @@ fn should_submit_signed_twice_from_all_accounts() { // now check that the transaction nonces are not equal let s = state.read(); fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce { - let extra = tx.signature.unwrap().2; + let extra = tx.preamble.to_signed().unwrap().2; extra.5 } let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap()); @@ -237,7 +236,7 @@ fn submitted_transaction_should_be_valid() { let source = TransactionSource::External; let extrinsic = UncheckedExtrinsic::decode(&mut &*tx0).unwrap(); // add balance to the account - let author = extrinsic.signature.clone().unwrap().0; + let author = extrinsic.preamble.clone().to_signed().clone().unwrap().0; let address = Indices::lookup(author).unwrap(); let data = pallet_balances::AccountData { free: 5_000_000_000_000, ..Default::default() }; let account = frame_system::AccountInfo { providers: 1, data, ..Default::default() }; diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index ececf0d87b2..a2112e5977f 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -102,8 +102,8 @@ use sp_runtime::{ MaybeConvert, NumberFor, OpaqueKeys, SaturatedConversion, StaticLookup, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedPointNumber, FixedU128, Perbill, Percent, Permill, Perquintill, - RuntimeDebug, + ApplyExtrinsicResult, FixedPointNumber, FixedU128, MultiSignature, MultiSigner, Perbill, + Percent, Permill, Perquintill, RuntimeDebug, }; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; @@ -574,6 +574,7 @@ impl pallet_transaction_payment::Config for Runtime { MinimumMultiplier, MaximumMultiplier, >; + type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight; } impl pallet_asset_conversion_tx_payment::Config for Runtime { @@ -585,6 +586,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime { AssetConversion, ResolveAssetTo, >; + type WeightInfo = pallet_asset_conversion_tx_payment::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetConversionTxHelper; } impl pallet_skip_feeless_payment::Config for Runtime { @@ -1438,16 +1442,29 @@ parameter_types! { pub const MaxPeerInHeartbeats: u32 = 10_000; } +impl frame_system::offchain::CreateTransaction for Runtime +where + RuntimeCall: From, +{ + type Extension = TxExtension; + + fn create_transaction(call: RuntimeCall, extension: TxExtension) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_transaction(call, extension) + } +} + impl frame_system::offchain::CreateSignedTransaction for Runtime where RuntimeCall: From, { - fn create_transaction>( + fn create_signed_transaction< + C: frame_system::offchain::AppCrypto, + >( call: RuntimeCall, public: ::Signer, account: AccountId, nonce: Nonce, - ) -> Option<(RuntimeCall, ::SignaturePayload)> { + ) -> Option { let tip = 0; // take the biggest period possible. let period = @@ -1458,7 +1475,7 @@ where // so the actual block number is `n`. .saturating_sub(1); let era = Era::mortal(period, current_block); - let extra = ( + let tx_ext: TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -1473,15 +1490,26 @@ where ), frame_metadata_hash_extension::CheckMetadataHash::new(false), ); - let raw_payload = SignedPayload::new(call, extra) + + let raw_payload = SignedPayload::new(call, tx_ext) .map_err(|e| { log::warn!("Unable to create signed payload: {:?}", e); }) .ok()?; let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; let address = Indices::unlookup(account); - let (call, extra, _) = raw_payload.deconstruct(); - Some((call, (address, signature, extra))) + let (call, tx_ext, _) = raw_payload.deconstruct(); + let transaction = UncheckedExtrinsic::new_signed(call, address, signature, tx_ext); + Some(transaction) + } +} + +impl frame_system::offchain::CreateInherent for Runtime +where + RuntimeCall: From, +{ + fn create_inherent(call: RuntimeCall) -> UncheckedExtrinsic { + UncheckedExtrinsic::new_bare(call) } } @@ -1490,12 +1518,12 @@ impl frame_system::offchain::SigningTypes for Runtime { type Signature = Signature; } -impl frame_system::offchain::SendTransactionTypes for Runtime +impl frame_system::offchain::CreateTransactionBase for Runtime where RuntimeCall: From, { type Extrinsic = UncheckedExtrinsic; - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; } impl pallet_im_online::Config for Runtime { @@ -1990,6 +2018,30 @@ impl pallet_transaction_storage::Config for Runtime { ConstU32<{ pallet_transaction_storage::DEFAULT_MAX_TRANSACTION_SIZE }>; } +#[cfg(feature = "runtime-benchmarks")] +pub struct VerifySignatureBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl pallet_verify_signature::BenchmarkHelper + for VerifySignatureBenchmarkHelper +{ + fn create_signature(_entropy: &[u8], msg: &[u8]) -> (MultiSignature, AccountId) { + use sp_io::crypto::{sr25519_generate, sr25519_sign}; + use sp_runtime::traits::IdentifyAccount; + let public = sr25519_generate(0.into(), None); + let who_account: AccountId = MultiSigner::Sr25519(public).into_account().into(); + let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &public, msg).unwrap()); + (signature, who_account) + } +} + +impl pallet_verify_signature::Config for Runtime { + type Signature = MultiSignature; + type AccountIdentifier = MultiSigner; + type WeightInfo = pallet_verify_signature::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = VerifySignatureBenchmarkHelper; +} + impl pallet_whitelist::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -2530,6 +2582,9 @@ mod runtime { #[runtime::pallet_index(80)] pub type Revive = pallet_revive::Pallet; + + #[runtime::pallet_index(81)] + pub type VerifySignature = pallet_verify_signature::Pallet; } /// The address format for describing accounts. @@ -2542,12 +2597,12 @@ pub type Block = generic::Block; pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. +/// The TransactionExtension to the basic transaction logic. /// /// When you change this, you **MUST** modify [`sign`] in `bin/node/testing/src/keyring.rs`! /// /// [`sign`]: <../../testing/src/keyring.rs.html> -pub type SignedExtra = ( +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -2564,11 +2619,14 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; +/// Unchecked signature payload type as expected by this runtime. +pub type UncheckedSignaturePayload = + generic::UncheckedSignaturePayload; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; /// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, @@ -2624,6 +2682,62 @@ mod mmr { pub type Hashing = ::Hashing; } +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetConversionTxHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl + pallet_asset_conversion_tx_payment::BenchmarkHelperTrait< + AccountId, + NativeOrWithId, + NativeOrWithId, + > for AssetConversionTxHelper +{ + fn create_asset_id_parameter(seed: u32) -> (NativeOrWithId, NativeOrWithId) { + (NativeOrWithId::WithId(seed), NativeOrWithId::WithId(seed)) + } + + fn setup_balances_and_pool(asset_id: NativeOrWithId, account: AccountId) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + let NativeOrWithId::WithId(asset_idx) = asset_id.clone() else { unimplemented!() }; + assert_ok!(Assets::force_create( + RuntimeOrigin::root(), + asset_idx.into(), + account.clone().into(), /* owner */ + true, /* is_sufficient */ + 1, + )); + + let lp_provider = account.clone(); + let _ = Balances::deposit_creating(&lp_provider, ((u64::MAX as u128) * 100).into()); + assert_ok!(Assets::mint_into( + asset_idx.into(), + &lp_provider, + ((u64::MAX as u128) * 100).into() + )); + + let token_native = alloc::boxed::Box::new(NativeOrWithId::Native); + let token_second = alloc::boxed::Box::new(asset_id); + + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider.clone()), + token_native.clone(), + token_second.clone() + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(lp_provider.clone()), + token_native, + token_second, + u64::MAX.into(), // 1 desired + u64::MAX.into(), // 2 desired + 1, // 1 min + 1, // 2 min + lp_provider, + )); + } +} + #[cfg(feature = "runtime-benchmarks")] mod benches { polkadot_sdk::frame_benchmarking::define_benchmarks!( @@ -2646,6 +2760,8 @@ mod benches { [pallet_example_tasks, TasksExample] [pallet_democracy, Democracy] [pallet_asset_conversion, AssetConversion] + [pallet_asset_conversion_tx_payment, AssetConversionTxPayment] + [pallet_transaction_payment, TransactionPayment] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] [pallet_election_provider_support_benchmarking, EPSBench::] [pallet_elections_phragmen, Elections] @@ -2679,6 +2795,7 @@ mod benches { [pallet_state_trie_migration, StateTrieMigration] [pallet_sudo, Sudo] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_timestamp, Timestamp] [pallet_tips, Tips] [pallet_transaction_storage, TransactionStorage] @@ -2694,6 +2811,7 @@ mod benches { [pallet_safe_mode, SafeMode] [pallet_example_mbm, PalletExampleMbms] [pallet_asset_conversion_ops, AssetConversionMigration] + [pallet_verify_signature, VerifySignature] ); } @@ -3361,6 +3479,7 @@ impl_runtime_apis! { use pallet_offences_benchmarking::Pallet as OffencesBench; use pallet_election_provider_support_benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use baseline::Pallet as BaselineBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; @@ -3385,6 +3504,7 @@ impl_runtime_apis! { use pallet_offences_benchmarking::Pallet as OffencesBench; use pallet_election_provider_support_benchmarking::Pallet as EPSBench; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use baseline::Pallet as BaselineBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; diff --git a/substrate/bin/node/testing/src/bench.rs b/substrate/bin/node/testing/src/bench.rs index fb0504f8fad..cce1627a2ad 100644 --- a/substrate/bin/node/testing/src/bench.rs +++ b/substrate/bin/node/testing/src/bench.rs @@ -53,6 +53,7 @@ use sp_core::{ use sp_crypto_hashing::blake2_256; use sp_inherents::InherentData; use sp_runtime::{ + generic::{ExtrinsicFormat, Preamble, EXTRINSIC_FORMAT_VERSION}, traits::{Block as BlockT, IdentifyAccount, Verify}, OpaqueExtrinsic, }; @@ -298,10 +299,10 @@ impl<'a> Iterator for BlockContentIterator<'a> { let signed = self.keyring.sign( CheckedExtrinsic { - signed: Some(( + format: ExtrinsicFormat::Signed( sender, - signed_extra(0, kitchensink_runtime::ExistentialDeposit::get() + 1), - )), + tx_ext(0, kitchensink_runtime::ExistentialDeposit::get() + 1), + ), function: match self.content.block_type { BlockType::RandomTransfersKeepAlive => RuntimeCall::Balances(BalancesCall::transfer_keep_alive { @@ -565,11 +566,11 @@ impl BenchKeyring { tx_version: u32, genesis_hash: [u8; 32], ) -> UncheckedExtrinsic { - match xt.signed { - Some((signed, extra)) => { + match xt.format { + ExtrinsicFormat::Signed(signed, tx_ext) => { let payload = ( xt.function, - extra.clone(), + tx_ext.clone(), spec_version, tx_version, genesis_hash, @@ -586,11 +587,23 @@ impl BenchKeyring { } }); UncheckedExtrinsic { - signature: Some((sp_runtime::MultiAddress::Id(signed), signature, extra)), + preamble: Preamble::Signed( + sp_runtime::MultiAddress::Id(signed), + signature, + 0, + tx_ext, + ), function: payload.0, } }, - None => UncheckedExtrinsic { signature: None, function: xt.function }, + ExtrinsicFormat::Bare => UncheckedExtrinsic { + preamble: Preamble::Bare(EXTRINSIC_FORMAT_VERSION), + function: xt.function, + }, + ExtrinsicFormat::General(tx_ext) => UncheckedExtrinsic { + preamble: sp_runtime::generic::Preamble::General(0, tx_ext), + function: xt.function, + }, } } diff --git a/substrate/bin/node/testing/src/keyring.rs b/substrate/bin/node/testing/src/keyring.rs index 312fba8319b..2334cb3c4df 100644 --- a/substrate/bin/node/testing/src/keyring.rs +++ b/substrate/bin/node/testing/src/keyring.rs @@ -19,12 +19,12 @@ //! Test accounts. use codec::Encode; -use kitchensink_runtime::{CheckedExtrinsic, SessionKeys, SignedExtra, UncheckedExtrinsic}; +use kitchensink_runtime::{CheckedExtrinsic, SessionKeys, TxExtension, UncheckedExtrinsic}; use node_primitives::{AccountId, Balance, Nonce}; use sp_core::{crypto::get_public_from_string_or_panic, ecdsa, ed25519, sr25519}; use sp_crypto_hashing::blake2_256; use sp_keyring::Sr25519Keyring; -use sp_runtime::generic::Era; +use sp_runtime::generic::{Era, ExtrinsicFormat, EXTRINSIC_FORMAT_VERSION}; /// Alice's account id. pub fn alice() -> AccountId { @@ -73,7 +73,7 @@ pub fn session_keys_from_seed(seed: &str) -> SessionKeys { } /// Returns transaction extra. -pub fn signed_extra(nonce: Nonce, extra_fee: Balance) -> SignedExtra { +pub fn tx_ext(nonce: Nonce, extra_fee: Balance) -> TxExtension { ( frame_system::CheckNonZeroSender::new(), frame_system::CheckSpecVersion::new(), @@ -97,11 +97,11 @@ pub fn sign( genesis_hash: [u8; 32], metadata_hash: Option<[u8; 32]>, ) -> UncheckedExtrinsic { - match xt.signed { - Some((signed, extra)) => { + match xt.format { + ExtrinsicFormat::Signed(signed, tx_ext) => { let payload = ( xt.function, - extra.clone(), + tx_ext.clone(), spec_version, tx_version, genesis_hash, @@ -120,10 +120,22 @@ pub fn sign( }) .into(); UncheckedExtrinsic { - signature: Some((sp_runtime::MultiAddress::Id(signed), signature, extra)), + preamble: sp_runtime::generic::Preamble::Signed( + sp_runtime::MultiAddress::Id(signed), + signature, + 0, + tx_ext, + ), function: payload.0, } }, - None => UncheckedExtrinsic { signature: None, function: xt.function }, + ExtrinsicFormat::Bare => UncheckedExtrinsic { + preamble: sp_runtime::generic::Preamble::Bare(EXTRINSIC_FORMAT_VERSION), + function: xt.function, + }, + ExtrinsicFormat::General(tx_ext) => UncheckedExtrinsic { + preamble: sp_runtime::generic::Preamble::General(0, tx_ext), + function: xt.function, + }, } } diff --git a/substrate/client/api/src/notifications/tests.rs b/substrate/client/api/src/notifications/tests.rs index fba829b1cf9..9ad7973514b 100644 --- a/substrate/client/api/src/notifications/tests.rs +++ b/substrate/client/api/src/notifications/tests.rs @@ -18,7 +18,7 @@ use super::*; -use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper, H256 as Hash}; +use sp_runtime::testing::{Block as RawBlock, TestXt, H256 as Hash}; use std::iter::{empty, Empty}; type TestChangeSet = ( @@ -50,7 +50,7 @@ impl PartialEq for StorageChangeSet { } } -type Block = RawBlock>; +type Block = RawBlock>; #[test] fn triggering_change_should_notify_wildcard_listeners() { diff --git a/substrate/client/db/benches/state_access.rs b/substrate/client/db/benches/state_access.rs index e47559e710d..7ea8e708018 100644 --- a/substrate/client/db/benches/state_access.rs +++ b/substrate/client/db/benches/state_access.rs @@ -22,12 +22,12 @@ use sc_client_api::{Backend as _, BlockImportOperation, NewBlockState, StateBack use sc_client_db::{Backend, BlocksPruning, DatabaseSettings, DatabaseSource, PruningMode}; use sp_core::H256; use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, Header}, + testing::{Block as RawBlock, Header, MockCallU64, TestXt}, StateVersion, Storage, }; use tempfile::TempDir; -pub(crate) type Block = RawBlock>; +pub(crate) type Block = RawBlock>; fn insert_blocks(db: &Backend, storage: Vec<(Vec, Vec)>) -> H256 { let mut op = db.begin_operation().unwrap(); diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index 72707c306f5..aaa1398a13b 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -2567,7 +2567,7 @@ pub(crate) mod tests { use sp_blockchain::{lowest_common_ancestor, tree_route}; use sp_core::H256; use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, Header}, + testing::{Block as RawBlock, Header, MockCallU64, TestXt}, traits::{BlakeTwo256, Hash}, ConsensusEngineId, StateVersion, }; @@ -2575,7 +2575,8 @@ pub(crate) mod tests { const CONS0_ENGINE_ID: ConsensusEngineId = *b"CON0"; const CONS1_ENGINE_ID: ConsensusEngineId = *b"CON1"; - pub(crate) type Block = RawBlock>; + type UncheckedXt = TestXt; + pub(crate) type Block = RawBlock; pub fn insert_header( backend: &Backend, @@ -2594,7 +2595,7 @@ pub(crate) mod tests { parent_hash: H256, _changes: Option, Vec)>>, extrinsics_root: H256, - body: Vec>, + body: Vec, transaction_index: Option>, ) -> Result { use sp_runtime::testing::Digest; @@ -3680,7 +3681,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -3702,11 +3703,20 @@ pub(crate) mod tests { assert_eq!(None, bc.body(blocks[0]).unwrap()); assert_eq!(None, bc.body(blocks[1]).unwrap()); assert_eq!(None, bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(3.into(), ())]), + bc.body(blocks[3]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); } else { for i in 0..5 { - assert_eq!(Some(vec![(i as u64).into()]), bc.body(blocks[i]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction((i as u64).into(), ())]), + bc.body(blocks[i]).unwrap() + ); } } } @@ -3730,7 +3740,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -3739,16 +3749,26 @@ pub(crate) mod tests { } // insert a fork at block 2 - let fork_hash_root = - insert_block(&backend, 2, blocks[1], None, H256::random(), vec![2.into()], None) - .unwrap(); + let fork_hash_root = insert_block( + &backend, + 2, + blocks[1], + None, + H256::random(), + vec![UncheckedXt::new_transaction(2.into(), ())], + None, + ) + .unwrap(); insert_block( &backend, 3, fork_hash_root, None, H256::random(), - vec![3.into(), 11.into()], + vec![ + UncheckedXt::new_transaction(3.into(), ()), + UncheckedXt::new_transaction(11.into(), ()), + ], None, ) .unwrap(); @@ -3758,7 +3778,10 @@ pub(crate) mod tests { backend.commit_operation(op).unwrap(); let bc = backend.blockchain(); - assert_eq!(Some(vec![2.into()]), bc.body(fork_hash_root).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(2.into(), ())]), + bc.body(fork_hash_root).unwrap() + ); for i in 1..5 { let mut op = backend.begin_operation().unwrap(); @@ -3772,16 +3795,28 @@ pub(crate) mod tests { assert_eq!(None, bc.body(blocks[1]).unwrap()); assert_eq!(None, bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(3.into(), ())]), + bc.body(blocks[3]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); } else { for i in 0..5 { - assert_eq!(Some(vec![(i as u64).into()]), bc.body(blocks[i]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction((i as u64).into(), ())]), + bc.body(blocks[i]).unwrap() + ); } } if matches!(pruning, BlocksPruning::KeepAll) { - assert_eq!(Some(vec![2.into()]), bc.body(fork_hash_root).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(2.into(), ())]), + bc.body(fork_hash_root).unwrap() + ); } else { assert_eq!(None, bc.body(fork_hash_root).unwrap()); } @@ -3802,8 +3837,16 @@ pub(crate) mod tests { let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(10), 10); let make_block = |index, parent, val: u64| { - insert_block(&backend, index, parent, None, H256::random(), vec![val.into()], None) - .unwrap() + insert_block( + &backend, + index, + parent, + None, + H256::random(), + vec![UncheckedXt::new_transaction(val.into(), ())], + None, + ) + .unwrap() }; let block_0 = make_block(0, Default::default(), 0x00); @@ -3831,18 +3874,30 @@ pub(crate) mod tests { let bc = backend.blockchain(); assert_eq!(None, bc.body(block_1b).unwrap()); assert_eq!(None, bc.body(block_2b).unwrap()); - assert_eq!(Some(vec![0x00.into()]), bc.body(block_0).unwrap()); - assert_eq!(Some(vec![0x1a.into()]), bc.body(block_1a).unwrap()); - assert_eq!(Some(vec![0x2a.into()]), bc.body(block_2a).unwrap()); - assert_eq!(Some(vec![0x3a.into()]), bc.body(block_3a).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0x00.into(), ())]), + bc.body(block_0).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0x1a.into(), ())]), + bc.body(block_1a).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0x2a.into(), ())]), + bc.body(block_2a).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0x3a.into(), ())]), + bc.body(block_3a).unwrap() + ); } #[test] fn indexed_data_block_body() { let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); - let x0 = ExtrinsicWrapper::from(0u64).encode(); - let x1 = ExtrinsicWrapper::from(1u64).encode(); + let x0 = UncheckedXt::new_transaction(0.into(), ()).encode(); + let x1 = UncheckedXt::new_transaction(1.into(), ()).encode(); let x0_hash = as sp_core::Hasher>::hash(&x0[1..]); let x1_hash = as sp_core::Hasher>::hash(&x1[1..]); let index = vec![ @@ -3863,7 +3918,10 @@ pub(crate) mod tests { Default::default(), None, Default::default(), - vec![0u64.into(), 1u64.into()], + vec![ + UncheckedXt::new_transaction(0.into(), ()), + UncheckedXt::new_transaction(1.into(), ()), + ], Some(index), ) .unwrap(); @@ -3885,8 +3943,9 @@ pub(crate) mod tests { fn index_invalid_size() { let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); - let x0 = ExtrinsicWrapper::from(0u64).encode(); - let x1 = ExtrinsicWrapper::from(1u64).encode(); + let x0 = UncheckedXt::new_transaction(0.into(), ()).encode(); + let x1 = UncheckedXt::new_transaction(1.into(), ()).encode(); + let x0_hash = as sp_core::Hasher>::hash(&x0[..]); let x1_hash = as sp_core::Hasher>::hash(&x1[..]); let index = vec![ @@ -3907,7 +3966,10 @@ pub(crate) mod tests { Default::default(), None, Default::default(), - vec![0u64.into(), 1u64.into()], + vec![ + UncheckedXt::new_transaction(0.into(), ()), + UncheckedXt::new_transaction(1.into(), ()), + ], Some(index), ) .unwrap(); @@ -3921,7 +3983,7 @@ pub(crate) mod tests { let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 10); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); - let x1 = ExtrinsicWrapper::from(0u64).encode(); + let x1 = UncheckedXt::new_transaction(0.into(), ()).encode(); let x1_hash = as sp_core::Hasher>::hash(&x1[1..]); for i in 0..10 { let mut index = Vec::new(); @@ -3941,7 +4003,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], Some(index), ) .unwrap(); @@ -3975,7 +4037,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -3990,7 +4052,7 @@ pub(crate) mod tests { blocks[1], None, sp_core::H256::random(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -4004,7 +4066,7 @@ pub(crate) mod tests { blocks[0], None, sp_core::H256::random(), - vec![42.into()], + vec![UncheckedXt::new_transaction(42.into(), ())], None, ) .unwrap(); @@ -4478,7 +4540,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -4493,7 +4555,10 @@ pub(crate) mod tests { // Check that we can properly access values when there is reference count // but no value. - assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(1.into(), ())]), + bc.body(blocks[1]).unwrap() + ); // Block 1 gets pinned three times backend.pin_block(blocks[1]).unwrap(); @@ -4510,27 +4575,42 @@ pub(crate) mod tests { // Block 0, 1, 2, 3 are pinned, so all values should be cached. // Block 4 is inside the pruning window, its value is in db. - assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0.into(), ())]), + bc.body(blocks[0]).unwrap() + ); - assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(1.into(), ())]), + bc.body(blocks[1]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(1))), bc.justifications(blocks[1]).unwrap() ); - assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(2.into(), ())]), + bc.body(blocks[2]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(2))), bc.justifications(blocks[2]).unwrap() ); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(3.into(), ())]), + bc.body(blocks[3]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(3))), bc.justifications(blocks[3]).unwrap() ); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(4))), bc.justifications(blocks[4]).unwrap() @@ -4561,7 +4641,10 @@ pub(crate) mod tests { assert!(bc.justifications(blocks[1]).unwrap().is_none()); // Block 4 is inside the pruning window and still kept - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(4))), bc.justifications(blocks[4]).unwrap() @@ -4569,9 +4652,16 @@ pub(crate) mod tests { // Block tree: // 0 -> 1 -> 2 -> 3 -> 4 -> 5 - let hash = - insert_block(&backend, 5, prev_hash, None, Default::default(), vec![5.into()], None) - .unwrap(); + let hash = insert_block( + &backend, + 5, + prev_hash, + None, + Default::default(), + vec![UncheckedXt::new_transaction(5.into(), ())], + None, + ) + .unwrap(); blocks.push(hash); backend.pin_block(blocks[4]).unwrap(); @@ -4586,12 +4676,18 @@ pub(crate) mod tests { assert!(bc.body(blocks[2]).unwrap().is_none()); assert!(bc.body(blocks[3]).unwrap().is_none()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); assert_eq!( Some(Justifications::from(build_justification(4))), bc.justifications(blocks[4]).unwrap() ); - assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(5.into(), ())]), + bc.body(blocks[5]).unwrap() + ); assert!(bc.header(blocks[5]).ok().flatten().is_some()); backend.unpin_block(blocks[4]); @@ -4601,9 +4697,16 @@ pub(crate) mod tests { // Append a justification to block 5. backend.append_justification(blocks[5], ([0, 0, 0, 1], vec![42])).unwrap(); - let hash = - insert_block(&backend, 6, blocks[5], None, Default::default(), vec![6.into()], None) - .unwrap(); + let hash = insert_block( + &backend, + 6, + blocks[5], + None, + Default::default(), + vec![UncheckedXt::new_transaction(6.into(), ())], + None, + ) + .unwrap(); blocks.push(hash); // Pin block 5 so it gets loaded into the cache on prune @@ -4616,7 +4719,10 @@ pub(crate) mod tests { op.mark_finalized(blocks[6], None).unwrap(); backend.commit_operation(op).unwrap(); - assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(5.into(), ())]), + bc.body(blocks[5]).unwrap() + ); assert!(bc.header(blocks[5]).ok().flatten().is_some()); let mut expected = Justifications::from(build_justification(5)); expected.append(([0, 0, 0, 1], vec![42])); @@ -4638,7 +4744,7 @@ pub(crate) mod tests { prev_hash, None, Default::default(), - vec![i.into()], + vec![UncheckedXt::new_transaction(i.into(), ())], None, ) .unwrap(); @@ -4654,16 +4760,26 @@ pub(crate) mod tests { // Block tree: // 0 -> 1 -> 2 -> 3 -> 4 // \ -> 2 -> 3 - let fork_hash_root = - insert_block(&backend, 2, blocks[1], None, H256::random(), vec![2.into()], None) - .unwrap(); + let fork_hash_root = insert_block( + &backend, + 2, + blocks[1], + None, + H256::random(), + vec![UncheckedXt::new_transaction(2.into(), ())], + None, + ) + .unwrap(); let fork_hash_3 = insert_block( &backend, 3, fork_hash_root, None, H256::random(), - vec![3.into(), 11.into()], + vec![ + UncheckedXt::new_transaction(3.into(), ()), + UncheckedXt::new_transaction(11.into(), ()), + ], None, ) .unwrap(); @@ -4684,14 +4800,35 @@ pub(crate) mod tests { } let bc = backend.blockchain(); - assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); - assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); - assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(0.into(), ())]), + bc.body(blocks[0]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(1.into(), ())]), + bc.body(blocks[1]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(2.into(), ())]), + bc.body(blocks[2]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(3.into(), ())]), + bc.body(blocks[3]).unwrap() + ); + assert_eq!( + Some(vec![UncheckedXt::new_transaction(4.into(), ())]), + bc.body(blocks[4]).unwrap() + ); // Check the fork hashes. assert_eq!(None, bc.body(fork_hash_root).unwrap()); - assert_eq!(Some(vec![3.into(), 11.into()]), bc.body(fork_hash_3).unwrap()); + assert_eq!( + Some(vec![ + UncheckedXt::new_transaction(3.into(), ()), + UncheckedXt::new_transaction(11.into(), ()) + ]), + bc.body(fork_hash_3).unwrap() + ); // Unpin all blocks, except the forked one. for block in &blocks { diff --git a/substrate/client/db/src/utils.rs b/substrate/client/db/src/utils.rs index 0b591c967e6..a79f5ab3ac7 100644 --- a/substrate/client/db/src/utils.rs +++ b/substrate/client/db/src/utils.rs @@ -613,14 +613,16 @@ impl<'a, 'b> codec::Input for JoinInput<'a, 'b> { mod tests { use super::*; use codec::Input; - use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper}; - type Block = RawBlock>; + use sp_runtime::testing::{Block as RawBlock, MockCallU64, TestXt}; + + pub type UncheckedXt = TestXt; + type Block = RawBlock; #[cfg(feature = "rocksdb")] #[test] fn database_type_subdir_migration() { use std::path::PathBuf; - type Block = RawBlock>; + type Block = RawBlock; fn check_dir_for_db_type( db_type: DatabaseType, diff --git a/substrate/client/network-gossip/src/state_machine.rs b/substrate/client/network-gossip/src/state_machine.rs index ac3f7a1b8c7..7649c8cc637 100644 --- a/substrate/client/network-gossip/src/state_machine.rs +++ b/substrate/client/network-gossip/src/state_machine.rs @@ -549,7 +549,7 @@ mod tests { }; use sc_network_types::multiaddr::Multiaddr; use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, H256}, + testing::{Block as RawBlock, MockCallU64, TestXt, H256}, traits::NumberFor, }; use std::{ @@ -558,7 +558,7 @@ mod tests { sync::{Arc, Mutex}, }; - type Block = RawBlock>; + type Block = RawBlock>; macro_rules! push_msg { ($consensus:expr, $topic:expr, $hash: expr, $m:expr) => { diff --git a/substrate/client/network/sync/src/blocks.rs b/substrate/client/network/sync/src/blocks.rs index af88c5245dc..eedba18bebe 100644 --- a/substrate/client/network/sync/src/blocks.rs +++ b/substrate/client/network/sync/src/blocks.rs @@ -265,9 +265,9 @@ mod test { use sc_network_common::sync::message; use sc_network_types::PeerId; use sp_core::H256; - use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper}; + use sp_runtime::testing::{Block as RawBlock, MockCallU64, TestXt}; - type Block = RawBlock>; + type Block = RawBlock>; fn is_empty(bc: &BlockCollection) -> bool { bc.blocks.is_empty() && bc.peer_requests.is_empty() diff --git a/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs b/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs index 404225167e5..11e30bef7ea 100644 --- a/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs +++ b/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs @@ -53,7 +53,7 @@ use sp_blockchain::{HashAndNumber, TreeRoute}; use sp_core::traits::SpawnEssentialNamed; use sp_runtime::{ generic::BlockId, - traits::{Block as BlockT, Extrinsic, NumberFor}, + traits::{Block as BlockT, NumberFor}, }; use std::{ collections::{HashMap, HashSet}, @@ -1194,8 +1194,7 @@ where None }) .unwrap_or_default() - .into_iter() - .filter(|tx| tx.is_signed().unwrap_or(true)); + .into_iter(); let mut resubmitted_to_report = 0; diff --git a/substrate/client/transaction-pool/src/single_state_txpool/single_state_txpool.rs b/substrate/client/transaction-pool/src/single_state_txpool/single_state_txpool.rs index 6b4feca44bf..0826b95cf07 100644 --- a/substrate/client/transaction-pool/src/single_state_txpool/single_state_txpool.rs +++ b/substrate/client/transaction-pool/src/single_state_txpool/single_state_txpool.rs @@ -46,7 +46,7 @@ use sp_blockchain::{HashAndNumber, TreeRoute}; use sp_core::traits::SpawnEssentialNamed; use sp_runtime::{ generic::BlockId, - traits::{AtLeast32Bit, Block as BlockT, Extrinsic, Header as HeaderT, NumberFor, Zero}, + traits::{AtLeast32Bit, Block as BlockT, Header as HeaderT, NumberFor, Zero}, }; use std::{ collections::{HashMap, HashSet}, @@ -675,8 +675,7 @@ where None }) .unwrap_or_default() - .into_iter() - .filter(|tx| tx.is_signed().unwrap_or(true)); + .into_iter(); let mut resubmitted_to_report = 0; diff --git a/substrate/frame/Cargo.toml b/substrate/frame/Cargo.toml index 41ece6c9a27..595fb5a19b0 100644 --- a/substrate/frame/Cargo.toml +++ b/substrate/frame/Cargo.toml @@ -67,6 +67,8 @@ pallet-examples = { workspace = true } default = ["runtime", "std"] experimental = ["frame-support/experimental"] runtime = [ + "frame-executive", + "frame-system-rpc-runtime-api", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -77,9 +79,6 @@ runtime = [ "sp-storage", "sp-transaction-pool", "sp-version", - - "frame-executive", - "frame-system-rpc-runtime-api", ] std = [ "codec/std", diff --git a/substrate/frame/alliance/src/tests.rs b/substrate/frame/alliance/src/tests.rs index ec31ebf6a47..2397ebfe7db 100644 --- a/substrate/frame/alliance/src/tests.rs +++ b/substrate/frame/alliance/src/tests.rs @@ -244,7 +244,7 @@ fn vote_works() { fn close_works() { new_test_ext().execute_with(|| { let (proposal, proposal_len, hash) = make_remark_proposal(42); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; assert_ok!(Alliance::propose( RuntimeOrigin::signed(1), 3, @@ -645,8 +645,8 @@ fn remove_unscrupulous_items_works() { #[test] fn weights_sane() { let info = crate::Call::::join_alliance {}.get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::join_alliance(), info.weight); + assert_eq!(<() as crate::WeightInfo>::join_alliance(), info.call_weight); let info = crate::Call::::nominate_ally { who: 10 }.get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::nominate_ally(), info.weight); + assert_eq!(<() as crate::WeightInfo>::nominate_ally(), info.call_weight); } diff --git a/substrate/frame/asset-conversion/src/weights.rs b/substrate/frame/asset-conversion/src/weights.rs index 9aea19dbf57..f6e025520d7 100644 --- a/substrate/frame/asset-conversion/src/weights.rs +++ b/substrate/frame/asset-conversion/src/weights.rs @@ -37,6 +37,7 @@ // --chain=dev // --header=./substrate/HEADER-APACHE2 // --output=./substrate/frame/asset-conversion/src/weights.rs +// --header=./substrate/HEADER-APACHE2 // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index af605c5a3c6..75a6139702c 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1785,10 +1785,10 @@ fn multiple_transfer_alls_work_ok() { #[test] fn weights_sane() { let info = crate::Call::::create { id: 10, admin: 4, min_balance: 3 }.get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::create(), info.weight); + assert_eq!(<() as crate::WeightInfo>::create(), info.call_weight); let info = crate::Call::::finish_destroy { id: 10 }.get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::finish_destroy(), info.weight); + assert_eq!(<() as crate::WeightInfo>::finish_destroy(), info.call_weight); } #[test] diff --git a/substrate/frame/babe/src/equivocation.rs b/substrate/frame/babe/src/equivocation.rs index 4be07bdae1f..524ad23e58e 100644 --- a/substrate/frame/babe/src/equivocation.rs +++ b/substrate/frame/babe/src/equivocation.rs @@ -100,7 +100,7 @@ impl Offence for EquivocationOffence { /// /// This type implements `OffenceReportSystem` such that: /// - Equivocation reports are published on-chain as unsigned extrinsic via -/// `offchain::SendTransactionTypes`. +/// `offchain::CreateTransactionBase`. /// - On-chain validity checks and processing are mostly delegated to the user provided generic /// types implementing `KeyOwnerProofSystem` and `ReportOffence` traits. /// - Offence reporter for unsigned transactions is fetched via the the authorship pallet. @@ -110,7 +110,7 @@ impl OffenceReportSystem, (EquivocationProof>, T::KeyOwnerProof)> for EquivocationReportSystem where - T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, + T: Config + pallet_authorship::Config + frame_system::offchain::CreateInherent>, R: ReportOffence< T::AccountId, P::IdentificationTuple, @@ -132,7 +132,8 @@ where equivocation_proof: Box::new(equivocation_proof), key_owner_proof, }; - let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + let xt = T::create_inherent(call.into()); + let res = SubmitTransaction::>::submit_transaction(xt); match res { Ok(_) => info!(target: LOG_TARGET, "Submitted equivocation report"), Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index d416e31b25f..c2e24c73a7b 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -68,14 +68,23 @@ impl frame_system::Config for Test { type AccountData = pallet_balances::AccountData; } -impl frame_system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; type Extrinsic = TestXt; } +impl frame_system::offchain::CreateInherent for Test +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + TestXt::new_bare(call) + } +} + impl_opaque_keys! { pub struct MockSessionKeys { pub babe_authority: super::Pallet, diff --git a/substrate/frame/babe/src/tests.rs b/substrate/frame/babe/src/tests.rs index b9a214ca105..eca95816023 100644 --- a/substrate/frame/babe/src/tests.rs +++ b/substrate/frame/babe/src/tests.rs @@ -906,7 +906,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // it should have non-zero weight and the fee has to be paid. // TODO: account for proof size weight - assert!(info.weight.ref_time() > 0); + assert!(info.call_weight.ref_time() > 0); assert_eq!(info.pays_fee, Pays::Yes); // report the equivocation. diff --git a/substrate/frame/balances/Cargo.toml b/substrate/frame/balances/Cargo.toml index 44899e5b7d8..f0117555c37 100644 --- a/substrate/frame/balances/Cargo.toml +++ b/substrate/frame/balances/Cargo.toml @@ -52,6 +52,7 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ diff --git a/substrate/frame/balances/src/tests/currency_tests.rs b/substrate/frame/balances/src/tests/currency_tests.rs index a4984b34f6d..7fcc49d50aa 100644 --- a/substrate/frame/balances/src/tests/currency_tests.rs +++ b/substrate/frame/balances/src/tests/currency_tests.rs @@ -30,6 +30,7 @@ use frame_support::{ StorageNoopGuard, }; use frame_system::Event as SysEvent; +use sp_runtime::traits::DispatchTransaction; const ID_1: LockIdentifier = *b"1 "; const ID_2: LockIdentifier = *b"2 "; @@ -258,17 +259,17 @@ fn lock_should_work_reserve() { TokenError::Frozen ); assert_noop!(Balances::reserve(&1, 1), Error::::LiquidityRestrictions,); - assert!( as SignedExtension>::pre_dispatch( + assert!(ChargeTransactionPayment::::validate_and_prepare( ChargeTransactionPayment::from(1), - &1, + Some(1).into(), CALL, &info_from_weight(Weight::from_parts(1, 0)), 1, ) .is_err()); - assert!( as SignedExtension>::pre_dispatch( + assert!(ChargeTransactionPayment::::validate_and_prepare( ChargeTransactionPayment::from(0), - &1, + Some(1).into(), CALL, &info_from_weight(Weight::from_parts(1, 0)), 1, @@ -289,17 +290,17 @@ fn lock_should_work_tx_fee() { TokenError::Frozen ); assert_noop!(Balances::reserve(&1, 1), Error::::LiquidityRestrictions,); - assert!( as SignedExtension>::pre_dispatch( + assert!(ChargeTransactionPayment::::validate_and_prepare( ChargeTransactionPayment::from(1), - &1, + Some(1).into(), CALL, &info_from_weight(Weight::from_parts(1, 0)), 1, ) .is_err()); - assert!( as SignedExtension>::pre_dispatch( + assert!(ChargeTransactionPayment::::validate_and_prepare( ChargeTransactionPayment::from(0), - &1, + Some(1).into(), CALL, &info_from_weight(Weight::from_parts(1, 0)), 1, diff --git a/substrate/frame/balances/src/tests/mod.rs b/substrate/frame/balances/src/tests/mod.rs index ba0cdabdabb..bf49ad9f0a1 100644 --- a/substrate/frame/balances/src/tests/mod.rs +++ b/substrate/frame/balances/src/tests/mod.rs @@ -37,7 +37,7 @@ use scale_info::TypeInfo; use sp_core::hexdisplay::HexDisplay; use sp_io; use sp_runtime::{ - traits::{BadOrigin, SignedExtension, Zero}, + traits::{BadOrigin, Zero}, ArithmeticError, BuildStorage, DispatchError, DispatchResult, FixedPointNumber, RuntimeDebug, TokenError, }; @@ -104,7 +104,6 @@ impl pallet_transaction_payment::Config for Test { type OperationalFeeMultiplier = ConstU8<5>; type WeightToFee = IdentityFee; type LengthToFee = IdentityFee; - type FeeMultiplierUpdate = (); } parameter_types! { @@ -275,7 +274,7 @@ pub fn events() -> Vec { /// create a transaction info struct from weight. Handy to avoid building the whole struct. pub fn info_from_weight(w: Weight) -> DispatchInfo { - DispatchInfo { weight: w, ..Default::default() } + DispatchInfo { call_weight: w, ..Default::default() } } /// Check that the total-issuance matches the sum of all accounts' total balances. @@ -298,10 +297,10 @@ pub fn ensure_ti_valid() { #[test] fn weights_sane() { let info = crate::Call::::transfer_allow_death { dest: 10, value: 4 }.get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::transfer_allow_death(), info.weight); + assert_eq!(<() as crate::WeightInfo>::transfer_allow_death(), info.call_weight); let info = crate::Call::::force_unreserve { who: 10, amount: 4 }.get_dispatch_info(); - assert_eq!(<() as crate::WeightInfo>::force_unreserve(), info.weight); + assert_eq!(<() as crate::WeightInfo>::force_unreserve(), info.call_weight); } #[test] diff --git a/substrate/frame/beefy/src/equivocation.rs b/substrate/frame/beefy/src/equivocation.rs index 15345e6ae19..3a49b9e169c 100644 --- a/substrate/frame/beefy/src/equivocation.rs +++ b/substrate/frame/beefy/src/equivocation.rs @@ -118,7 +118,7 @@ where /// /// This type implements `OffenceReportSystem` such that: /// - Equivocation reports are published on-chain as unsigned extrinsic via -/// `offchain::SendTransactionTypes`. +/// `offchain::CreateTransactionBase`. /// - On-chain validity checks and processing are mostly delegated to the user provided generic /// types implementing `KeyOwnerProofSystem` and `ReportOffence` traits. /// - Offence reporter for unsigned transactions is fetched via the authorship pallet. @@ -262,7 +262,7 @@ impl EquivocationEvidenceFor { impl OffenceReportSystem, EquivocationEvidenceFor> for EquivocationReportSystem where - T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, + T: Config + pallet_authorship::Config + frame_system::offchain::CreateInherent>, R: ReportOffence< T::AccountId, P::IdentificationTuple, @@ -278,7 +278,8 @@ where use frame_system::offchain::SubmitTransaction; let call: Call = evidence.into(); - let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + let xt = T::create_inherent(call.into()); + let res = SubmitTransaction::>::submit_transaction(xt); match res { Ok(_) => info!(target: LOG_TARGET, "Submitted equivocation report."), Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index 5c79d8f7d7d..2b75c410741 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -75,14 +75,23 @@ impl frame_system::Config for Test { type AccountData = pallet_balances::AccountData; } -impl frame_system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; type Extrinsic = TestXt; } +impl frame_system::offchain::CreateInherent for Test +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + TestXt::new_bare(call) + } +} + #[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] pub struct MockAncestryProofContext { pub is_valid: bool, diff --git a/substrate/frame/collective/src/lib.rs b/substrate/frame/collective/src/lib.rs index 79428689caa..8e533a7b290 100644 --- a/substrate/frame/collective/src/lib.rs +++ b/substrate/frame/collective/src/lib.rs @@ -629,7 +629,7 @@ pub mod pallet { T::WeightInfo::execute( *length_bound, // B T::MaxMembers::get(), // M - ).saturating_add(proposal.get_dispatch_info().weight), // P + ).saturating_add(proposal.get_dispatch_info().call_weight), // P DispatchClass::Operational ))] pub fn execute( @@ -681,7 +681,7 @@ pub mod pallet { T::WeightInfo::propose_execute( *length_bound, // B T::MaxMembers::get(), // M - ).saturating_add(proposal.get_dispatch_info().weight) // P1 + ).saturating_add(proposal.get_dispatch_info().call_weight) // P1 } else { T::WeightInfo::propose_proposed( *length_bound, // B @@ -915,7 +915,7 @@ impl, I: 'static> Pallet { ) -> Result<(u32, DispatchResultWithPostInfo), DispatchError> { let proposal_len = proposal.encoded_size(); ensure!(proposal_len <= length_bound as usize, Error::::WrongProposalLength); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; ensure!( proposal_weight.all_lte(T::MaxProposalWeight::get()), Error::::WrongProposalWeight @@ -942,7 +942,7 @@ impl, I: 'static> Pallet { ) -> Result<(u32, u32), DispatchError> { let proposal_len = proposal.encoded_size(); ensure!(proposal_len <= length_bound as usize, Error::::WrongProposalLength); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; ensure!( proposal_weight.all_lte(T::MaxProposalWeight::get()), Error::::WrongProposalWeight @@ -1130,7 +1130,7 @@ impl, I: 'static> Pallet { storage::read(&key, &mut [0; 0], 0).ok_or(Error::::ProposalMissing)?; ensure!(proposal_len <= length_bound, Error::::WrongProposalLength); let proposal = ProposalOf::::get(hash).ok_or(Error::::ProposalMissing)?; - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; ensure!(proposal_weight.all_lte(weight_bound), Error::::WrongProposalWeight); Ok((proposal, proposal_len as usize)) } @@ -1157,7 +1157,7 @@ impl, I: 'static> Pallet { ) -> (Weight, u32) { Self::deposit_event(Event::Approved { proposal_hash }); - let dispatch_weight = proposal.get_dispatch_info().weight; + let dispatch_weight = proposal.get_dispatch_info().call_weight; let origin = RawOrigin::Members(yes_votes, seats).into(); let result = proposal.dispatch(origin); Self::deposit_event(Event::Executed { diff --git a/substrate/frame/collective/src/tests.rs b/substrate/frame/collective/src/tests.rs index 70ce221f10d..c4ed17821ae 100644 --- a/substrate/frame/collective/src/tests.rs +++ b/substrate/frame/collective/src/tests.rs @@ -36,7 +36,7 @@ use sp_runtime::{ }; pub type Block = sp_runtime::generic::Block; -pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( pub enum Test @@ -316,7 +316,7 @@ fn close_works() { ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash = BlakeTwo256::hash_of(&proposal); assert_ok!(Collective::propose( @@ -388,7 +388,7 @@ fn proposal_weight_limit_works_on_approve() { old_count: MaxMembers::get(), }); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash = BlakeTwo256::hash_of(&proposal); // Set 1 as prime voter Prime::::set(Some(1)); @@ -430,7 +430,7 @@ fn proposal_weight_limit_ignored_on_disapprove() { old_count: MaxMembers::get(), }); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash = BlakeTwo256::hash_of(&proposal); assert_ok!(Collective::propose( @@ -456,7 +456,7 @@ fn close_with_prime_works() { ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash = BlakeTwo256::hash_of(&proposal); assert_ok!(Collective::set_members( RuntimeOrigin::root(), @@ -524,7 +524,7 @@ fn close_with_voting_prime_works() { ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash = BlakeTwo256::hash_of(&proposal); assert_ok!(Collective::set_members( RuntimeOrigin::root(), @@ -594,7 +594,7 @@ fn close_with_no_prime_but_majority_works() { ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash = BlakeTwo256::hash_of(&proposal); assert_ok!(CollectiveMajority::set_members( RuntimeOrigin::root(), @@ -874,7 +874,7 @@ fn correct_validate_and_get_proposal() { )); let hash = BlakeTwo256::hash_of(&proposal); - let weight = proposal.get_dispatch_info().weight; + let weight = proposal.get_dispatch_info().call_weight; assert_noop!( Collective::validate_and_get_proposal( &BlakeTwo256::hash_of(&vec![3; 4]), @@ -1073,7 +1073,7 @@ fn motions_all_first_vote_free_works() { // Test close() Extrinsics | Check DispatchResultWithPostInfo with Pay Info - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let close_rval: DispatchResultWithPostInfo = Collective::close(RuntimeOrigin::signed(2), hash, 0, proposal_weight, proposal_len); assert_eq!(close_rval.unwrap().pays_fee, Pays::No); @@ -1091,7 +1091,7 @@ fn motions_reproposing_disapproved_works() { ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash: H256 = proposal.blake2_256().into(); assert_ok!(Collective::propose( RuntimeOrigin::signed(1), @@ -1123,7 +1123,7 @@ fn motions_approval_with_enough_votes_and_lower_voting_threshold_works() { ExtBuilder::default().build_and_execute(|| { let proposal = RuntimeCall::Democracy(mock_democracy::Call::external_propose_majority {}); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash: H256 = proposal.blake2_256().into(); // The voting threshold is 2, but the required votes for `ExternalMajorityOrigin` is 3. // The proposal will be executed regardless of the voting threshold @@ -1253,7 +1253,7 @@ fn motions_disapproval_works() { ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash: H256 = proposal.blake2_256().into(); assert_ok!(Collective::propose( RuntimeOrigin::signed(1), @@ -1312,7 +1312,7 @@ fn motions_approval_works() { ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash: H256 = proposal.blake2_256().into(); assert_ok!(Collective::propose( RuntimeOrigin::signed(1), @@ -1373,7 +1373,7 @@ fn motion_with_no_votes_closes_with_disapproval() { ExtBuilder::default().build_and_execute(|| { let proposal = make_proposal(42); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash: H256 = proposal.blake2_256().into(); assert_ok!(Collective::propose( RuntimeOrigin::signed(1), diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 984e5712ae0..39f846ac431 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -522,7 +522,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { run: impl FnOnce(&mut Self) -> DispatchResultWithPostInfo, ) -> Result { use frame_support::dispatch::extract_actual_weight; - let charged = self.charge_gas(runtime_cost(dispatch_info.weight))?; + let charged = self.charge_gas(runtime_cost(dispatch_info.call_weight))?; let result = run(self); let actual_weight = extract_actual_weight(&result, &dispatch_info); self.adjust_gas(charged, runtime_cost(actual_weight)); @@ -2347,7 +2347,7 @@ pub mod env { let execute_weight = <::Xcm as ExecuteController<_, _>>::WeightInfo::execute(); let weight = ctx.ext.gas_meter().gas_left().max(execute_weight); - let dispatch_info = DispatchInfo { weight, ..Default::default() }; + let dispatch_info = DispatchInfo { call_weight: weight, ..Default::default() }; ctx.call_dispatchable::( dispatch_info, diff --git a/substrate/frame/election-provider-multi-phase/src/lib.rs b/substrate/frame/election-provider-multi-phase/src/lib.rs index 072cfe176b6..06cb2963d76 100644 --- a/substrate/frame/election-provider-multi-phase/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/src/lib.rs @@ -245,7 +245,7 @@ use frame_support::{ weights::Weight, DefaultNoBound, EqNoBound, PartialEqNoBound, }; -use frame_system::{ensure_none, offchain::SendTransactionTypes, pallet_prelude::BlockNumberFor}; +use frame_system::{ensure_none, offchain::CreateInherent, pallet_prelude::BlockNumberFor}; use scale_info::TypeInfo; use sp_arithmetic::{ traits::{CheckedAdd, Zero}, @@ -576,7 +576,7 @@ pub mod pallet { use sp_runtime::traits::Convert; #[pallet::config] - pub trait Config: frame_system::Config + SendTransactionTypes> { + pub trait Config: frame_system::Config + CreateInherent> { type RuntimeEvent: From> + IsType<::RuntimeEvent> + TryInto>; diff --git a/substrate/frame/election-provider-multi-phase/src/mock.rs b/substrate/frame/election-provider-multi-phase/src/mock.rs index 32a099e1a26..2e5ac252720 100644 --- a/substrate/frame/election-provider-multi-phase/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/src/mock.rs @@ -421,14 +421,23 @@ impl Convert> for Runtime { } } -impl frame_system::offchain::SendTransactionTypes for Runtime +impl frame_system::offchain::CreateTransactionBase for Runtime where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; type Extrinsic = Extrinsic; } +impl frame_system::offchain::CreateInherent for Runtime +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + Extrinsic::new_bare(call) + } +} + pub type Extrinsic = sp_runtime::testing::TestXt; parameter_types! { diff --git a/substrate/frame/election-provider-multi-phase/src/unsigned.rs b/substrate/frame/election-provider-multi-phase/src/unsigned.rs index 4c56f02db52..191131ed3ac 100644 --- a/substrate/frame/election-provider-multi-phase/src/unsigned.rs +++ b/substrate/frame/election-provider-multi-phase/src/unsigned.rs @@ -31,7 +31,10 @@ use frame_support::{ traits::{DefensiveResult, Get}, BoundedVec, }; -use frame_system::{offchain::SubmitTransaction, pallet_prelude::BlockNumberFor}; +use frame_system::{ + offchain::{CreateInherent, SubmitTransaction}, + pallet_prelude::BlockNumberFor, +}; use scale_info::TypeInfo; use sp_npos_elections::{ assignment_ratio_to_staked_normalized, assignment_staked_to_ratio_normalized, ElectionResult, @@ -179,7 +182,7 @@ fn ocw_solution_exists() -> bool { matches!(StorageValueRef::persistent(OFFCHAIN_CACHED_CALL).get::>(), Ok(Some(_))) } -impl Pallet { +impl>> Pallet { /// Mine a new npos solution. /// /// The Npos Solver type, `S`, must have the same AccountId and Error type as the @@ -277,7 +280,8 @@ impl Pallet { fn submit_call(call: Call) -> Result<(), MinerError> { log!(debug, "miner submitting a solution as an unsigned transaction"); - SubmitTransaction::>::submit_unsigned_transaction(call.into()) + let xt = T::create_inherent(call.into()); + SubmitTransaction::>::submit_transaction(xt) .map_err(|_| MinerError::PoolSubmissionFailed) } @@ -1818,7 +1822,7 @@ mod tests { let encoded = pool.read().transactions[0].clone(); let extrinsic: Extrinsic = codec::Decode::decode(&mut &*encoded).unwrap(); - let call = extrinsic.call; + let call = extrinsic.function; assert!(matches!(call, RuntimeCall::MultiPhase(Call::submit_unsigned { .. }))); }) } @@ -1835,7 +1839,7 @@ mod tests { let encoded = pool.read().transactions[0].clone(); let extrinsic = Extrinsic::decode(&mut &*encoded).unwrap(); - let call = match extrinsic.call { + let call = match extrinsic.function { RuntimeCall::MultiPhase(call @ Call::submit_unsigned { .. }) => call, _ => panic!("bad call: unexpected submission"), }; diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index f20e3983b09..360f14913fc 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -61,7 +61,7 @@ pub const INIT_TIMESTAMP: BlockNumber = 30_000; pub const BLOCK_TIME: BlockNumber = 1000; type Block = frame_system::mocking::MockBlockU32; -type Extrinsic = testing::TestXt; +type Extrinsic = sp_runtime::testing::TestXt; frame_support::construct_runtime!( pub enum Runtime { @@ -308,14 +308,23 @@ impl pallet_staking::Config for Runtime { type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; } -impl frame_system::offchain::SendTransactionTypes for Runtime +impl frame_system::offchain::CreateTransactionBase for Runtime where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; type Extrinsic = Extrinsic; } +impl frame_system::offchain::CreateInherent for Runtime +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + Extrinsic::new_bare(call) + } +} + pub struct OnChainSeqPhragmen; parameter_types! { @@ -687,7 +696,7 @@ pub fn roll_to_with_ocw(n: BlockNumber, pool: Arc>, delay_solu for encoded in &pool.read().transactions { let extrinsic = Extrinsic::decode(&mut &encoded[..]).unwrap(); - let _ = match extrinsic.call { + let _ = match extrinsic.function { RuntimeCall::ElectionProviderMultiPhase( call @ Call::submit_unsigned { .. }, ) => { diff --git a/substrate/frame/elections-phragmen/src/lib.rs b/substrate/frame/elections-phragmen/src/lib.rs index a1c5f69e1b6..effbb6e786c 100644 --- a/substrate/frame/elections-phragmen/src/lib.rs +++ b/substrate/frame/elections-phragmen/src/lib.rs @@ -1408,7 +1408,7 @@ mod tests { pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = - sp_runtime::generic::UncheckedExtrinsic; + sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( pub enum Test diff --git a/substrate/frame/examples/Cargo.toml b/substrate/frame/examples/Cargo.toml index ee0f8df29cf..0c6ad5ef097 100644 --- a/substrate/frame/examples/Cargo.toml +++ b/substrate/frame/examples/Cargo.toml @@ -25,12 +25,14 @@ pallet-example-offchain-worker = { workspace = true } pallet-example-split = { workspace = true } pallet-example-single-block-migrations = { workspace = true } pallet-example-tasks = { workspace = true } +pallet-example-authorization-tx-extension = { workspace = true } [features] default = ["std"] std = [ "pallet-default-config-example/std", "pallet-dev-mode/std", + "pallet-example-authorization-tx-extension/std", "pallet-example-basic/std", "pallet-example-frame-crate/std", "pallet-example-kitchensink/std", @@ -42,6 +44,7 @@ std = [ try-runtime = [ "pallet-default-config-example/try-runtime", "pallet-dev-mode/try-runtime", + "pallet-example-authorization-tx-extension/try-runtime", "pallet-example-basic/try-runtime", "pallet-example-kitchensink/try-runtime", "pallet-example-offchain-worker/try-runtime", diff --git a/substrate/frame/examples/authorization-tx-extension/Cargo.toml b/substrate/frame/examples/authorization-tx-extension/Cargo.toml new file mode 100644 index 00000000000..9b51fc6c1e6 --- /dev/null +++ b/substrate/frame/examples/authorization-tx-extension/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "pallet-example-authorization-tx-extension" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license = "MIT-0" +homepage.workspace = true +repository.workspace = true +description = "FRAME example authorization transaction extension pallet" +publish = false + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true } +docify = { workspace = true } +log = { workspace = true } +scale-info = { features = ["derive"], workspace = true } + +frame-benchmarking = { optional = true, workspace = true } +frame-support = { features = ["experimental"], workspace = true } +frame-system = { workspace = true } + +sp-io = { workspace = true } +sp-runtime = { workspace = true } + +[dev-dependencies] +pallet-verify-signature = { workspace = true } +sp-core = { workspace = true } +sp-keyring = { workspace = true, default-features = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-verify-signature/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-verify-signature/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-verify-signature/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/examples/authorization-tx-extension/src/extensions.rs b/substrate/frame/examples/authorization-tx-extension/src/extensions.rs new file mode 100644 index 00000000000..d1e56916d3a --- /dev/null +++ b/substrate/frame/examples/authorization-tx-extension/src/extensions.rs @@ -0,0 +1,132 @@ +// 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 core::{fmt, marker::PhantomData}; + +use codec::{Decode, Encode}; +use frame_support::{traits::OriginTrait, Parameter}; +use scale_info::TypeInfo; +use sp_runtime::{ + impl_tx_ext_default, + traits::{ + DispatchInfoOf, DispatchOriginOf, IdentifyAccount, TransactionExtension, ValidateResult, + Verify, + }, + transaction_validity::{InvalidTransaction, ValidTransaction}, +}; + +use crate::pallet_coownership::{Config, Origin}; + +/// Helper struct to organize the data needed for signature verification of both parties involved. +#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] +pub struct AuthCredentials { + first: (Signer, Signature), + second: (Signer, Signature), +} + +/// Extension that, if activated by providing a pair of signers and signatures, will authorize a +/// coowner origin of the two signers. Both signers have to construct their signatures on all of the +/// data that follows this extension in the `TransactionExtension` pipeline, their implications and +/// the call. Essentially re-sign the transaction from this point onwards in the pipeline by using +/// the `inherited_implication`, as shown below. +#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct AuthorizeCoownership { + inner: Option>, + _phantom: PhantomData, +} + +impl Default for AuthorizeCoownership { + fn default() -> Self { + Self { inner: None, _phantom: Default::default() } + } +} + +impl AuthorizeCoownership { + /// Creates an active extension that will try to authorize the coownership origin. + pub fn new(first: (Signer, Signature), second: (Signer, Signature)) -> Self { + Self { inner: Some(AuthCredentials { first, second }), _phantom: Default::default() } + } +} + +impl fmt::Debug for AuthorizeCoownership { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "AuthorizeCoownership") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { + Ok(()) + } +} + +impl TransactionExtension + for AuthorizeCoownership +where + Signer: IdentifyAccount + Parameter + Send + Sync + 'static, + Signature: Verify + Parameter + Send + Sync + 'static, +{ + const IDENTIFIER: &'static str = "AuthorizeCoownership"; + type Implicit = (); + type Val = (); + type Pre = (); + + fn validate( + &self, + mut origin: DispatchOriginOf, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _self_implicit: Self::Implicit, + inherited_implication: &impl codec::Encode, + ) -> ValidateResult { + // If the extension is inactive, just move on in the pipeline. + let Some(auth) = &self.inner else { + return Ok((ValidTransaction::default(), (), origin)); + }; + let first_account = auth.first.0.clone().into_account(); + let second_account = auth.second.0.clone().into_account(); + + // Construct the payload to sign using the `inherited_implication`. + let msg = inherited_implication.using_encoded(sp_io::hashing::blake2_256); + + // Both parties' signatures must be correct for the origin to be authorized. + // In a prod environment, we're just return a `InvalidTransaction::BadProof` if the + // signature isn't valid, but we return these custom errors to be able to assert them in + // tests. + if !auth.first.1.verify(&msg[..], &first_account) { + Err(InvalidTransaction::Custom(100))? + } + if !auth.second.1.verify(&msg[..], &second_account) { + Err(InvalidTransaction::Custom(200))? + } + // Construct a `pallet_coownership::Origin`. + let local_origin = Origin::Coowners(first_account, second_account); + // Turn it into a local `PalletsOrigin`. + let local_origin = ::PalletsOrigin::from(local_origin); + // Then finally into a pallet `RuntimeOrigin`. + let local_origin = ::RuntimeOrigin::from(local_origin); + // Which the `set_caller_from` function will convert into the overarching `RuntimeOrigin` + // created by `construct_runtime!`. + origin.set_caller_from(local_origin); + // Make sure to return the new origin. + Ok((ValidTransaction::default(), (), origin)) + } + // We're not doing any special logic in `TransactionExtension::prepare`, so just impl a default. + impl_tx_ext_default!(T::RuntimeCall; weight prepare); +} diff --git a/substrate/frame/examples/authorization-tx-extension/src/lib.rs b/substrate/frame/examples/authorization-tx-extension/src/lib.rs new file mode 100644 index 00000000000..9105155a94d --- /dev/null +++ b/substrate/frame/examples/authorization-tx-extension/src/lib.rs @@ -0,0 +1,158 @@ +// 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. + +//! # Authorization Transaction Extension Example Pallet +//! +//! **This pallet serves as an example and is not meant to be used in production.** +//! +//! FRAME Transaction Extension reference implementation, origin mutation, origin authorization and +//! integration in a `TransactionExtension` pipeline. +//! +//! The [TransactionExtension](sp_runtime::traits::TransactionExtension) used in this example is +//! [AuthorizeCoownership](extensions::AuthorizeCoownership). If activated, the extension will +//! authorize 2 signers as coowners, with a [coowner origin](pallet_coownership::Origin) specific to +//! the [coownership example pallet](pallet_coownership), by validating a signature of the rest of +//! the transaction from each party. This means any extensions after ours in the pipeline, their +//! implicits and the actual call. The extension pipeline used in our example checks the genesis +//! hash, transaction version and mortality of the transaction after the `AuthorizeCoownership` runs +//! as we want these transactions to run regardless of what origin passes through them and/or we +//! want their implicit data in any signature authorization happening earlier in the pipeline. +//! +//! In this example, aside from the [AuthorizeCoownership](extensions::AuthorizeCoownership) +//! extension, we use the following pallets: +//! - [pallet_coownership] - provides a coowner origin and the functionality to authorize it. +//! - [pallet_assets] - a dummy asset pallet that tracks assets, identified by an +//! [AssetId](pallet_assets::AssetId), and their respective owners, which can be either an +//! [account](pallet_assets::Owner::Single) or a [pair of owners](pallet_assets::Owner::Double). +//! +//! Assets are created in [pallet_assets] using the +//! [create_asset](pallet_assets::Call::create_asset) call, which accepts traditionally signed +//! origins (a single account) or coowner origins, authorized through the +//! [CoownerOrigin](pallet_assets::Config::CoownerOrigin) type. +//! +//! ### Example runtime setup +#![doc = docify::embed!("src/mock.rs", example_runtime)] +//! +//! ### Example usage +#![doc = docify::embed!("src/tests.rs", create_coowned_asset_works)] +//! +//! This example does not focus on any pallet logic or syntax, but rather on `TransactionExtension` +//! functionality. The pallets used are just skeletons to provide storage state and custom origin +//! choices and requirements, as shown in the examples. Any weight and/or +//! transaction fee is out of scope for this example. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod extensions; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +extern crate alloc; + +use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::*; + +#[frame_support::pallet(dev_mode)] +pub mod pallet_coownership { + use super::*; + use frame_support::traits::OriginTrait; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The aggregated origin which the dispatch will take. + type RuntimeOrigin: OriginTrait + + From + + IsType<::RuntimeOrigin>; + + /// The caller origin, overarching type of all pallets origins. + type PalletsOrigin: From> + TryInto, Error = Self::PalletsOrigin>; + } + + #[pallet::pallet] + pub struct Pallet(_); + + /// Origin that this pallet can authorize. For the purposes of this example, it's just two + /// accounts that own something together. + #[pallet::origin] + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + pub enum Origin { + Coowners(T::AccountId, T::AccountId), + } +} + +#[frame_support::pallet(dev_mode)] +pub mod pallet_assets { + use super::*; + + pub type AssetId = u32; + + /// Type that describes possible owners of a particular asset. + #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] + pub enum Owner { + Single(AccountId), + Double(AccountId, AccountId), + } + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Type that can authorize an account pair coowner origin. + type CoownerOrigin: EnsureOrigin< + Self::RuntimeOrigin, + Success = (Self::AccountId, Self::AccountId), + >; + } + + /// Map that holds the owner information for each asset it manages. + #[pallet::storage] + pub type AssetOwners = + StorageMap<_, Blake2_128Concat, AssetId, Owner<::AccountId>>; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::error] + pub enum Error { + /// Asset already exists. + AlreadyExists, + } + + #[pallet::call] + impl Pallet { + /// Simple call that just creates an asset with a specific `AssetId`. This call will fail if + /// there is already an asset with the same `AssetId`. + /// + /// The origin is either a single account (traditionally signed origin) or a coowner origin. + #[pallet::call_index(0)] + pub fn create_asset(origin: OriginFor, asset_id: AssetId) -> DispatchResult { + let owner: Owner = match T::CoownerOrigin::try_origin(origin) { + Ok((first, second)) => Owner::Double(first, second), + Err(origin) => ensure_signed(origin).map(|account| Owner::Single(account))?, + }; + AssetOwners::::try_mutate(asset_id, |maybe_owner| { + if maybe_owner.is_some() { + return Err(Error::::AlreadyExists); + } + *maybe_owner = Some(owner); + Ok(()) + })?; + Ok(()) + } + } +} diff --git a/substrate/frame/examples/authorization-tx-extension/src/mock.rs b/substrate/frame/examples/authorization-tx-extension/src/mock.rs new file mode 100644 index 00000000000..aa70d12d7d8 --- /dev/null +++ b/substrate/frame/examples/authorization-tx-extension/src/mock.rs @@ -0,0 +1,142 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +pub(crate) use example_runtime::*; +use extensions::AuthorizeCoownership; +use frame_support::derive_impl; +use frame_system::{CheckEra, CheckGenesis, CheckNonce, CheckTxVersion}; +use pallet_verify_signature::VerifySignature; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + BuildStorage, MultiSignature, MultiSigner, +}; + +#[docify::export] +mod example_runtime { + use super::*; + + /// Our `TransactionExtension` fit for general transactions. + pub type TxExtension = ( + // Validate the signature of regular account transactions (substitutes the old signed + // transaction). + VerifySignature, + // Nonce check (and increment) for the caller. + CheckNonce, + // If activated, will mutate the origin to a `pallet_coownership` origin of 2 accounts that + // own something. + AuthorizeCoownership, + // Some other extensions that we want to run for every possible origin and we want captured + // in any and all signature and authorization schemes (such as the traditional account + // signature or the double signature in `pallet_coownership`). + CheckGenesis, + CheckTxVersion, + CheckEra, + ); + /// Convenience type to more easily construct the signature to be signed in case + /// `AuthorizeCoownership` is activated. + pub type InnerTxExtension = (CheckGenesis, CheckTxVersion, CheckEra); + pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; + pub type Header = generic::Header; + pub type Block = generic::Block; + pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + pub type Signature = MultiSignature; + pub type BlockNumber = u32; + + // For testing the pallet, we construct a mock runtime. + frame_support::construct_runtime!( + pub enum Runtime + { + System: frame_system, + VerifySignaturePallet: pallet_verify_signature, + + Assets: pallet_assets, + Coownership: pallet_coownership, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type AccountId = AccountId; + type Block = Block; + type Lookup = IdentityLookup; + } + + #[cfg(feature = "runtime-benchmarks")] + pub struct BenchmarkHelper; + #[cfg(feature = "runtime-benchmarks")] + impl pallet_verify_signature::BenchmarkHelper for BenchmarkHelper { + fn create_signature(_entropy: &[u8], msg: &[u8]) -> (MultiSignature, AccountId) { + use sp_io::crypto::{sr25519_generate, sr25519_sign}; + use sp_runtime::traits::IdentifyAccount; + let public = sr25519_generate(0.into(), None); + let who_account: AccountId = MultiSigner::Sr25519(public).into_account().into(); + let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &public, msg).unwrap()); + (signature, who_account) + } + } + + impl pallet_verify_signature::Config for Runtime { + type Signature = MultiSignature; + type AccountIdentifier = MultiSigner; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = BenchmarkHelper; + } + + /// Type that enables any pallet to ask for a coowner origin. + pub struct EnsureCoowner; + impl EnsureOrigin for EnsureCoowner { + type Success = (AccountId, AccountId); + + fn try_origin(o: RuntimeOrigin) -> Result { + match o.clone().into() { + Ok(pallet_coownership::Origin::::Coowners(first, second)) => + Ok((first, second)), + _ => Err(o), + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + unimplemented!() + } + } + + impl pallet_assets::Config for Runtime { + type CoownerOrigin = EnsureCoowner; + } + + impl pallet_coownership::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + } +} + +// This function basically just builds a genesis storage key/value store according to +// our desired mockup. +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = RuntimeGenesisConfig { + // We use default for brevity, but you can configure as desired if needed. + system: Default::default(), + } + .build_storage() + .unwrap(); + t.into() +} diff --git a/substrate/frame/examples/authorization-tx-extension/src/tests.rs b/substrate/frame/examples/authorization-tx-extension/src/tests.rs new file mode 100644 index 00000000000..7ede549a2f1 --- /dev/null +++ b/substrate/frame/examples/authorization-tx-extension/src/tests.rs @@ -0,0 +1,269 @@ +// 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. + +//! Tests for pallet-example-authorization-tx-extension. + +use codec::Encode; +use frame_support::{ + assert_noop, + dispatch::GetDispatchInfo, + pallet_prelude::{InvalidTransaction, TransactionValidityError}, +}; +use pallet_verify_signature::VerifySignature; +use sp_keyring::AccountKeyring; +use sp_runtime::{ + traits::{Applyable, Checkable, IdentityLookup, TransactionExtension}, + MultiSignature, MultiSigner, +}; + +use crate::{extensions::AuthorizeCoownership, mock::*, pallet_assets}; + +#[test] +fn create_asset_works() { + new_test_ext().execute_with(|| { + let alice_keyring = AccountKeyring::Alice; + let alice_account = AccountId::from(alice_keyring.public()); + // Simple call to create asset with Id `42`. + let create_asset_call = + RuntimeCall::Assets(pallet_assets::Call::create_asset { asset_id: 42 }); + // Create extension that will be used for dispatch. + let initial_nonce = 23; + let tx_ext = ( + frame_system::CheckNonce::::from(initial_nonce), + AuthorizeCoownership::::default(), + frame_system::CheckGenesis::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckEra::::from(sp_runtime::generic::Era::immortal()), + ); + // Create the transaction signature, to be used in the top level `VerifyMultiSignature` + // extension. + let tx_sign = MultiSignature::Sr25519( + (&create_asset_call, &tx_ext, tx_ext.implicit().unwrap()) + .using_encoded(|e| alice_keyring.sign(&sp_io::hashing::blake2_256(e))), + ); + // Add the signature to the extension. + let tx_ext = ( + VerifySignature::new_with_signature(tx_sign, alice_account.clone()), + frame_system::CheckNonce::::from(initial_nonce), + AuthorizeCoownership::::default(), + frame_system::CheckGenesis::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckEra::::from(sp_runtime::generic::Era::immortal()), + ); + // Create the transaction and we're ready for dispatch. + let uxt = UncheckedExtrinsic::new_transaction(create_asset_call, tx_ext); + // Check Extrinsic validity and apply it. + let uxt_info = uxt.get_dispatch_info(); + let uxt_len = uxt.using_encoded(|e| e.len()); + // Manually pay for Alice's nonce. + frame_system::Account::::mutate(&alice_account, |info| { + info.nonce = initial_nonce; + info.providers = 1; + }); + // Check should pass. + let xt = >>::check( + uxt, + &Default::default(), + ) + .unwrap(); + // Apply the extrinsic. + let res = xt.apply::(&uxt_info, uxt_len).unwrap(); + + // Asserting the results. + assert_eq!(frame_system::Account::::get(&alice_account).nonce, initial_nonce + 1); + assert_eq!( + pallet_assets::AssetOwners::::get(42), + Some(pallet_assets::Owner::::Single(alice_account)) + ); + assert!(res.is_ok()); + }); +} + +#[docify::export] +#[test] +fn create_coowned_asset_works() { + new_test_ext().execute_with(|| { + let alice_keyring = AccountKeyring::Alice; + let bob_keyring = AccountKeyring::Bob; + let charlie_keyring = AccountKeyring::Charlie; + let alice_account = AccountId::from(alice_keyring.public()); + let bob_account = AccountId::from(bob_keyring.public()); + let charlie_account = AccountId::from(charlie_keyring.public()); + // Simple call to create asset with Id `42`. + let create_asset_call = + RuntimeCall::Assets(pallet_assets::Call::create_asset { asset_id: 42 }); + // Create the inner transaction extension, to be signed by our coowners, Alice and Bob. + let inner_ext: InnerTxExtension = ( + frame_system::CheckGenesis::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckEra::::from(sp_runtime::generic::Era::immortal()), + ); + // Create the payload Alice and Bob need to sign. + let inner_payload = (&create_asset_call, &inner_ext, inner_ext.implicit().unwrap()); + // Create Alice's signature. + let alice_inner_sig = MultiSignature::Sr25519( + inner_payload.using_encoded(|e| alice_keyring.sign(&sp_io::hashing::blake2_256(e))), + ); + // Create Bob's signature. + let bob_inner_sig = MultiSignature::Sr25519( + inner_payload.using_encoded(|e| bob_keyring.sign(&sp_io::hashing::blake2_256(e))), + ); + // Create the transaction extension, to be signed by the submitter of the extrinsic, let's + // have it be Charlie. + let initial_nonce = 23; + let tx_ext = ( + frame_system::CheckNonce::::from(initial_nonce), + AuthorizeCoownership::::new( + (alice_keyring.into(), alice_inner_sig.clone()), + (bob_keyring.into(), bob_inner_sig.clone()), + ), + frame_system::CheckGenesis::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckEra::::from(sp_runtime::generic::Era::immortal()), + ); + // Create Charlie's transaction signature, to be used in the top level + // `VerifyMultiSignature` extension. + let tx_sign = MultiSignature::Sr25519( + (&create_asset_call, &tx_ext, tx_ext.implicit().unwrap()) + .using_encoded(|e| charlie_keyring.sign(&sp_io::hashing::blake2_256(e))), + ); + // Add the signature to the extension. + let tx_ext = ( + VerifySignature::new_with_signature(tx_sign, charlie_account.clone()), + frame_system::CheckNonce::::from(initial_nonce), + AuthorizeCoownership::::new( + (alice_keyring.into(), alice_inner_sig), + (bob_keyring.into(), bob_inner_sig), + ), + frame_system::CheckGenesis::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckEra::::from(sp_runtime::generic::Era::immortal()), + ); + // Create the transaction and we're ready for dispatch. + let uxt = UncheckedExtrinsic::new_transaction(create_asset_call, tx_ext); + // Check Extrinsic validity and apply it. + let uxt_info = uxt.get_dispatch_info(); + let uxt_len = uxt.using_encoded(|e| e.len()); + // Manually pay for Charlie's nonce. + frame_system::Account::::mutate(&charlie_account, |info| { + info.nonce = initial_nonce; + info.providers = 1; + }); + // Check should pass. + let xt = >>::check( + uxt, + &Default::default(), + ) + .unwrap(); + // Apply the extrinsic. + let res = xt.apply::(&uxt_info, uxt_len).unwrap(); + + // Asserting the results. + assert!(res.is_ok()); + assert_eq!(frame_system::Account::::get(charlie_account).nonce, initial_nonce + 1); + assert_eq!( + pallet_assets::AssetOwners::::get(42), + Some(pallet_assets::Owner::::Double(alice_account, bob_account)) + ); + }); +} + +#[test] +fn inner_authorization_works() { + new_test_ext().execute_with(|| { + let alice_keyring = AccountKeyring::Alice; + let bob_keyring = AccountKeyring::Bob; + let charlie_keyring = AccountKeyring::Charlie; + let charlie_account = AccountId::from(charlie_keyring.public()); + // Simple call to create asset with Id `42`. + let create_asset_call = + RuntimeCall::Assets(pallet_assets::Call::create_asset { asset_id: 42 }); + // Create the inner transaction extension, to be signed by our coowners, Alice and Bob. They + // are going to sign this transaction as a mortal one. + let inner_ext: InnerTxExtension = ( + frame_system::CheckGenesis::::new(), + frame_system::CheckTxVersion::::new(), + // Sign with mortal era check. + frame_system::CheckEra::::from(sp_runtime::generic::Era::mortal(4, 0)), + ); + // Create the payload Alice and Bob need to sign. + let inner_payload = (&create_asset_call, &inner_ext, inner_ext.implicit().unwrap()); + // Create Alice's signature. + let alice_inner_sig = MultiSignature::Sr25519( + inner_payload.using_encoded(|e| alice_keyring.sign(&sp_io::hashing::blake2_256(e))), + ); + // Create Bob's signature. + let bob_inner_sig = MultiSignature::Sr25519( + inner_payload.using_encoded(|e| bob_keyring.sign(&sp_io::hashing::blake2_256(e))), + ); + // Create the transaction extension, to be signed by the submitter of the extrinsic, let's + // have it be Charlie. + let initial_nonce = 23; + let tx_ext = ( + frame_system::CheckNonce::::from(initial_nonce), + AuthorizeCoownership::::new( + (alice_keyring.into(), alice_inner_sig.clone()), + (bob_keyring.into(), bob_inner_sig.clone()), + ), + frame_system::CheckGenesis::::new(), + frame_system::CheckTxVersion::::new(), + // Construct the transaction as immortal with a different era check. + frame_system::CheckEra::::from(sp_runtime::generic::Era::immortal()), + ); + // Create Charlie's transaction signature, to be used in the top level + // `VerifyMultiSignature` extension. + let tx_sign = MultiSignature::Sr25519( + (&create_asset_call, &tx_ext, tx_ext.implicit().unwrap()) + .using_encoded(|e| charlie_keyring.sign(&sp_io::hashing::blake2_256(e))), + ); + // Add the signature to the extension that Charlie signed. + let tx_ext = ( + VerifySignature::new_with_signature(tx_sign, charlie_account.clone()), + frame_system::CheckNonce::::from(initial_nonce), + AuthorizeCoownership::::new( + (alice_keyring.into(), alice_inner_sig), + (bob_keyring.into(), bob_inner_sig), + ), + frame_system::CheckGenesis::::new(), + frame_system::CheckTxVersion::::new(), + // Construct the transaction as immortal with a different era check. + frame_system::CheckEra::::from(sp_runtime::generic::Era::immortal()), + ); + // Create the transaction and we're ready for dispatch. + let uxt = UncheckedExtrinsic::new_transaction(create_asset_call, tx_ext); + // Check Extrinsic validity and apply it. + let uxt_info = uxt.get_dispatch_info(); + let uxt_len = uxt.using_encoded(|e| e.len()); + // Manually pay for Charlie's nonce. + frame_system::Account::::mutate(charlie_account, |info| { + info.nonce = initial_nonce; + info.providers = 1; + }); + // Check should pass. + let xt = >>::check( + uxt, + &Default::default(), + ) + .unwrap(); + // The extrinsic should fail as the signature for the `AuthorizeCoownership` doesn't work + // for the provided payload with the changed transaction mortality. + assert_noop!( + xt.apply::(&uxt_info, uxt_len), + TransactionValidityError::Invalid(InvalidTransaction::Custom(100)) + ); + }); +} diff --git a/substrate/frame/examples/basic/src/lib.rs b/substrate/frame/examples/basic/src/lib.rs index fea04cb447a..2f1b32d964e 100644 --- a/substrate/frame/examples/basic/src/lib.rs +++ b/substrate/frame/examples/basic/src/lib.rs @@ -46,9 +46,10 @@ //! use the [`Config::WeightInfo`] trait to calculate call weights. This can also be overridden, //! as demonstrated by [`Call::set_dummy`]. //! - A private function that performs a storage update. -//! - A simple signed extension implementation (see: [`sp_runtime::traits::SignedExtension`]) which -//! increases the priority of the [`Call::set_dummy`] if it's present and drops any transaction -//! with an encoded length higher than 200 bytes. +//! - A simple transaction extension implementation (see: +//! [`sp_runtime::traits::TransactionExtension`]) which increases the priority of the +//! [`Call::set_dummy`] if it's present and drops any transaction with an encoded length higher +//! than 200 bytes. // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] @@ -67,10 +68,12 @@ use frame_system::ensure_signed; use log::info; use scale_info::TypeInfo; use sp_runtime::{ - traits::{Bounded, DispatchInfoOf, SaturatedConversion, Saturating, SignedExtension}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, + impl_tx_ext_default, + traits::{ + Bounded, DispatchInfoOf, DispatchOriginOf, SaturatedConversion, Saturating, + TransactionExtension, ValidateResult, }, + transaction_validity::{InvalidTransaction, ValidTransaction}, }; // Re-export pallet items so that they can be accessed from the crate namespace. @@ -440,42 +443,43 @@ impl Pallet { } } -// Similar to other FRAME pallets, your pallet can also define a signed extension and perform some -// checks and [pre/post]processing [before/after] the transaction. A signed extension can be any -// decodable type that implements `SignedExtension`. See the trait definition for the full list of -// bounds. As a convention, you can follow this approach to create an extension for your pallet: +// Similar to other FRAME pallets, your pallet can also define a transaction extension and perform +// some checks and [pre/post]processing [before/after] the transaction. A transaction extension can +// be any decodable type that implements `TransactionExtension`. See the trait definition for the +// full list of bounds. As a convention, you can follow this approach to create an extension for +// your pallet: // - If the extension does not carry any data, then use a tuple struct with just a `marker` // (needed for the compiler to accept `T: Config`) will suffice. // - Otherwise, create a tuple struct which contains the external data. Of course, for the entire // struct to be decodable, each individual item also needs to be decodable. // -// Note that a signed extension can also indicate that a particular data must be present in the -// _signing payload_ of a transaction by providing an implementation for the `additional_signed` -// method. This example will not cover this type of extension. See `CheckSpecVersion` in -// [FRAME System](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/system#signed-extensions) +// Note that a transaction extension can also indicate that a particular data must be present in the +// _signing payload_ of a transaction by providing an implementation for the `implicit` method. This +// example will not cover this type of extension. See `CheckSpecVersion` in [FRAME +// System](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/system#signed-extensions) // for an example. // // Using the extension, you can add some hooks to the life cycle of each transaction. Note that by // default, an extension is applied to all `Call` functions (i.e. all transactions). the `Call` enum -// variant is given to each function of `SignedExtension`. Hence, you can filter based on pallet or -// a particular call if needed. +// variant is given to each function of `TransactionExtension`. Hence, you can filter based on +// pallet or a particular call if needed. // // Some extra information, such as encoded length, some static dispatch info like weight and the // sender of the transaction (if signed) are also provided. // -// The full list of hooks that can be added to a signed extension can be found -// [here](https://paritytech.github.io/polkadot-sdk/master/sp_runtime/traits/trait.SignedExtension.html). +// The full list of hooks that can be added to a transaction extension can be found in the +// `TransactionExtension` trait definition. // -// The signed extensions are aggregated in the runtime file of a substrate chain. All extensions -// should be aggregated in a tuple and passed to the `CheckedExtrinsic` and `UncheckedExtrinsic` -// types defined in the runtime. Lookup `pub type SignedExtra = (...)` in `node/runtime` and -// `node-template` for an example of this. +// The transaction extensions are aggregated in the runtime file of a substrate chain. All +// extensions should be aggregated in a tuple and passed to the `CheckedExtrinsic` and +// `UncheckedExtrinsic` types defined in the runtime. Lookup `pub type TxExtension = (...)` in +// `node/runtime` and `node-template` for an example of this. -/// A simple signed extension that checks for the `set_dummy` call. In that case, it increases the -/// priority and prints some log. +/// A simple transaction extension that checks for the `set_dummy` call. In that case, it increases +/// the priority and prints some log. /// /// Additionally, it drops any transaction with an encoded length higher than 200 bytes. No -/// particular reason why, just to demonstrate the power of signed extensions. +/// particular reason why, just to demonstrate the power of transaction extensions. #[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct WatchDummy(PhantomData); @@ -486,52 +490,42 @@ impl core::fmt::Debug for WatchDummy { } } -impl SignedExtension for WatchDummy +impl TransactionExtension<::RuntimeCall> + for WatchDummy where ::RuntimeCall: IsSubType>, { const IDENTIFIER: &'static str = "WatchDummy"; - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = (); + type Implicit = (); type Pre = (); - - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } + type Val = (); fn validate( &self, - _who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + origin: DispatchOriginOf<::RuntimeCall>, + call: &::RuntimeCall, + _info: &DispatchInfoOf<::RuntimeCall>, len: usize, - ) -> TransactionValidity { + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult::RuntimeCall> { // if the transaction is too big, just drop it. if len > 200 { - return InvalidTransaction::ExhaustsResources.into() + return Err(InvalidTransaction::ExhaustsResources.into()) } // check for `set_dummy` - match call.is_sub_type() { + let validity = match call.is_sub_type() { Some(Call::set_dummy { .. }) => { sp_runtime::print("set_dummy was received."); let valid_tx = ValidTransaction { priority: Bounded::max_value(), ..Default::default() }; - Ok(valid_tx) + valid_tx }, - _ => Ok(Default::default()), - } + _ => Default::default(), + }; + Ok((validity, (), origin)) } + impl_tx_ext_default!(::RuntimeCall; weight prepare); } diff --git a/substrate/frame/examples/basic/src/tests.rs b/substrate/frame/examples/basic/src/tests.rs index d7095eb3c94..8e33d3d0a34 100644 --- a/substrate/frame/examples/basic/src/tests.rs +++ b/substrate/frame/examples/basic/src/tests.rs @@ -27,7 +27,7 @@ use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures // or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, DispatchTransaction, IdentityLookup}, BuildStorage, }; // Reexport crate as its pallet name for construct_runtime. @@ -146,13 +146,16 @@ fn signed_ext_watch_dummy_works() { assert_eq!( WatchDummy::(PhantomData) - .validate(&1, &call, &info, 150) + .validate_only(Some(1).into(), &call, &info, 150) .unwrap() + .0 .priority, u64::MAX, ); assert_eq!( - WatchDummy::(PhantomData).validate(&1, &call, &info, 250), + WatchDummy::(PhantomData) + .validate_only(Some(1).into(), &call, &info, 250) + .unwrap_err(), InvalidTransaction::ExhaustsResources.into(), ); }) @@ -174,13 +177,13 @@ fn weights_work() { let info1 = default_call.get_dispatch_info(); // aka. `let info = as GetDispatchInfo>::get_dispatch_info(&default_call);` // TODO: account for proof size weight - assert!(info1.weight.ref_time() > 0); - assert_eq!(info1.weight, ::WeightInfo::accumulate_dummy()); + assert!(info1.call_weight.ref_time() > 0); + assert_eq!(info1.call_weight, ::WeightInfo::accumulate_dummy()); // `set_dummy` is simpler than `accumulate_dummy`, and the weight // should be less. let custom_call = pallet_example_basic::Call::::set_dummy { new_value: 20 }; let info2 = custom_call.get_dispatch_info(); // TODO: account for proof size weight - assert!(info1.weight.ref_time() > info2.weight.ref_time()); + assert!(info1.call_weight.ref_time() > info2.call_weight.ref_time()); } diff --git a/substrate/frame/examples/offchain-worker/src/lib.rs b/substrate/frame/examples/offchain-worker/src/lib.rs index add014f6b34..b3fdb6ea189 100644 --- a/substrate/frame/examples/offchain-worker/src/lib.rs +++ b/substrate/frame/examples/offchain-worker/src/lib.rs @@ -53,8 +53,8 @@ use frame_support::traits::Get; use frame_system::{ self as system, offchain::{ - AppCrypto, CreateSignedTransaction, SendSignedTransaction, SendUnsignedTransaction, - SignedPayload, Signer, SigningTypes, SubmitTransaction, + AppCrypto, CreateInherent, CreateSignedTransaction, SendSignedTransaction, + SendUnsignedTransaction, SignedPayload, Signer, SigningTypes, SubmitTransaction, }, pallet_prelude::BlockNumberFor, }; @@ -124,7 +124,9 @@ pub mod pallet { /// This pallet's configuration trait #[pallet::config] - pub trait Config: CreateSignedTransaction> + frame_system::Config { + pub trait Config: + CreateSignedTransaction> + CreateInherent> + frame_system::Config + { /// The identifier type for an offchain worker. type AuthorityId: AppCrypto; @@ -501,7 +503,8 @@ impl Pallet { // implement unsigned validation logic, as any mistakes can lead to opening DoS or spam // attack vectors. See validation logic docs for more details. // - SubmitTransaction::>::submit_unsigned_transaction(call.into()) + let xt = T::create_inherent(call.into()); + SubmitTransaction::>::submit_transaction(xt) .map_err(|()| "Unable to submit unsigned transaction.")?; Ok(()) diff --git a/substrate/frame/examples/offchain-worker/src/tests.rs b/substrate/frame/examples/offchain-worker/src/tests.rs index 741adbe6d26..755beb8b82e 100644 --- a/substrate/frame/examples/offchain-worker/src/tests.rs +++ b/substrate/frame/examples/offchain-worker/src/tests.rs @@ -31,7 +31,7 @@ use sp_core::{ use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; use sp_runtime::{ testing::TestXt, - traits::{BlakeTwo256, Extrinsic as ExtrinsicT, IdentifyAccount, IdentityLookup, Verify}, + traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, RuntimeAppPublic, }; @@ -80,25 +80,47 @@ impl frame_system::offchain::SigningTypes for Test { type Signature = Signature; } -impl frame_system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; type Extrinsic = Extrinsic; } +impl frame_system::offchain::CreateTransaction for Test +where + RuntimeCall: From, +{ + type Extension = (); + + fn create_transaction(call: RuntimeCall, _extension: Self::Extension) -> Extrinsic { + Extrinsic::new_transaction(call, ()) + } +} + impl frame_system::offchain::CreateSignedTransaction for Test where RuntimeCall: From, { - fn create_transaction>( + fn create_signed_transaction< + C: frame_system::offchain::AppCrypto, + >( call: RuntimeCall, _public: ::Signer, _account: AccountId, nonce: u64, - ) -> Option<(RuntimeCall, ::SignaturePayload)> { - Some((call, (nonce, ()))) + ) -> Option { + Some(Extrinsic::new_signed(call, nonce, (), ())) + } +} + +impl frame_system::offchain::CreateInherent for Test +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + Extrinsic::new_bare(call) } } @@ -218,8 +240,8 @@ fn should_submit_signed_transaction_on_chain() { let tx = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.signature.unwrap().0, 0); - assert_eq!(tx.call, RuntimeCall::Example(crate::Call::submit_price { price: 15523 })); + assert!(matches!(tx.preamble, sp_runtime::generic::Preamble::Signed(0, (), 0, (),))); + assert_eq!(tx.function, RuntimeCall::Example(crate::Call::submit_price { price: 15523 })); }); } @@ -258,11 +280,11 @@ fn should_submit_unsigned_transaction_on_chain_for_any_account() { // then let tx = pool_state.write().transactions.pop().unwrap(); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.signature, None); + assert!(tx.is_inherent()); if let RuntimeCall::Example(crate::Call::submit_price_unsigned_with_signed_payload { price_payload: body, signature, - }) = tx.call + }) = tx.function { assert_eq!(body, price_payload); @@ -313,11 +335,11 @@ fn should_submit_unsigned_transaction_on_chain_for_all_accounts() { // then let tx = pool_state.write().transactions.pop().unwrap(); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.signature, None); + assert!(tx.is_inherent()); if let RuntimeCall::Example(crate::Call::submit_price_unsigned_with_signed_payload { price_payload: body, signature, - }) = tx.call + }) = tx.function { assert_eq!(body, price_payload); @@ -354,9 +376,9 @@ fn should_submit_raw_unsigned_transaction_on_chain() { let tx = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.signature, None); + assert!(tx.is_inherent()); assert_eq!( - tx.call, + tx.function, RuntimeCall::Example(crate::Call::submit_price_unsigned { block_number: 1, price: 15523 diff --git a/substrate/frame/examples/src/lib.rs b/substrate/frame/examples/src/lib.rs index dee23a41379..d0d30830f2f 100644 --- a/substrate/frame/examples/src/lib.rs +++ b/substrate/frame/examples/src/lib.rs @@ -40,12 +40,16 @@ //! - [`pallet_example_split`]: A simple example of a FRAME pallet demonstrating the ability to //! split sections across multiple files. //! -//! - [`pallet_example_frame_crate`]: Example pallet showcasing how one can be -//! built using only the `frame` umbrella crate. +//! - [`pallet_example_frame_crate`]: Example pallet showcasing how one can be built using only the +//! `frame` umbrella crate. //! //! - [`pallet_example_single_block_migrations`]: An example pallet demonstrating best-practices for //! writing storage migrations. //! //! - [`pallet_example_tasks`]: This pallet demonstrates the use of `Tasks` to execute service work. //! +//! - [`pallet_example_authorization_tx_extension`]: An example `TransactionExtension` that +//! authorizes a custom origin through signature validation, along with two support pallets to +//! showcase the usage. +//! //! **Tip**: Use `cargo doc --package --open` to view each pallet's documentation. diff --git a/substrate/frame/examples/tasks/src/lib.rs b/substrate/frame/examples/tasks/src/lib.rs index 1908a235ba1..7d51617497d 100644 --- a/substrate/frame/examples/tasks/src/lib.rs +++ b/substrate/frame/examples/tasks/src/lib.rs @@ -19,7 +19,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::dispatch::DispatchResult; -use frame_system::offchain::SendTransactionTypes; +use frame_system::offchain::CreateInherent; #[cfg(feature = "experimental")] use frame_system::offchain::SubmitTransaction; // Re-export pallet items so that they can be accessed from the crate namespace. @@ -77,22 +77,21 @@ pub mod pallet { let call = frame_system::Call::::do_task { task: runtime_task.into() }; // Submit the task as an unsigned transaction - let res = - SubmitTransaction::>::submit_unsigned_transaction( - call.into(), - ); + let xt = >>::create_inherent(call.into()); + let res = SubmitTransaction::>::submit_transaction(xt); match res { Ok(_) => log::info!(target: LOG_TARGET, "Submitted the task."), Err(e) => log::error!(target: LOG_TARGET, "Error submitting task: {:?}", e), } } } + + #[cfg(not(feature = "experimental"))] + fn offchain_worker(_block_number: BlockNumberFor) {} } #[pallet::config] - pub trait Config: - SendTransactionTypes> + frame_system::Config - { + pub trait Config: CreateInherent> + frame_system::Config { type RuntimeTask: frame_support::traits::Task + IsType<::RuntimeTask> + From>; diff --git a/substrate/frame/examples/tasks/src/mock.rs b/substrate/frame/examples/tasks/src/mock.rs index 9a1112946f6..3dc9153c94a 100644 --- a/substrate/frame/examples/tasks/src/mock.rs +++ b/substrate/frame/examples/tasks/src/mock.rs @@ -40,14 +40,23 @@ impl frame_system::Config for Runtime { type Block = Block; } -impl frame_system::offchain::SendTransactionTypes for Runtime +impl frame_system::offchain::CreateTransactionBase for Runtime where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; type Extrinsic = Extrinsic; } +impl frame_system::offchain::CreateInherent for Runtime +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + Extrinsic::new_bare(call) + } +} + impl pallet_example_tasks::Config for Runtime { type RuntimeTask = RuntimeTask; type WeightInfo = (); diff --git a/substrate/frame/examples/tasks/src/tests.rs b/substrate/frame/examples/tasks/src/tests.rs index 6c8acb0194b..4b31849c2ea 100644 --- a/substrate/frame/examples/tasks/src/tests.rs +++ b/substrate/frame/examples/tasks/src/tests.rs @@ -157,6 +157,7 @@ fn task_with_offchain_worker() { let tx = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - assert_eq!(tx.signature, None); + use sp_runtime::traits::ExtrinsicLike; + assert!(tx.is_bare()); }); } diff --git a/substrate/frame/examples/tasks/src/weights.rs b/substrate/frame/examples/tasks/src/weights.rs index 793af6e9622..c9ddea6f9a8 100644 --- a/substrate/frame/examples/tasks/src/weights.rs +++ b/substrate/frame/examples/tasks/src/weights.rs @@ -15,30 +15,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for `pallet_example_tasks` +//! Autogenerated weights for `tasks_example` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-02, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `MacBook.local`, CPU: `` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/release/node-template +// ./target/production/substrate-node // benchmark // pallet -// --chain -// dev -// --pallet -// pallet_example_tasks -// --extrinsic -// * -// --steps -// 20 -// --repeat -// 10 -// --output -// frame/examples/tasks/src/weights.rs +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=tasks_example +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/examples/tasks/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,37 +49,42 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_template. +/// Weight functions needed for `tasks_example`. pub trait WeightInfo { fn add_number_into_total() -> Weight; } -/// Weight functions for `pallet_example_kitchensink`. +/// Weights for `tasks_example` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Kitchensink OtherFoo (r:0 w:1) - /// Proof Skipped: Kitchensink OtherFoo (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TasksExample::Numbers` (r:1 w:1) + /// Proof: `TasksExample::Numbers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TasksExample::Total` (r:1 w:1) + /// Proof: `TasksExample::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn add_number_into_total() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 1_000_000 picoseconds. - Weight::from_parts(1_000_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(T::DbWeight::get().writes(1)) + // Measured: `149` + // Estimated: `3614` + // Minimum execution time: 5_776_000 picoseconds. + Weight::from_parts(6_178_000, 3614) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Kitchensink OtherFoo (r:0 w:1) - /// Proof Skipped: Kitchensink OtherFoo (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: `TasksExample::Numbers` (r:1 w:1) + /// Proof: `TasksExample::Numbers` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TasksExample::Total` (r:1 w:1) + /// Proof: `TasksExample::Total` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn add_number_into_total() -> Weight { // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 1_000_000 picoseconds. - Weight::from_parts(1_000_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(RocksDbWeight::get().writes(1)) + // Measured: `149` + // Estimated: `3614` + // Minimum execution time: 5_776_000 picoseconds. + Weight::from_parts(6_178_000, 3614) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/substrate/frame/executive/src/tests.rs b/substrate/frame/executive/src/tests.rs index 69a970a89d9..3841b010325 100644 --- a/substrate/frame/executive/src/tests.rs +++ b/substrate/frame/executive/src/tests.rs @@ -24,7 +24,7 @@ use sp_core::H256; use sp_runtime::{ generic::{DigestItem, Era}, testing::{Block, Digest, Header}, - traits::{Block as BlockT, Header as HeaderT}, + traits::{Block as BlockT, Header as HeaderT, TransactionExtension}, transaction_validity::{ InvalidTransaction, TransactionValidityError, UnknownTransaction, ValidTransaction, }, @@ -309,6 +309,34 @@ parameter_types! { }; } +pub struct MockExtensionsWeights; +impl frame_system::ExtensionsWeightInfo for MockExtensionsWeights { + fn check_genesis() -> Weight { + Weight::zero() + } + fn check_mortality_mortal_transaction() -> Weight { + Weight::from_parts(10, 0) + } + fn check_mortality_immortal_transaction() -> Weight { + Weight::from_parts(10, 0) + } + fn check_non_zero_sender() -> Weight { + Weight::zero() + } + fn check_nonce() -> Weight { + Weight::from_parts(10, 0) + } + fn check_spec_version() -> Weight { + Weight::zero() + } + fn check_tx_version() -> Weight { + Weight::zero() + } + fn check_weight() -> Weight { + Weight::from_parts(10, 0) + } +} + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type BlockWeights = BlockWeights; @@ -323,6 +351,7 @@ impl frame_system::Config for Runtime { type PostInherents = MockedSystemCallbacks; type PostTransactions = MockedSystemCallbacks; type MultiBlockMigrator = MockedModeGetter; + type ExtensionsWeightInfo = MockExtensionsWeights; } #[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, MaxEncodedLen, TypeInfo, RuntimeDebug)] @@ -336,15 +365,60 @@ impl VariantCount for FreezeReasonId { type Balance = u64; +pub struct BalancesWeights; +impl pallet_balances::WeightInfo for BalancesWeights { + fn transfer_allow_death() -> Weight { + Weight::from_parts(25, 0) + } + fn transfer_keep_alive() -> Weight { + Weight::zero() + } + fn force_set_balance_creating() -> Weight { + Weight::zero() + } + fn force_set_balance_killing() -> Weight { + Weight::zero() + } + fn force_transfer() -> Weight { + Weight::zero() + } + fn transfer_all() -> Weight { + Weight::zero() + } + fn force_unreserve() -> Weight { + Weight::zero() + } + fn upgrade_accounts(_u: u32) -> Weight { + Weight::zero() + } + fn force_adjust_total_issuance() -> Weight { + Weight::zero() + } + fn burn_allow_death() -> Weight { + Weight::zero() + } + fn burn_keep_alive() -> Weight { + Weight::zero() + } +} + #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Runtime { type Balance = Balance; type AccountStore = System; + type WeightInfo = BalancesWeights; type RuntimeFreezeReason = FreezeReasonId; type FreezeIdentifier = FreezeReasonId; type MaxFreezes = VariantCountOf; } +pub struct MockTxPaymentWeights; +impl pallet_transaction_payment::WeightInfo for MockTxPaymentWeights { + fn charge_transaction_payment() -> Weight { + Weight::from_parts(10, 0) + } +} + parameter_types! { pub const TransactionByteFee: Balance = 0; } @@ -355,6 +429,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = IdentityFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = (); + type WeightInfo = MockTxPaymentWeights; } impl custom::Config for Runtime {} @@ -372,14 +447,19 @@ parameter_types! { Default::default(); } -type SignedExtra = ( +type TxExtension = ( frame_system::CheckEra, frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, ); -type TestXt = sp_runtime::testing::TestXt; -type TestBlock = Block; +type UncheckedXt = sp_runtime::generic::UncheckedExtrinsic< + u64, + RuntimeCall, + sp_runtime::testing::UintAuthorityId, + TxExtension, +>; +type TestBlock = Block; // Will contain `true` when the custom runtime logic was called. const CUSTOM_ON_RUNTIME_KEY: &[u8] = b":custom:on_runtime"; @@ -399,7 +479,7 @@ impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade { type Executive = super::Executive< Runtime, - Block, + Block, ChainContext, Runtime, AllPalletsWithSystem, @@ -474,17 +554,14 @@ impl MultiStepMigrator for MockedModeGetter { } } -fn extra(nonce: u64, fee: Balance) -> SignedExtra { +fn tx_ext(nonce: u64, fee: Balance) -> TxExtension { ( frame_system::CheckEra::from(Era::Immortal), frame_system::CheckNonce::from(nonce), frame_system::CheckWeight::new(), pallet_transaction_payment::ChargeTransactionPayment::from(fee), ) -} - -fn sign_extra(who: u64, nonce: u64, fee: Balance) -> Option<(u64, SignedExtra)> { - Some((who, extra(nonce, fee))) + .into() } fn call_transfer(dest: u64, value: u64) -> RuntimeCall { @@ -497,8 +574,8 @@ fn balance_transfer_dispatch_works() { pallet_balances::GenesisConfig:: { balances: vec![(1, 211)] } .assimilate_storage(&mut t) .unwrap(); - let xt = TestXt::new(call_transfer(2, 69), sign_extra(1, 0, 0)); - let weight = xt.get_dispatch_info().weight + + let xt = UncheckedXt::new_signed(call_transfer(2, 69), 1, 1.into(), tx_ext(0, 0)); + let weight = xt.get_dispatch_info().total_weight() + ::BlockWeights::get() .get(DispatchClass::Normal) .base_extrinsic; @@ -608,7 +685,7 @@ fn block_import_of_bad_extrinsic_root_fails() { fn bad_extrinsic_not_inserted() { let mut t = new_test_ext(1); // bad nonce check! - let xt = TestXt::new(call_transfer(33, 69), sign_extra(1, 30, 0)); + let xt = UncheckedXt::new_signed(call_transfer(33, 69), 1, 1.into(), tx_ext(30, 0)); t.execute_with(|| { Executive::initialize_block(&Header::new_from_number(1)); assert_err!( @@ -622,35 +699,47 @@ fn bad_extrinsic_not_inserted() { #[test] fn block_weight_limit_enforced() { let mut t = new_test_ext(10000); - // given: TestXt uses the encoded len as fixed Len: - let xt = TestXt::new( - RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), - ); - let encoded = xt.encode(); - let encoded_len = encoded.len() as u64; + let transfer_weight = + <::WeightInfo as pallet_balances::WeightInfo>::transfer_allow_death(); + let extension_weight = tx_ext(0u32.into(), 0) + .weight(&RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 })); // on_initialize weight + base block execution weight let block_weights = ::BlockWeights::get(); let base_block_weight = Weight::from_parts(175, 0) + block_weights.base_block; let limit = block_weights.get(DispatchClass::Normal).max_total.unwrap() - base_block_weight; - let num_to_exhaust_block = limit.ref_time() / (encoded_len + 5); + let num_to_exhaust_block = + limit.ref_time() / (transfer_weight.ref_time() + extension_weight.ref_time() + 5); t.execute_with(|| { Executive::initialize_block(&Header::new_from_number(1)); // Base block execution weight + `on_initialize` weight from the custom module. assert_eq!(>::block_weight().total(), base_block_weight); for nonce in 0..=num_to_exhaust_block { - let xt = TestXt::new( + let xt = UncheckedXt::new_signed( RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, nonce.into(), 0), + 1, + 1.into(), + tx_ext(nonce.into(), 0), ); + let encoded = xt.encode(); + let encoded_len = encoded.len() as u64; let res = Executive::apply_extrinsic(xt); if nonce != num_to_exhaust_block { assert!(res.is_ok()); assert_eq!( >::block_weight().total(), - //--------------------- on_initialize + block_execution + extrinsic_base weight + extrinsic len - Weight::from_parts((encoded_len + 5) * (nonce + 1), (nonce + 1)* encoded_len) + base_block_weight, + //--------------------- + // on_initialize + // + block_execution + // + extrinsic_base weight + // + call weight + // + extension weight + // + extrinsic len + Weight::from_parts( + (transfer_weight.ref_time() + extension_weight.ref_time() + 5) * + (nonce + 1), + (nonce + 1) * encoded_len + ) + base_block_weight, ); assert_eq!( >::extrinsic_index(), @@ -665,20 +754,28 @@ fn block_weight_limit_enforced() { #[test] fn block_weight_and_size_is_stored_per_tx() { - let xt = TestXt::new( + let xt = UncheckedXt::new_signed( RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), + 1, + 1.into(), + tx_ext(0, 0), ); - let x1 = TestXt::new( + let x1 = UncheckedXt::new_signed( RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 1, 0), + 1, + 1.into(), + tx_ext(1, 0), ); - let x2 = TestXt::new( + let x2 = UncheckedXt::new_signed( RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 2, 0), + 1, + 1.into(), + tx_ext(2, 0), ); let len = xt.clone().encode().len() as u32; - let mut t = new_test_ext(1); + let extension_weight = xt.extension_weight(); + let transfer_weight = <::WeightInfo as pallet_balances::WeightInfo>::transfer_allow_death(); + let mut t = new_test_ext(2); t.execute_with(|| { // Block execution weight + on_initialize weight from custom module let base_block_weight = Weight::from_parts(175, 0) + @@ -693,8 +790,8 @@ fn block_weight_and_size_is_stored_per_tx() { assert!(Executive::apply_extrinsic(x1.clone()).unwrap().is_ok()); assert!(Executive::apply_extrinsic(x2.clone()).unwrap().is_ok()); - // default weight for `TestXt` == encoded length. - let extrinsic_weight = Weight::from_parts(len as u64, 0) + + let extrinsic_weight = transfer_weight + + extension_weight + ::BlockWeights::get() .get(DispatchClass::Normal) .base_extrinsic; @@ -720,8 +817,8 @@ fn block_weight_and_size_is_stored_per_tx() { #[test] fn validate_unsigned() { - let valid = TestXt::new(RuntimeCall::Custom(custom::Call::allowed_unsigned {}), None); - let invalid = TestXt::new(RuntimeCall::Custom(custom::Call::unallowed_unsigned {}), None); + let valid = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::allowed_unsigned {})); + let invalid = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::unallowed_unsigned {})); let mut t = new_test_ext(1); t.execute_with(|| { @@ -762,9 +859,11 @@ fn can_not_pay_for_tx_fee_on_full_lock() { 110, ) .unwrap(); - let xt = TestXt::new( + let xt = UncheckedXt::new_signed( RuntimeCall::System(frame_system::Call::remark { remark: vec![1u8] }), - sign_extra(1, 0, 0), + 1, + 1.into(), + tx_ext(0, 0), ); Executive::initialize_block(&Header::new_from_number(1)); @@ -889,9 +988,11 @@ fn event_from_runtime_upgrade_is_included() { /// used through the `ExecuteBlock` trait. #[test] fn custom_runtime_upgrade_is_called_when_using_execute_block_trait() { - let xt = TestXt::new( + let xt = UncheckedXt::new_signed( RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), + 1, + 1.into(), + tx_ext(0, 0), ); let header = new_test_ext(1).execute_with(|| { @@ -919,7 +1020,10 @@ fn custom_runtime_upgrade_is_called_when_using_execute_block_trait() { *v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() } }); - >>::execute_block(Block::new(header, vec![xt])); + >>::execute_block(Block::new( + header, + vec![xt], + )); assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); @@ -985,7 +1089,7 @@ fn offchain_worker_works_as_expected() { #[test] fn calculating_storage_root_twice_works() { let call = RuntimeCall::Custom(custom::Call::calculate_storage_root {}); - let xt = TestXt::new(call, sign_extra(1, 0, 0)); + let xt = UncheckedXt::new_signed(call, 1, 1.into(), tx_ext(0, 0)); let header = new_test_ext(1).execute_with(|| { // Let's build some fake block. @@ -1004,11 +1108,13 @@ fn calculating_storage_root_twice_works() { #[test] #[should_panic(expected = "Invalid inherent position for extrinsic at index 1")] fn invalid_inherent_position_fail() { - let xt1 = TestXt::new( + let xt1 = UncheckedXt::new_signed( RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 33, value: 0 }), - sign_extra(1, 0, 0), + 1, + 1.into(), + tx_ext(0, 0), ); - let xt2 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + let xt2 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); let header = new_test_ext(1).execute_with(|| { // Let's build some fake block. @@ -1027,8 +1133,8 @@ fn invalid_inherent_position_fail() { #[test] fn valid_inherents_position_works() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); - let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); let header = new_test_ext(1).execute_with(|| { // Let's build some fake block. @@ -1048,7 +1154,12 @@ fn valid_inherents_position_works() { #[test] #[should_panic(expected = "A call was labelled as mandatory, but resulted in an Error.")] fn invalid_inherents_fail_block_execution() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), sign_extra(1, 0, 0)); + let xt1 = UncheckedXt::new_signed( + RuntimeCall::Custom(custom::Call::inherent {}), + 1, + 1.into(), + tx_ext(0, 0), + ); new_test_ext(1).execute_with(|| { Executive::execute_block(Block::new( @@ -1061,7 +1172,7 @@ fn invalid_inherents_fail_block_execution() { // Inherents are created by the runtime and don't need to be validated. #[test] fn inherents_fail_validate_block() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); new_test_ext(1).execute_with(|| { assert_eq!( @@ -1075,7 +1186,7 @@ fn inherents_fail_validate_block() { /// Inherents still work while `initialize_block` forbids transactions. #[test] fn inherents_ok_while_exts_forbidden_works() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); let header = new_test_ext(1).execute_with(|| { Executive::initialize_block(&Header::new_from_number(1)); @@ -1095,8 +1206,8 @@ fn inherents_ok_while_exts_forbidden_works() { #[test] #[should_panic = "Only inherents are allowed in this block"] fn transactions_in_only_inherents_block_errors() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); - let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); let header = new_test_ext(1).execute_with(|| { Executive::initialize_block(&Header::new_from_number(1)); @@ -1116,8 +1227,8 @@ fn transactions_in_only_inherents_block_errors() { /// Same as above but no error. #[test] fn transactions_in_normal_block_works() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); - let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); let header = new_test_ext(1).execute_with(|| { Executive::initialize_block(&Header::new_from_number(1)); @@ -1137,8 +1248,8 @@ fn transactions_in_normal_block_works() { #[test] #[cfg(feature = "try-runtime")] fn try_execute_block_works() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); - let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); let header = new_test_ext(1).execute_with(|| { Executive::initialize_block(&Header::new_from_number(1)); @@ -1165,8 +1276,8 @@ fn try_execute_block_works() { #[cfg(feature = "try-runtime")] #[should_panic = "Only inherents allowed"] fn try_execute_tx_forbidden_errors() { - let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); - let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + let xt1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); let header = new_test_ext(1).execute_with(|| { // Let's build some fake block. @@ -1193,9 +1304,9 @@ fn try_execute_tx_forbidden_errors() { /// Check that `ensure_inherents_are_first` reports the correct indices. #[test] fn ensure_inherents_are_first_works() { - let in1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None); - let in2 = TestXt::new(RuntimeCall::Custom2(custom2::Call::inherent {}), None); - let xt2 = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + let in1 = UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})); + let in2 = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::inherent {})); + let xt2 = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); // Mocked empty header: let header = new_test_ext(1).execute_with(|| { @@ -1273,18 +1384,20 @@ fn callbacks_in_block_execution_works_inner(mbms_active: bool) { for i in 0..n_in { let xt = if i % 2 == 0 { - TestXt::new(RuntimeCall::Custom(custom::Call::inherent {}), None) + UncheckedXt::new_bare(RuntimeCall::Custom(custom::Call::inherent {})) } else { - TestXt::new(RuntimeCall::Custom2(custom2::Call::optional_inherent {}), None) + UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::optional_inherent {})) }; Executive::apply_extrinsic(xt.clone()).unwrap().unwrap(); extrinsics.push(xt); } for t in 0..n_tx { - let xt = TestXt::new( + let xt = UncheckedXt::new_signed( RuntimeCall::Custom2(custom2::Call::some_call {}), - sign_extra(1, t as u64, 0), + 1, + 1.into(), + tx_ext(t as u64, 0), ); // Extrinsics can be applied even when MBMs are active. Only the `execute_block` // will reject it. @@ -1324,8 +1437,13 @@ fn callbacks_in_block_execution_works_inner(mbms_active: bool) { #[test] fn post_inherent_called_after_all_inherents() { - let in1 = TestXt::new(RuntimeCall::Custom2(custom2::Call::inherent {}), None); - let xt1 = TestXt::new(RuntimeCall::Custom2(custom2::Call::some_call {}), sign_extra(1, 0, 0)); + let in1 = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::inherent {})); + let xt1 = UncheckedXt::new_signed( + RuntimeCall::Custom2(custom2::Call::some_call {}), + 1, + 1.into(), + tx_ext(0, 0), + ); let header = new_test_ext(1).execute_with(|| { // Let's build some fake block. @@ -1359,8 +1477,13 @@ fn post_inherent_called_after_all_inherents() { /// Regression test for AppSec finding #40. #[test] fn post_inherent_called_after_all_optional_inherents() { - let in1 = TestXt::new(RuntimeCall::Custom2(custom2::Call::optional_inherent {}), None); - let xt1 = TestXt::new(RuntimeCall::Custom2(custom2::Call::some_call {}), sign_extra(1, 0, 0)); + let in1 = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::optional_inherent {})); + let xt1 = UncheckedXt::new_signed( + RuntimeCall::Custom2(custom2::Call::some_call {}), + 1, + 1.into(), + tx_ext(0, 0), + ); let header = new_test_ext(1).execute_with(|| { // Let's build some fake block. @@ -1393,14 +1516,14 @@ fn post_inherent_called_after_all_optional_inherents() { #[test] fn is_inherent_works() { - let ext = TestXt::new(RuntimeCall::Custom2(custom2::Call::inherent {}), None); + let ext = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::inherent {})); assert!(Runtime::is_inherent(&ext)); - let ext = TestXt::new(RuntimeCall::Custom2(custom2::Call::optional_inherent {}), None); + let ext = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::optional_inherent {})); assert!(Runtime::is_inherent(&ext)); - let ext = TestXt::new(call_transfer(33, 0), sign_extra(1, 0, 0)); + let ext = UncheckedXt::new_signed(call_transfer(33, 0), 1, 1.into(), tx_ext(0, 0)); assert!(!Runtime::is_inherent(&ext)); - let ext = TestXt::new(RuntimeCall::Custom2(custom2::Call::allowed_unsigned {}), None); + let ext = UncheckedXt::new_bare(RuntimeCall::Custom2(custom2::Call::allowed_unsigned {})); assert!(!Runtime::is_inherent(&ext), "Unsigned ext are not automatically inherents"); } diff --git a/substrate/frame/grandpa/src/equivocation.rs b/substrate/frame/grandpa/src/equivocation.rs index b213c1ceb72..2366c957e9a 100644 --- a/substrate/frame/grandpa/src/equivocation.rs +++ b/substrate/frame/grandpa/src/equivocation.rs @@ -110,7 +110,7 @@ impl Offence for EquivocationOffence { /// /// This type implements `OffenceReportSystem` such that: /// - Equivocation reports are published on-chain as unsigned extrinsic via -/// `offchain::SendTransactionTypes`. +/// `offchain::CreateTransactionBase`. /// - On-chain validity checks and processing are mostly delegated to the user provided generic /// types implementing `KeyOwnerProofSystem` and `ReportOffence` traits. /// - Offence reporter for unsigned transactions is fetched via the the authorship pallet. @@ -122,7 +122,7 @@ impl (EquivocationProof>, T::KeyOwnerProof), > for EquivocationReportSystem where - T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, + T: Config + pallet_authorship::Config + frame_system::offchain::CreateInherent>, R: ReportOffence< T::AccountId, P::IdentificationTuple, @@ -144,7 +144,8 @@ where equivocation_proof: Box::new(equivocation_proof), key_owner_proof, }; - let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + let xt = T::create_inherent(call.into()); + let res = SubmitTransaction::>::submit_transaction(xt); match res { Ok(_) => info!(target: LOG_TARGET, "Submitted equivocation report"), Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index caac4107cfb..cf4c29003a7 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -72,14 +72,23 @@ impl frame_system::Config for Test { type AccountData = pallet_balances::AccountData; } -impl frame_system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; type Extrinsic = TestXt; } +impl frame_system::offchain::CreateInherent for Test +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + TestXt::new_bare(call) + } +} + parameter_types! { pub const Period: u64 = 1; pub const Offset: u64 = 0; diff --git a/substrate/frame/grandpa/src/tests.rs b/substrate/frame/grandpa/src/tests.rs index 8b12d63adaa..e1e963ce564 100644 --- a/substrate/frame/grandpa/src/tests.rs +++ b/substrate/frame/grandpa/src/tests.rs @@ -882,7 +882,7 @@ fn valid_equivocation_reports_dont_pay_fees() { .get_dispatch_info(); // it should have non-zero weight and the fee has to be paid. - assert!(info.weight.any_gt(Weight::zero())); + assert!(info.call_weight.any_gt(Weight::zero())); assert_eq!(info.pays_fee, Pays::Yes); // report the equivocation. diff --git a/substrate/frame/im-online/src/lib.rs b/substrate/frame/im-online/src/lib.rs index ee2a8451d6f..74d3bc6484d 100644 --- a/substrate/frame/im-online/src/lib.rs +++ b/substrate/frame/im-online/src/lib.rs @@ -95,7 +95,7 @@ use frame_support::{ BoundedSlice, WeakBoundedVec, }; use frame_system::{ - offchain::{SendTransactionTypes, SubmitTransaction}, + offchain::{CreateInherent, SubmitTransaction}, pallet_prelude::*, }; pub use pallet::*; @@ -261,7 +261,7 @@ pub mod pallet { pub struct Pallet(_); #[pallet::config] - pub trait Config: SendTransactionTypes> + frame_system::Config { + pub trait Config: CreateInherent> + frame_system::Config { /// The identifier type for an authority. type AuthorityId: Member + Parameter @@ -642,7 +642,8 @@ impl Pallet { call, ); - SubmitTransaction::>::submit_unsigned_transaction(call.into()) + let xt = T::create_inherent(call.into()); + SubmitTransaction::>::submit_transaction(xt) .map_err(|_| OffchainErr::SubmitTransaction)?; Ok(()) diff --git a/substrate/frame/im-online/src/mock.rs b/substrate/frame/im-online/src/mock.rs index 882581702ea..a5d9a6e20e6 100644 --- a/substrate/frame/im-online/src/mock.rs +++ b/substrate/frame/im-online/src/mock.rs @@ -25,11 +25,7 @@ use frame_support::{ weights::Weight, }; use pallet_session::historical as pallet_session_historical; -use sp_runtime::{ - testing::{TestXt, UintAuthorityId}, - traits::ConvertInto, - BuildStorage, Permill, -}; +use sp_runtime::{testing::UintAuthorityId, traits::ConvertInto, BuildStorage, Permill}; use sp_staking::{ offence::{OffenceError, ReportOffence}, SessionIndex, @@ -77,7 +73,7 @@ impl pallet_session::historical::SessionManager for TestSessionManager } /// An extrinsic type used for tests. -pub type Extrinsic = TestXt; +pub type Extrinsic = sp_runtime::testing::TestXt; type IdentificationTuple = (u64, u64); type Offence = crate::UnresponsivenessOffence; @@ -191,14 +187,23 @@ impl Config for Runtime { type MaxPeerInHeartbeats = ConstU32<10_000>; } -impl frame_system::offchain::SendTransactionTypes for Runtime +impl frame_system::offchain::CreateTransactionBase for Runtime where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; type Extrinsic = Extrinsic; } +impl frame_system::offchain::CreateInherent for Runtime +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + Extrinsic::new_bare(call) + } +} + pub fn advance_session() { let now = System::block_number().max(1); System::set_block_number(now + 1); diff --git a/substrate/frame/im-online/src/tests.rs b/substrate/frame/im-online/src/tests.rs index 12333d59ef8..b9a2772da68 100644 --- a/substrate/frame/im-online/src/tests.rs +++ b/substrate/frame/im-online/src/tests.rs @@ -225,7 +225,7 @@ fn should_generate_heartbeats() { // check stuff about the transaction. let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap(); - let heartbeat = match ex.call { + let heartbeat = match ex.function { crate::mock::RuntimeCall::ImOnline(crate::Call::heartbeat { heartbeat, .. }) => heartbeat, e => panic!("Unexpected call: {:?}", e), @@ -339,7 +339,7 @@ fn should_not_send_a_report_if_already_online() { assert_eq!(pool_state.read().transactions.len(), 0); // check stuff about the transaction. let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap(); - let heartbeat = match ex.call { + let heartbeat = match ex.function { crate::mock::RuntimeCall::ImOnline(crate::Call::heartbeat { heartbeat, .. }) => heartbeat, e => panic!("Unexpected call: {:?}", e), diff --git a/substrate/frame/lottery/src/lib.rs b/substrate/frame/lottery/src/lib.rs index 0071b258fc4..6a15de55ebd 100644 --- a/substrate/frame/lottery/src/lib.rs +++ b/substrate/frame/lottery/src/lib.rs @@ -300,7 +300,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight( T::WeightInfo::buy_ticket() - .saturating_add(call.get_dispatch_info().weight) + .saturating_add(call.get_dispatch_info().call_weight) )] pub fn buy_ticket( origin: OriginFor, diff --git a/substrate/frame/metadata-hash-extension/src/lib.rs b/substrate/frame/metadata-hash-extension/src/lib.rs index d09acbfb3df..9bd092c8982 100644 --- a/substrate/frame/metadata-hash-extension/src/lib.rs +++ b/substrate/frame/metadata-hash-extension/src/lib.rs @@ -17,14 +17,14 @@ #![cfg_attr(not(feature = "std"), no_std)] -//! The [`CheckMetadataHash`] signed extension. +//! The [`CheckMetadataHash`] transaction extension. //! //! The extension for optionally checking the metadata hash. For information how it works and what //! it does exactly, see the docs of [`CheckMetadataHash`]. //! //! # Integration //! -//! As any signed extension you will need to add it to your runtime signed extensions: +//! As any transaction extension you will need to add it to your runtime transaction extensions: #![doc = docify::embed!("src/tests.rs", add_metadata_hash_extension)] //! As the extension requires the `RUNTIME_METADATA_HASH` environment variable to be present at //! compile time, it requires a little bit more setup. To have this environment variable available @@ -43,7 +43,8 @@ use frame_support::DebugNoBound; use frame_system::Config; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension}, + impl_tx_ext_default, + traits::TransactionExtension, transaction_validity::{TransactionValidityError, UnknownTransaction}, }; @@ -85,15 +86,15 @@ impl MetadataHash { /// This metadata hash should give users the confidence that what they build with an online wallet /// is the same they are signing with their offline wallet and then applying on chain. To ensure /// that the online wallet is not tricking the offline wallet into decoding and showing an incorrect -/// extrinsic, the offline wallet will include the metadata hash into the additional signed data and +/// extrinsic, the offline wallet will include the metadata hash into the extension implicit and /// the runtime will then do the same. If the metadata hash doesn't match, the signature /// verification will fail and thus, the transaction will be rejected. The RFC contains more details /// on how it works. /// /// The extension adds one byte (the `mode`) to the size of the extrinsic. This one byte is -/// controlling if the metadata hash should be added to the signed data or not. Mode `0` means that -/// the metadata hash is not added and thus, `None` is added to the signed data. Mode `1` means that -/// the metadata hash is added and thus, `Some(metadata_hash)` is added to the signed data. Further +/// controlling if the metadata hash should be added to the implicit or not. Mode `0` means that +/// the metadata hash is not added and thus, `None` is added to the implicit. Mode `1` means that +/// the metadata hash is added and thus, `Some(metadata_hash)` is added to the implicit. Further /// values of `mode` are reserved for future changes. /// /// The metadata hash is read from the environment variable `RUNTIME_METADATA_HASH`. This @@ -110,7 +111,7 @@ pub struct CheckMetadataHash { } impl CheckMetadataHash { - /// Creates new `SignedExtension` to check metadata hash. + /// Creates new `TransactionExtension` to check metadata hash. pub fn new(enable: bool) -> Self { Self { _phantom: core::marker::PhantomData, @@ -131,14 +132,10 @@ impl CheckMetadataHash { } } -impl SignedExtension for CheckMetadataHash { - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = Option<[u8; 32]>; - type Pre = (); +impl TransactionExtension for CheckMetadataHash { const IDENTIFIER: &'static str = "CheckMetadataHash"; - - fn additional_signed(&self) -> Result { + type Implicit = Option<[u8; 32]>; + fn implicit(&self) -> Result { let signed = match self.mode { Mode::Disabled => None, Mode::Enabled => match self.metadata_hash.hash() { @@ -149,20 +146,14 @@ impl SignedExtension for CheckMetadataHash { log::debug!( target: "runtime::metadata-hash", - "CheckMetadataHash::additional_signed => {:?}", + "CheckMetadataHash::implicit => {:?}", signed.as_ref().map(|h| array_bytes::bytes2hex("0x", h)), ); Ok(signed) } + type Val = (); + type Pre = (); - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } + impl_tx_ext_default!(T::RuntimeCall; weight validate prepare); } diff --git a/substrate/frame/metadata-hash-extension/src/tests.rs b/substrate/frame/metadata-hash-extension/src/tests.rs index f13eecfd94b..11a3345ee15 100644 --- a/substrate/frame/metadata-hash-extension/src/tests.rs +++ b/substrate/frame/metadata-hash-extension/src/tests.rs @@ -25,7 +25,7 @@ use frame_support::{ use merkleized_metadata::{generate_metadata_digest, ExtraInfo}; use sp_api::{Metadata, ProvideRuntimeApi}; use sp_runtime::{ - traits::{Extrinsic as _, SignedExtension}, + traits::{ExtrinsicLike, TransactionExtension}, transaction_validity::{TransactionSource, UnknownTransaction}, }; use sp_transaction_pool::runtime_api::TaggedTransactionQueue; @@ -51,7 +51,7 @@ impl frame_system::Config for Test { #[test] fn rejects_when_no_metadata_hash_was_passed() { let ext = CheckMetadataHash::::decode(&mut &1u8.encode()[..]).unwrap(); - assert_eq!(Err(UnknownTransaction::CannotLookup.into()), ext.additional_signed()); + assert_eq!(Err(UnknownTransaction::CannotLookup.into()), ext.implicit()); } #[test] @@ -92,7 +92,7 @@ fn ensure_check_metadata_works_on_real_extrinsics() { .metadata_hash(generate_metadata_hash(metadata)) .build(); // Ensure that the transaction is signed. - assert!(valid_transaction.is_signed().unwrap()); + assert!(!valid_transaction.is_bare()); runtime_api .validate_transaction(best_hash, TransactionSource::External, valid_transaction, best_hash) @@ -104,7 +104,7 @@ fn ensure_check_metadata_works_on_real_extrinsics() { .metadata_hash([10u8; 32]) .build(); // Ensure that the transaction is signed. - assert!(invalid_transaction.is_signed().unwrap()); + assert!(!invalid_transaction.is_bare()); assert_eq!( TransactionValidityError::from(InvalidTransaction::BadProof), @@ -132,8 +132,8 @@ mod docs { } } - /// The `SignedExtension` to the basic transaction logic. - pub type SignedExtra = ( + /// The `TransactionExtension` to the basic transaction logic. + pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -153,7 +153,7 @@ mod docs { /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - sp_runtime::generic::UncheckedExtrinsic; + sp_runtime::generic::UncheckedExtrinsic; } // Put here to not have it in the docs as well. diff --git a/substrate/frame/mixnet/src/lib.rs b/substrate/frame/mixnet/src/lib.rs index c0505a4f010..6579ed678ae 100644 --- a/substrate/frame/mixnet/src/lib.rs +++ b/substrate/frame/mixnet/src/lib.rs @@ -31,7 +31,7 @@ use frame_support::{ BoundedVec, }; use frame_system::{ - offchain::{SendTransactionTypes, SubmitTransaction}, + offchain::{CreateInherent, SubmitTransaction}, pallet_prelude::BlockNumberFor, }; pub use pallet::*; @@ -178,7 +178,7 @@ pub mod pallet { pub struct Pallet(_); #[pallet::config] - pub trait Config: frame_system::Config + SendTransactionTypes> { + pub trait Config: frame_system::Config + CreateInherent> { /// The maximum number of authorities per session. #[pallet::constant] type MaxAuthorities: Get; @@ -531,7 +531,8 @@ impl Pallet { return false }; let call = Call::register { registration, signature }; - match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { + let xt = T::create_inherent(call.into()); + match SubmitTransaction::>::submit_transaction(xt) { Ok(()) => true, Err(()) => { log::debug!( diff --git a/substrate/frame/multisig/src/lib.rs b/substrate/frame/multisig/src/lib.rs index 51c36773bda..8faae73c716 100644 --- a/substrate/frame/multisig/src/lib.rs +++ b/substrate/frame/multisig/src/lib.rs @@ -273,7 +273,7 @@ pub mod pallet { T::WeightInfo::as_multi_threshold_1(call.using_encoded(|c| c.len() as u32)) // AccountData for inner call origin accountdata. .saturating_add(T::DbWeight::get().reads_writes(1, 1)) - .saturating_add(dispatch_info.weight), + .saturating_add(dispatch_info.call_weight), dispatch_info.class, ) })] @@ -554,7 +554,7 @@ impl Pallet { if let Some(call) = maybe_call.filter(|_| approvals >= threshold) { // verify weight ensure!( - call.get_dispatch_info().weight.all_lte(max_weight), + call.get_dispatch_info().call_weight.all_lte(max_weight), Error::::MaxWeightTooLow ); diff --git a/substrate/frame/multisig/src/tests.rs b/substrate/frame/multisig/src/tests.rs index cfdd33f7dfc..4f8a7a44243 100644 --- a/substrate/frame/multisig/src/tests.rs +++ b/substrate/frame/multisig/src/tests.rs @@ -104,7 +104,7 @@ fn multisig_deposit_is_taken_and_returned() { assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, @@ -225,7 +225,7 @@ fn multisig_2_of_3_works() { assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; let hash = blake2_256(&call.encode()); assert_ok!(Multisig::approve_as_multi( RuntimeOrigin::signed(1), @@ -258,7 +258,7 @@ fn multisig_3_of_3_works() { assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; let hash = blake2_256(&call.encode()); assert_ok!(Multisig::approve_as_multi( RuntimeOrigin::signed(1), @@ -328,7 +328,7 @@ fn multisig_2_of_3_as_multi_works() { assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, @@ -360,9 +360,9 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call1 = call_transfer(6, 10); - let call1_weight = call1.get_dispatch_info().weight; + let call1_weight = call1.get_dispatch_info().call_weight; let call2 = call_transfer(7, 5); - let call2_weight = call2.get_dispatch_info().weight; + let call2_weight = call2.get_dispatch_info().call_weight; assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), @@ -411,7 +411,7 @@ fn multisig_2_of_3_cannot_reissue_same_call() { assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 10); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; let hash = blake2_256(&call.encode()); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), @@ -652,7 +652,7 @@ fn multisig_handles_no_preimage_after_all_approve() { assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; let hash = blake2_256(&call.encode()); assert_ok!(Multisig::approve_as_multi( RuntimeOrigin::signed(1), diff --git a/substrate/frame/offences/benchmarking/src/mock.rs b/substrate/frame/offences/benchmarking/src/mock.rs index e243ad0e718..efaec49a65b 100644 --- a/substrate/frame/offences/benchmarking/src/mock.rs +++ b/substrate/frame/offences/benchmarking/src/mock.rs @@ -112,8 +112,6 @@ parameter_types! { pub static ElectionsBounds: ElectionBounds = ElectionBoundsBuilder::default().build(); } -pub type Extrinsic = sp_runtime::testing::TestXt; - pub struct OnChainSeqPhragmen; impl onchain::Config for OnChainSeqPhragmen { type System = Test; @@ -157,12 +155,21 @@ impl pallet_offences::Config for Test { type OnOffenceHandler = Staking; } -impl frame_system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { - type Extrinsic = Extrinsic; - type OverarchingCall = RuntimeCall; + type Extrinsic = UncheckedExtrinsic; + type RuntimeCall = RuntimeCall; +} + +impl frame_system::offchain::CreateInherent for Test +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + UncheckedExtrinsic::new_bare(call) + } } impl crate::Config for Test {} diff --git a/substrate/frame/proxy/src/lib.rs b/substrate/frame/proxy/src/lib.rs index 016f2cf225e..c041880a59d 100644 --- a/substrate/frame/proxy/src/lib.rs +++ b/substrate/frame/proxy/src/lib.rs @@ -195,7 +195,7 @@ pub mod pallet { (T::WeightInfo::proxy(T::MaxProxies::get()) // AccountData for inner call origin accountdata. .saturating_add(T::DbWeight::get().reads_writes(1, 1)) - .saturating_add(di.weight), + .saturating_add(di.call_weight), di.class) })] pub fn proxy( @@ -487,7 +487,7 @@ pub mod pallet { (T::WeightInfo::proxy_announced(T::MaxPending::get(), T::MaxProxies::get()) // AccountData for inner call origin accountdata. .saturating_add(T::DbWeight::get().reads_writes(1, 1)) - .saturating_add(di.weight), + .saturating_add(di.call_weight), di.class) })] pub fn proxy_announced( diff --git a/substrate/frame/recovery/src/lib.rs b/substrate/frame/recovery/src/lib.rs index 69be4df971b..f8622880538 100644 --- a/substrate/frame/recovery/src/lib.rs +++ b/substrate/frame/recovery/src/lib.rs @@ -378,7 +378,7 @@ pub mod pallet { #[pallet::weight({ let dispatch_info = call.get_dispatch_info(); ( - T::WeightInfo::as_recovered().saturating_add(dispatch_info.weight), + T::WeightInfo::as_recovered().saturating_add(dispatch_info.call_weight), dispatch_info.class, )})] pub fn as_recovered( diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 36cd03e9dd6..78c8b192965 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -643,7 +643,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { run: impl FnOnce(&mut Self) -> DispatchResultWithPostInfo, ) -> Result { use frame_support::dispatch::extract_actual_weight; - let charged = self.charge_gas(runtime_cost(dispatch_info.weight))?; + let charged = self.charge_gas(runtime_cost(dispatch_info.call_weight))?; let result = run(self); let actual_weight = extract_actual_weight(&result, &dispatch_info); self.adjust_gas(charged, runtime_cost(actual_weight)); @@ -1835,7 +1835,7 @@ pub mod env { let execute_weight = <::Xcm as ExecuteController<_, _>>::WeightInfo::execute(); let weight = self.ext.gas_meter().gas_left().max(execute_weight); - let dispatch_info = DispatchInfo { weight, ..Default::default() }; + let dispatch_info = DispatchInfo { call_weight: weight, ..Default::default() }; self.call_dispatchable::( dispatch_info, diff --git a/substrate/frame/sassafras/src/lib.rs b/substrate/frame/sassafras/src/lib.rs index 285758afbe6..f6c409833e3 100644 --- a/substrate/frame/sassafras/src/lib.rs +++ b/substrate/frame/sassafras/src/lib.rs @@ -61,7 +61,7 @@ use frame_support::{ BoundedVec, WeakBoundedVec, }; use frame_system::{ - offchain::{SendTransactionTypes, SubmitTransaction}, + offchain::{CreateInherent, SubmitTransaction}, pallet_prelude::BlockNumberFor, }; use sp_consensus_sassafras::{ @@ -131,7 +131,7 @@ pub mod pallet { /// Configuration parameters. #[pallet::config] - pub trait Config: frame_system::Config + SendTransactionTypes> { + pub trait Config: frame_system::Config + CreateInherent> { /// Amount of slots that each epoch should last. #[pallet::constant] type EpochLength: Get; @@ -1020,7 +1020,8 @@ impl Pallet { pub fn submit_tickets_unsigned_extrinsic(tickets: Vec) -> bool { let tickets = BoundedVec::truncate_from(tickets); let call = Call::submit_tickets { tickets }; - match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { + let xt = T::create_inherent(call.into()); + match SubmitTransaction::>::submit_transaction(xt) { Ok(_) => true, Err(e) => { error!(target: LOG_TARGET, "Error submitting tickets {:?}", e); diff --git a/substrate/frame/sassafras/src/mock.rs b/substrate/frame/sassafras/src/mock.rs index d2b329e8a2b..d7e2fb63dc2 100644 --- a/substrate/frame/sassafras/src/mock.rs +++ b/substrate/frame/sassafras/src/mock.rs @@ -34,7 +34,7 @@ use sp_core::{ H256, U256, }; use sp_runtime::{ - testing::{Digest, DigestItem, Header, TestXt}, + testing::{Digest, DigestItem, Header}, BuildStorage, }; @@ -48,12 +48,21 @@ impl frame_system::Config for Test { type Block = frame_system::mocking::MockBlock; } -impl frame_system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::CreateTransactionBase for Test where RuntimeCall: From, { - type OverarchingCall = RuntimeCall; - type Extrinsic = TestXt; + type RuntimeCall = RuntimeCall; + type Extrinsic = frame_system::mocking::MockUncheckedExtrinsic; +} + +impl frame_system::offchain::CreateInherent for Test +where + RuntimeCall: From, +{ + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + frame_system::mocking::MockUncheckedExtrinsic::::new_bare(call) + } } impl pallet_sassafras::Config for Test { diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index 3eecf6d6f9e..468099010bf 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -1364,7 +1364,7 @@ impl Pallet { Some(&RawOrigin::Signed(_)) => T::WeightInfo::execute_dispatch_signed(), _ => T::WeightInfo::execute_dispatch_unsigned(), }; - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; // We only allow a scheduled call if it cannot push the weight past the limit. let max_weight = base_weight.saturating_add(call_weight); diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index e5fb15cdd07..fcd96b40c3c 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -291,8 +291,8 @@ pub mod runtime { /// The block type, which should be fed into [`frame_system::Config`]. /// - /// Should be parameterized with `T: frame_system::Config` and a tuple of `SignedExtension`. - /// When in doubt, use [`SystemSignedExtensionsOf`]. + /// Should be parameterized with `T: frame_system::Config` and a tuple of + /// `TransactionExtension`. When in doubt, use [`SystemTransactionExtensionsOf`]. // Note that this cannot be dependent on `T` for block-number because it would lead to a // circular dependency (self-referential generics). pub type BlockOf = generic::Block>; @@ -306,7 +306,7 @@ pub mod runtime { /// Default set of signed extensions exposed from the `frame_system`. /// /// crucially, this does NOT contain any tx-payment extension. - pub type SystemSignedExtensionsOf = ( + pub type SystemTransactionExtensionsOf = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, diff --git a/substrate/frame/sudo/src/benchmarking.rs b/substrate/frame/sudo/src/benchmarking.rs index dee7d09c9d0..ff34cc3a700 100644 --- a/substrate/frame/sudo/src/benchmarking.rs +++ b/substrate/frame/sudo/src/benchmarking.rs @@ -21,14 +21,25 @@ use super::*; use crate::Pallet; use alloc::{boxed::Box, vec}; use frame_benchmarking::v2::*; +use frame_support::dispatch::{DispatchInfo, GetDispatchInfo}; use frame_system::RawOrigin; +use sp_runtime::traits::{ + AsSystemOriginSigner, AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, +}; fn assert_last_event(generic_event: crate::Event) { let re: ::RuntimeEvent = generic_event.into(); frame_system::Pallet::::assert_last_event(re.into()); } -#[benchmarks(where ::RuntimeCall: From>)] +#[benchmarks(where + T: Send + Sync, + ::RuntimeCall: From>, + ::RuntimeCall: Dispatchable + GetDispatchInfo, + <::RuntimeCall as Dispatchable>::PostInfo: Default, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: + AsSystemOriginSigner + AsTransactionAuthorizedOrigin + Clone, +)] mod benchmarks { use super::*; @@ -86,5 +97,26 @@ mod benchmarks { assert_last_event::(Event::KeyRemoved {}); } + #[benchmark] + fn check_only_sudo_account() { + let caller: T::AccountId = whitelisted_caller(); + Key::::put(&caller); + + let call: ::RuntimeCall = + frame_system::Call::remark { remark: vec![] }.into(); + let info = call.get_dispatch_info(); + let ext = CheckOnlySudoAccount::::new(); + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok( + Default::default() + )) + .unwrap() + .is_ok()); + } + } + impl_benchmark_test_suite!(Pallet, crate::mock::new_bench_ext(), crate::mock::Test); } diff --git a/substrate/frame/sudo/src/extension.rs b/substrate/frame/sudo/src/extension.rs index fb7eaf78948..573de45ba32 100644 --- a/substrate/frame/sudo/src/extension.rs +++ b/substrate/frame/sudo/src/extension.rs @@ -21,10 +21,11 @@ use core::{fmt, marker::PhantomData}; use frame_support::{dispatch::DispatchInfo, ensure}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, SignedExtension}, + impl_tx_ext_default, + traits::{AsSystemOriginSigner, DispatchInfoOf, Dispatchable, TransactionExtension}, transaction_validity::{ - InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError, - UnknownTransaction, ValidTransaction, + InvalidTransaction, TransactionPriority, TransactionValidityError, UnknownTransaction, + ValidTransaction, }, }; @@ -59,49 +60,61 @@ impl fmt::Debug for CheckOnlySudoAccount { } impl CheckOnlySudoAccount { - /// Creates new `SignedExtension` to check sudo key. + /// Creates new `TransactionExtension` to check sudo key. pub fn new() -> Self { Self::default() } } -impl SignedExtension for CheckOnlySudoAccount +impl TransactionExtension<::RuntimeCall> + for CheckOnlySudoAccount where - ::RuntimeCall: Dispatchable, + ::RuntimeCall: Dispatchable, + <::RuntimeCall as Dispatchable>::RuntimeOrigin: + AsSystemOriginSigner + Clone, { const IDENTIFIER: &'static str = "CheckOnlySudoAccount"; - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = (); + type Implicit = (); type Pre = (); + type Val = (); - fn additional_signed(&self) -> Result { - Ok(()) + fn weight( + &self, + _: &::RuntimeCall, + ) -> frame_support::weights::Weight { + use crate::weights::WeightInfo; + T::WeightInfo::check_only_sudo_account() } fn validate( &self, - who: &Self::AccountId, - _call: &Self::Call, - info: &DispatchInfoOf, + origin: <::RuntimeCall as Dispatchable>::RuntimeOrigin, + _call: &::RuntimeCall, + info: &DispatchInfoOf<::RuntimeCall>, _len: usize, - ) -> TransactionValidity { + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + ( + ValidTransaction, + Self::Val, + <::RuntimeCall as Dispatchable>::RuntimeOrigin, + ), + TransactionValidityError, + > { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; let sudo_key: T::AccountId = Key::::get().ok_or(UnknownTransaction::CannotLookup)?; ensure!(*who == sudo_key, InvalidTransaction::BadSigner); - Ok(ValidTransaction { - priority: info.weight.ref_time() as TransactionPriority, - ..Default::default() - }) + Ok(( + ValidTransaction { + priority: info.total_weight().ref_time() as TransactionPriority, + ..Default::default() + }, + (), + origin, + )) } - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } + impl_tx_ext_default!(::RuntimeCall; prepare); } diff --git a/substrate/frame/sudo/src/lib.rs b/substrate/frame/sudo/src/lib.rs index 07296e90b64..66616bf801e 100644 --- a/substrate/frame/sudo/src/lib.rs +++ b/substrate/frame/sudo/src/lib.rs @@ -85,8 +85,8 @@ //! meant to be used by constructing runtime calls from outside the runtime. //! //! -//! This pallet also defines a [`SignedExtension`](sp_runtime::traits::SignedExtension) called -//! [`CheckOnlySudoAccount`] to ensure that only signed transactions by the sudo account are +//! This pallet also defines a [`TransactionExtension`](sp_runtime::traits::TransactionExtension) +//! called [`CheckOnlySudoAccount`] to ensure that only signed transactions by the sudo account are //! accepted by the transaction pool. The intended use of this signed extension is to prevent other //! accounts from spamming the transaction pool for the initial phase of a chain, during which //! developers may only want a sudo account to be able to make transactions. @@ -197,7 +197,7 @@ pub mod pallet { #[pallet::weight({ let dispatch_info = call.get_dispatch_info(); ( - T::WeightInfo::sudo().saturating_add(dispatch_info.weight), + T::WeightInfo::sudo().saturating_add(dispatch_info.call_weight), dispatch_info.class ) })] @@ -262,7 +262,7 @@ pub mod pallet { #[pallet::weight({ let dispatch_info = call.get_dispatch_info(); ( - T::WeightInfo::sudo_as().saturating_add(dispatch_info.weight), + T::WeightInfo::sudo_as().saturating_add(dispatch_info.call_weight), dispatch_info.class, ) })] diff --git a/substrate/frame/sudo/src/tests.rs b/substrate/frame/sudo/src/tests.rs index 00bb86cc268..3ed3bd336f5 100644 --- a/substrate/frame/sudo/src/tests.rs +++ b/substrate/frame/sudo/src/tests.rs @@ -108,7 +108,7 @@ fn sudo_unchecked_weight_basics() { let sudo_unchecked_weight_call = SudoCall::sudo_unchecked_weight { call, weight: Weight::from_parts(1_000, 0) }; let info = sudo_unchecked_weight_call.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(1_000, 0)); + assert_eq!(info.call_weight, Weight::from_parts(1_000, 0)); }); } diff --git a/substrate/frame/sudo/src/weights.rs b/substrate/frame/sudo/src/weights.rs index c166ab442d7..ac5557e68a6 100644 --- a/substrate/frame/sudo/src/weights.rs +++ b/substrate/frame/sudo/src/weights.rs @@ -55,6 +55,7 @@ pub trait WeightInfo { fn sudo() -> Weight; fn sudo_as() -> Weight; fn remove_key() -> Weight; + fn check_only_sudo_account() -> Weight; } /// Weights for `pallet_sudo` using the Substrate node and recommended hardware. @@ -102,6 +103,16 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn check_only_sudo_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 3_416_000 picoseconds. + Weight::from_parts(3_645_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } } // For backwards compatibility and tests. @@ -148,4 +159,14 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: `Sudo::Key` (r:1 w:0) + /// Proof: `Sudo::Key` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + fn check_only_sudo_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 3_416_000 picoseconds. + Weight::from_parts(3_645_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } } diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index 9e9741ee161..18ca4a3acda 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -113,9 +113,7 @@ try-runtime = [ "sp-debug-derive/force-debug", "sp-runtime/try-runtime", ] -experimental = [ - "frame-support-procedural/experimental", -] +experimental = ["frame-support-procedural/experimental"] # By default some types have documentation, `no-metadata-docs` allows to reduce the documentation # in the metadata. no-metadata-docs = [ diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs index c5fe8440d21..e34c6ac5016 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs @@ -66,18 +66,16 @@ pub fn expand_outer_inherent( fn create_extrinsics(&self) -> #scrate::__private::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> { - use #scrate::inherent::ProvideInherent; + use #scrate::{inherent::ProvideInherent, traits::InherentBuilder}; let mut inherents = #scrate::__private::Vec::new(); #( #pallet_attrs if let Some(inherent) = #pallet_names::create_inherent(self) { - let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new( + let inherent = <#unchecked_extrinsic as InherentBuilder>::new_inherent( inherent.into(), - None, - ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return \ - `Some`; qed"); + ); inherents.push(inherent); } @@ -123,7 +121,7 @@ pub fn expand_outer_inherent( for xt in block.extrinsics() { // Inherents are before any other extrinsics. // And signed extrinsics are not inherents. - if #scrate::sp_runtime::traits::Extrinsic::is_signed(xt).unwrap_or(false) { + if !(#scrate::sp_runtime::traits::ExtrinsicLike::is_bare(xt)) { break } @@ -161,10 +159,9 @@ pub fn expand_outer_inherent( match #pallet_names::is_inherent_required(self) { Ok(Some(e)) => { let found = block.extrinsics().iter().any(|xt| { - let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) - .unwrap_or(false); + let is_bare = #scrate::sp_runtime::traits::ExtrinsicLike::is_bare(xt); - if !is_signed { + if is_bare { let call = < #unchecked_extrinsic as ExtrinsicCall >::call(xt); @@ -209,8 +206,9 @@ pub fn expand_outer_inherent( use #scrate::inherent::ProvideInherent; use #scrate::traits::{IsSubType, ExtrinsicCall}; - if #scrate::sp_runtime::traits::Extrinsic::is_signed(ext).unwrap_or(false) { - // Signed extrinsics are never inherents. + let is_bare = #scrate::sp_runtime::traits::ExtrinsicLike::is_bare(ext); + if !is_bare { + // Inherents must be bare extrinsics. return false } diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs index 54eb290ca6c..c12fc20bc8b 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -101,16 +101,16 @@ pub fn expand_runtime_metadata( let ty = #scrate::__private::scale_info::meta_type::<#extrinsic>(); let address_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureAddress + <#extrinsic as #scrate::traits::SignedTransactionBuilder>::Address >(); let call_ty = #scrate::__private::scale_info::meta_type::< - <#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::Call + <#extrinsic as #scrate::traits::ExtrinsicCall>::Call >(); let signature_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::Signature + <#extrinsic as #scrate::traits::SignedTransactionBuilder>::Signature >(); let extra_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureExtra + <#extrinsic as #scrate::traits::SignedTransactionBuilder>::Extension >(); #scrate::__private::metadata_ir::MetadataIR { @@ -122,16 +122,20 @@ pub fn expand_runtime_metadata( call_ty, signature_ty, extra_ty, - signed_extensions: < + extensions: < < #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata - >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension + >::TransactionExtensions + as + #scrate::sp_runtime::traits::TransactionExtension::< + <#runtime as #system_path::Config>::RuntimeCall + > >::metadata() .into_iter() - .map(|meta| #scrate::__private::metadata_ir::SignedExtensionMetadataIR { + .map(|meta| #scrate::__private::metadata_ir::TransactionExtensionMetadataIR { identifier: meta.identifier, ty: meta.ty, - additional_signed: meta.additional_signed, + implicit: meta.implicit, }) .collect(), }, diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs index 4a14853c04e..1c4ab436ad9 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs @@ -153,6 +153,10 @@ pub fn expand_outer_origin( self.filter = #scrate::__private::Rc::new(#scrate::__private::Box::new(filter)); } + fn set_caller(&mut self, caller: OriginCaller) { + self.caller = caller; + } + fn set_caller_from(&mut self, other: impl Into) { self.caller = other.into().caller; } @@ -301,6 +305,22 @@ pub fn expand_outer_origin( } } + impl #scrate::__private::AsSystemOriginSigner<<#runtime as #system_path::Config>::AccountId> for RuntimeOrigin { + fn as_system_origin_signer(&self) -> Option<&<#runtime as #system_path::Config>::AccountId> { + if let OriginCaller::system(#system_path::Origin::<#runtime>::Signed(ref signed)) = &self.caller { + Some(signed) + } else { + None + } + } + } + + impl #scrate::__private::AsTransactionAuthorizedOrigin for RuntimeOrigin { + fn is_transaction_authorized(&self) -> bool { + !matches!(&self.caller, OriginCaller::system(#system_path::Origin::<#runtime>::None)) + } + } + #pallet_conversions }) } diff --git a/substrate/frame/support/procedural/src/pallet/expand/call.rs b/substrate/frame/support/procedural/src/pallet/expand/call.rs index 206ffc1159f..87fb4b8967e 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/call.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/call.rs @@ -372,7 +372,8 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { >::pays_fee(&__pallet_base_weight, ( #( #args_name, )* )); #frame_support::dispatch::DispatchInfo { - weight: __pallet_weight, + call_weight: __pallet_weight, + extension_weight: Default::default(), class: __pallet_class, pays_fee: __pallet_pays_fee, } diff --git a/substrate/frame/support/src/dispatch.rs b/substrate/frame/support/src/dispatch.rs index 351ba3a15ef..3678f958980 100644 --- a/substrate/frame/support/src/dispatch.rs +++ b/substrate/frame/support/src/dispatch.rs @@ -26,7 +26,9 @@ use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_runtime::{ generic::{CheckedExtrinsic, UncheckedExtrinsic}, - traits::SignedExtension, + traits::{ + Dispatchable, ExtensionPostDispatchWeightHandler, RefundWeight, TransactionExtension, + }, DispatchError, RuntimeDebug, }; use sp_weights::Weight; @@ -236,14 +238,23 @@ impl<'a> OneOrMany for &'a [DispatchClass] { /// A bundle of static information collected from the `#[pallet::weight]` attributes. #[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo)] pub struct DispatchInfo { - /// Weight of this transaction. - pub weight: Weight, + /// Weight of this transaction's call. + pub call_weight: Weight, + /// Weight of this transaction's extension. + pub extension_weight: Weight, /// Class of this transaction. pub class: DispatchClass, /// Does this transaction pay fees. pub pays_fee: Pays, } +impl DispatchInfo { + /// Returns the weight used by this extrinsic's extension and call when applied. + pub fn total_weight(&self) -> Weight { + self.call_weight.saturating_add(self.extension_weight) + } +} + /// A `Dispatchable` function (aka transaction) that can carry some static information along with /// it, using the `#[pallet::weight]` attribute. pub trait GetDispatchInfo { @@ -268,7 +279,8 @@ pub fn extract_actual_weight(result: &DispatchResultWithPostInfo, info: &Dispatc .calc_actual_weight(info) } -/// Extract the actual pays_fee from a dispatch result if any or fall back to the default weight. +/// Extract the actual pays_fee from a dispatch result if any or fall back to the default +/// weight. pub fn extract_actual_pays_fee(result: &DispatchResultWithPostInfo, info: &DispatchInfo) -> Pays { match result { Ok(post_info) => post_info, @@ -290,15 +302,15 @@ pub struct PostDispatchInfo { impl PostDispatchInfo { /// Calculate how much (if any) weight was not used by the `Dispatchable`. pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight { - info.weight - self.calc_actual_weight(info) + info.total_weight() - self.calc_actual_weight(info) } /// Calculate how much weight was actually spent by the `Dispatchable`. pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight { if let Some(actual_weight) = self.actual_weight { - actual_weight.min(info.weight) + actual_weight.min(info.total_weight()) } else { - info.weight + info.total_weight() } } @@ -368,39 +380,28 @@ where } /// Implementation for unchecked extrinsic. -impl GetDispatchInfo - for UncheckedExtrinsic +impl> GetDispatchInfo + for UncheckedExtrinsic where - Call: GetDispatchInfo, - Extra: SignedExtension, + Call: GetDispatchInfo + Dispatchable, { fn get_dispatch_info(&self) -> DispatchInfo { - self.function.get_dispatch_info() + let mut info = self.function.get_dispatch_info(); + info.extension_weight = self.extension_weight(); + info } } /// Implementation for checked extrinsic. -impl GetDispatchInfo for CheckedExtrinsic +impl> GetDispatchInfo + for CheckedExtrinsic where Call: GetDispatchInfo, { fn get_dispatch_info(&self) -> DispatchInfo { - self.function.get_dispatch_info() - } -} - -/// Implementation for test extrinsic. -#[cfg(feature = "std")] -impl GetDispatchInfo - for sp_runtime::testing::TestXt -{ - fn get_dispatch_info(&self) -> DispatchInfo { - // for testing: weight == size. - DispatchInfo { - weight: Weight::from_parts(self.encode().len() as _, 0), - pays_fee: Pays::Yes, - class: self.call.get_dispatch_info().class, - } + let mut info = self.function.get_dispatch_info(); + info.extension_weight = self.extension_weight(); + info } } @@ -579,6 +580,28 @@ impl ClassifyDispatch for (Weight, DispatchClass, Pays) { } } +impl RefundWeight for PostDispatchInfo { + fn refund(&mut self, weight: Weight) { + if let Some(actual_weight) = self.actual_weight.as_mut() { + actual_weight.saturating_reduce(weight); + } + } +} + +impl ExtensionPostDispatchWeightHandler for PostDispatchInfo { + fn set_extension_weight(&mut self, info: &DispatchInfo) { + let actual_weight = self + .actual_weight + .unwrap_or(info.call_weight) + .saturating_add(info.extension_weight); + self.actual_weight = Some(actual_weight); + } +} + +impl ExtensionPostDispatchWeightHandler<()> for PostDispatchInfo { + fn set_extension_weight(&mut self, _: &()) {} +} + // TODO: Eventually remove these impl ClassifyDispatch for u64 { @@ -752,6 +775,19 @@ mod weight_tests { pub fn f21(_origin: OriginFor) -> DispatchResult { unimplemented!(); } + + #[pallet::weight(1000)] + pub fn f99(_origin: OriginFor) -> DispatchResult { + Ok(()) + } + + #[pallet::weight(1000)] + pub fn f100(_origin: OriginFor) -> DispatchResultWithPostInfo { + Ok(crate::dispatch::PostDispatchInfo { + actual_weight: Some(Weight::from_parts(500, 0)), + pays_fee: Pays::Yes, + }) + } } pub mod pallet_prelude { @@ -801,57 +837,61 @@ mod weight_tests { fn weights_are_correct() { // #[pallet::weight(1000)] let info = Call::::f00 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(1000, 0)); + assert_eq!(info.total_weight(), Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); // #[pallet::weight((1000, DispatchClass::Mandatory))] let info = Call::::f01 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(1000, 0)); + assert_eq!(info.total_weight(), Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Mandatory); assert_eq!(info.pays_fee, Pays::Yes); // #[pallet::weight((1000, Pays::No))] let info = Call::::f02 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(1000, 0)); + assert_eq!(info.total_weight(), Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::No); // #[pallet::weight((1000, DispatchClass::Operational, Pays::No))] let info = Call::::f03 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(1000, 0)); + assert_eq!(info.total_weight(), Weight::from_parts(1000, 0)); assert_eq!(info.class, DispatchClass::Operational); assert_eq!(info.pays_fee, Pays::No); // #[pallet::weight(((_a * 10 + _eb * 1) as u64, DispatchClass::Normal, Pays::Yes))] let info = Call::::f11 { a: 13, eb: 20 }.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(150, 0)); // 13*10 + 20 + assert_eq!(info.total_weight(), Weight::from_parts(150, 0)); // 13*10 + 20 assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); // #[pallet::weight((0, DispatchClass::Operational, Pays::Yes))] let info = Call::::f12 { a: 10, eb: 20 }.get_dispatch_info(); - assert_eq!(info.weight, Weight::zero()); + assert_eq!(info.total_weight(), Weight::zero()); assert_eq!(info.class, DispatchClass::Operational); assert_eq!(info.pays_fee, Pays::Yes); // #[pallet::weight(T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + // Weight::from_all(10_000))] let info = Call::::f20 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(12300, 10000)); // 100*3 + 1000*2 + 10_1000 + assert_eq!(info.total_weight(), Weight::from_parts(12300, 10000)); // 100*3 + 1000*2 + 10_1000 assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); // #[pallet::weight(T::DbWeight::get().reads_writes(6, 5) + Weight::from_all(40_000))] let info = Call::::f21 {}.get_dispatch_info(); - assert_eq!(info.weight, Weight::from_parts(45600, 40000)); // 100*6 + 1000*5 + 40_1000 + assert_eq!(info.total_weight(), Weight::from_parts(45600, 40000)); // 100*6 + 1000*5 + 40_1000 assert_eq!(info.class, DispatchClass::Normal); assert_eq!(info.pays_fee, Pays::Yes); } #[test] fn extract_actual_weight_works() { - let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; + let pre = DispatchInfo { + call_weight: Weight::from_parts(1000, 0), + extension_weight: Weight::zero(), + ..Default::default() + }; assert_eq!( extract_actual_weight(&Ok(from_actual_ref_time(Some(7))), &pre), Weight::from_parts(7, 0) @@ -871,7 +911,11 @@ mod weight_tests { #[test] fn extract_actual_weight_caps_at_pre_weight() { - let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; + let pre = DispatchInfo { + call_weight: Weight::from_parts(1000, 0), + extension_weight: Weight::zero(), + ..Default::default() + }; assert_eq!( extract_actual_weight(&Ok(from_actual_ref_time(Some(1250))), &pre), Weight::from_parts(1000, 0) @@ -887,7 +931,11 @@ mod weight_tests { #[test] fn extract_actual_pays_fee_works() { - let pre = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; + let pre = DispatchInfo { + call_weight: Weight::from_parts(1000, 0), + extension_weight: Weight::zero(), + ..Default::default() + }; assert_eq!(extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(7))), &pre), Pays::Yes); assert_eq!( extract_actual_pays_fee(&Ok(from_actual_ref_time(Some(1000)).into()), &pre), @@ -920,7 +968,8 @@ mod weight_tests { ); let pre = DispatchInfo { - weight: Weight::from_parts(1000, 0), + call_weight: Weight::from_parts(1000, 0), + extension_weight: Weight::zero(), pays_fee: Pays::No, ..Default::default() }; @@ -931,6 +980,26 @@ mod weight_tests { Pays::No ); } + + #[test] + fn weight_accrue_works() { + let mut post_dispatch = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(1100, 25)), + pays_fee: Pays::Yes, + }; + post_dispatch.refund(Weight::from_parts(100, 15)); + assert_eq!( + post_dispatch, + PostDispatchInfo { + actual_weight: Some(Weight::from_parts(1000, 10)), + pays_fee: Pays::Yes + } + ); + + let mut post_dispatch = PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }; + post_dispatch.refund(Weight::from_parts(100, 15)); + assert_eq!(post_dispatch, PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }); + } } #[cfg(test)] @@ -1107,3 +1176,407 @@ mod per_dispatch_class_tests { ); } } + +#[cfg(test)] +mod test_extensions { + use codec::{Decode, Encode}; + use scale_info::TypeInfo; + use sp_runtime::{ + impl_tx_ext_default, + traits::{ + DispatchInfoOf, DispatchOriginOf, Dispatchable, PostDispatchInfoOf, + TransactionExtension, + }, + transaction_validity::TransactionValidityError, + }; + use sp_weights::Weight; + + use super::{DispatchResult, PostDispatchInfo}; + + /// Test extension that refunds half its cost if the preset inner flag is set. + #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo)] + pub struct HalfCostIf(pub bool); + + impl TransactionExtension for HalfCostIf { + const IDENTIFIER: &'static str = "HalfCostIf"; + type Implicit = (); + type Val = (); + type Pre = bool; + + fn weight(&self, _: &RuntimeCall) -> sp_weights::Weight { + Weight::from_parts(100, 0) + } + + fn prepare( + self, + _val: Self::Val, + _origin: &DispatchOriginOf, + _call: &RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(self.0) + } + + fn post_dispatch_details( + pre: Self::Pre, + _info: &DispatchInfoOf, + _post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result { + if pre { + Ok(Weight::from_parts(50, 0)) + } else { + Ok(Weight::zero()) + } + } + impl_tx_ext_default!(RuntimeCall; validate); + } + + /// Test extension that refunds its cost if the actual post dispatch weight up until this point + /// in the extension pipeline is less than the preset inner `ref_time` amount. + #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo)] + pub struct FreeIfUnder(pub u64); + + impl TransactionExtension for FreeIfUnder + where + RuntimeCall: Dispatchable, + { + const IDENTIFIER: &'static str = "FreeIfUnder"; + type Implicit = (); + type Val = (); + type Pre = u64; + + fn weight(&self, _: &RuntimeCall) -> sp_weights::Weight { + Weight::from_parts(200, 0) + } + + fn prepare( + self, + _val: Self::Val, + _origin: &DispatchOriginOf, + _call: &RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(self.0) + } + + fn post_dispatch_details( + pre: Self::Pre, + _info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result { + if let Some(actual) = post_info.actual_weight { + if pre > actual.ref_time() { + return Ok(Weight::from_parts(200, 0)); + } + } + Ok(Weight::zero()) + } + impl_tx_ext_default!(RuntimeCall; validate); + } + + /// Test extension that sets its actual post dispatch `ref_time` weight to the preset inner + /// amount. + #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo)] + pub struct ActualWeightIs(pub u64); + + impl TransactionExtension for ActualWeightIs { + const IDENTIFIER: &'static str = "ActualWeightIs"; + type Implicit = (); + type Val = (); + type Pre = u64; + + fn weight(&self, _: &RuntimeCall) -> sp_weights::Weight { + Weight::from_parts(300, 0) + } + + fn prepare( + self, + _val: Self::Val, + _origin: &DispatchOriginOf, + _call: &RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(self.0) + } + + fn post_dispatch_details( + pre: Self::Pre, + _info: &DispatchInfoOf, + _post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result { + Ok(Weight::from_parts(300u64.saturating_sub(pre), 0)) + } + impl_tx_ext_default!(RuntimeCall; validate); + } +} + +#[cfg(test)] +// Do not complain about unused `dispatch` and `dispatch_aux`. +#[allow(dead_code)] +mod extension_weight_tests { + use crate::assert_ok; + + use super::*; + use sp_core::parameter_types; + use sp_runtime::{ + generic::{self, ExtrinsicFormat}, + traits::{Applyable, BlakeTwo256, DispatchTransaction, TransactionExtension}, + }; + use sp_weights::RuntimeDbWeight; + use test_extensions::{ActualWeightIs, FreeIfUnder, HalfCostIf}; + + use super::weight_tests::frame_system; + use frame_support::construct_runtime; + + pub type TxExtension = (HalfCostIf, FreeIfUnder, ActualWeightIs); + pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + pub type Header = generic::Header; + pub type Block = generic::Block; + pub type AccountId = u64; + pub type Balance = u32; + pub type BlockNumber = u32; + + construct_runtime!( + pub enum ExtRuntime { + System: frame_system, + } + ); + + impl frame_system::Config for ExtRuntime { + type Block = Block; + type AccountId = AccountId; + type Balance = Balance; + type BaseCallFilter = crate::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type DbWeight = DbWeight; + type PalletInfo = PalletInfo; + } + + parameter_types! { + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 100, + write: 1000, + }; + } + + pub struct ExtBuilder {} + + impl Default for ExtBuilder { + fn default() -> Self { + Self {} + } + } + + impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + let mut ext = sp_io::TestExternalities::new(Default::default()); + ext.execute_with(|| {}); + ext + } + + pub fn build_and_execute(self, test: impl FnOnce() -> ()) { + self.build().execute_with(|| { + test(); + }) + } + } + + #[test] + fn no_post_dispatch_with_no_refund() { + ExtBuilder::default().build_and_execute(|| { + let call = RuntimeCall::System(frame_system::Call::::f99 {}); + let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1500), ActualWeightIs(0)); + let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone()); + assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0)); + + let mut info = call.get_dispatch_info(); + assert_eq!(info.total_weight(), Weight::from_parts(1000, 0)); + info.extension_weight = ext.weight(&call); + let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0).unwrap(); + let res = call.dispatch(Some(0).into()); + let mut post_info = res.unwrap(); + assert!(post_info.actual_weight.is_none()); + assert_ok!(>::post_dispatch( + pre, + &info, + &mut post_info, + 0, + &Ok(()), + )); + assert!(post_info.actual_weight.is_none()); + }); + } + + #[test] + fn no_post_dispatch_refunds_when_dispatched() { + ExtBuilder::default().build_and_execute(|| { + let call = RuntimeCall::System(frame_system::Call::::f99 {}); + let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(100), ActualWeightIs(0)); + let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone()); + assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0)); + + let mut info = call.get_dispatch_info(); + assert_eq!(info.total_weight(), Weight::from_parts(1000, 0)); + info.extension_weight = ext.weight(&call); + let post_info = + ext.dispatch_transaction(Some(0).into(), call, &info, 0).unwrap().unwrap(); + // 1000 call weight + 50 + 200 + 0 + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1250, 0))); + }); + } + + #[test] + fn post_dispatch_with_refunds() { + ExtBuilder::default().build_and_execute(|| { + let call = RuntimeCall::System(frame_system::Call::::f100 {}); + // First testcase + let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(2000), ActualWeightIs(0)); + let uxt = UncheckedExtrinsic::new_signed(call.clone(), 0, (), ext.clone()); + assert_eq!(uxt.extension_weight(), Weight::from_parts(600, 0)); + + let mut info = call.get_dispatch_info(); + assert_eq!(info.call_weight, Weight::from_parts(1000, 0)); + info.extension_weight = ext.weight(&call); + assert_eq!(info.total_weight(), Weight::from_parts(1600, 0)); + let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0).unwrap(); + let res = call.clone().dispatch(Some(0).into()); + let mut post_info = res.unwrap(); + // 500 actual call weight + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0))); + // add the 600 worst case extension weight + post_info.set_extension_weight(&info); + // extension weight should be refunded + assert_ok!(>::post_dispatch( + pre, + &info, + &mut post_info, + 0, + &Ok(()), + )); + // 500 actual call weight + 100 + 0 + 0 + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(600, 0))); + + // Second testcase + let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1100), ActualWeightIs(200)); + let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0).unwrap(); + let res = call.clone().dispatch(Some(0).into()); + let mut post_info = res.unwrap(); + // 500 actual call weight + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0))); + // add the 600 worst case extension weight + post_info.set_extension_weight(&info); + // extension weight should be refunded + assert_ok!(>::post_dispatch( + pre, + &info, + &mut post_info, + 0, + &Ok(()), + )); + // 500 actual call weight + 100 + 200 + 200 + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1000, 0))); + + // Third testcase + let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(1060), ActualWeightIs(200)); + let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0).unwrap(); + let res = call.clone().dispatch(Some(0).into()); + let mut post_info = res.unwrap(); + // 500 actual call weight + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0))); + // add the 600 worst case extension weight + post_info.set_extension_weight(&info); + // extension weight should be refunded + assert_ok!(>::post_dispatch( + pre, + &info, + &mut post_info, + 0, + &Ok(()), + )); + // 500 actual call weight + 50 + 0 + 200 + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(750, 0))); + + // Fourth testcase + let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(100), ActualWeightIs(300)); + let (pre, _) = ext.validate_and_prepare(Some(0).into(), &call, &info, 0).unwrap(); + let res = call.clone().dispatch(Some(0).into()); + let mut post_info = res.unwrap(); + // 500 actual call weight + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(500, 0))); + // add the 600 worst case extension weight + post_info.set_extension_weight(&info); + // extension weight should be refunded + assert_ok!(>::post_dispatch( + pre, + &info, + &mut post_info, + 0, + &Ok(()), + )); + // 500 actual call weight + 100 + 200 + 300 + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1100, 0))); + }); + } + + #[test] + fn checked_extrinsic_apply() { + ExtBuilder::default().build_and_execute(|| { + let call = RuntimeCall::System(frame_system::Call::::f100 {}); + // First testcase + let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(2000), ActualWeightIs(0)); + let xt = CheckedExtrinsic { + format: ExtrinsicFormat::Signed(0, ext.clone()), + function: call.clone(), + }; + assert_eq!(xt.extension_weight(), Weight::from_parts(600, 0)); + let mut info = call.get_dispatch_info(); + assert_eq!(info.call_weight, Weight::from_parts(1000, 0)); + info.extension_weight = ext.weight(&call); + assert_eq!(info.total_weight(), Weight::from_parts(1600, 0)); + let post_info = xt.apply::(&info, 0).unwrap().unwrap(); + // 500 actual call weight + 100 + 0 + 0 + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(600, 0))); + + // Second testcase + let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(1100), ActualWeightIs(200)); + let xt = CheckedExtrinsic { + format: ExtrinsicFormat::Signed(0, ext), + function: call.clone(), + }; + let post_info = xt.apply::(&info, 0).unwrap().unwrap(); + // 500 actual call weight + 100 + 200 + 200 + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1000, 0))); + + // Third testcase + let ext: TxExtension = (HalfCostIf(true), FreeIfUnder(1060), ActualWeightIs(200)); + let xt = CheckedExtrinsic { + format: ExtrinsicFormat::Signed(0, ext), + function: call.clone(), + }; + let post_info = xt.apply::(&info, 0).unwrap().unwrap(); + // 500 actual call weight + 50 + 0 + 200 + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(750, 0))); + + // Fourth testcase + let ext: TxExtension = (HalfCostIf(false), FreeIfUnder(100), ActualWeightIs(300)); + let xt = CheckedExtrinsic { + format: ExtrinsicFormat::Signed(0, ext), + function: call.clone(), + }; + let post_info = xt.apply::(&info, 0).unwrap().unwrap(); + // 500 actual call weight + 100 + 200 + 300 + assert_eq!(post_info.actual_weight, Some(Weight::from_parts(1100, 0))); + }); + } +} diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 1f2ec71b191..cc805d72485 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -63,7 +63,8 @@ pub mod __private { #[cfg(feature = "std")] pub use sp_runtime::{bounded_btree_map, bounded_vec}; pub use sp_runtime::{ - traits::Dispatchable, DispatchError, RuntimeDebug, StateVersion, TransactionOutcome, + traits::{AsSystemOriginSigner, AsTransactionAuthorizedOrigin, Dispatchable}, + DispatchError, RuntimeDebug, StateVersion, TransactionOutcome, }; #[cfg(feature = "std")] pub use sp_state_machine::BasicExternalities; @@ -1695,8 +1696,8 @@ pub mod pallet_macros { /// [`ValidateUnsigned`](frame_support::pallet_prelude::ValidateUnsigned) for /// type `Pallet`, and some optional where clause. /// - /// NOTE: There is also the [`sp_runtime::traits::SignedExtension`] trait that can be used - /// to add some specific logic for transaction validation. + /// NOTE: There is also the [`sp_runtime::traits::TransactionExtension`] trait that can be + /// used to add some specific logic for transaction validation. /// /// ## Macro expansion /// diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 631225b9a32..635036d488d 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -60,10 +60,10 @@ pub use misc::{ AccountTouch, Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, DefensiveMax, DefensiveMin, DefensiveSaturating, DefensiveTruncateFrom, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee, - ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsInherent, - IsSubType, IsType, Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, - SameOrOther, Time, TryCollect, TryDrop, TypedGet, UnixTime, VariantCount, VariantCountOf, - WrapperKeepOpaque, WrapperOpaque, + ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, InherentBuilder, + IsInherent, IsSubType, IsType, Len, OffchainWorker, OnKilledAccount, OnNewAccount, + PrivilegeCmp, SameOrOther, SignedTransactionBuilder, Time, TryCollect, TryDrop, TypedGet, + UnixTime, VariantCount, VariantCountOf, WrapperKeepOpaque, WrapperOpaque, }; #[allow(deprecated)] pub use misc::{PreimageProvider, PreimageRecipient}; diff --git a/substrate/frame/support/src/traits/dispatch.rs b/substrate/frame/support/src/traits/dispatch.rs index 7dc8d3e4f5a..dbdf0885dd2 100644 --- a/substrate/frame/support/src/traits/dispatch.rs +++ b/substrate/frame/support/src/traits/dispatch.rs @@ -482,7 +482,7 @@ pub trait OriginTrait: Sized { type Call; /// The caller origin, overarching type of all pallets origins. - type PalletsOrigin: Into + CallerTrait + MaxEncodedLen; + type PalletsOrigin: Send + Sync + Into + CallerTrait + MaxEncodedLen; /// The AccountId used across the system. type AccountId; @@ -496,6 +496,14 @@ pub trait OriginTrait: Sized { /// Replace the caller with caller from the other origin fn set_caller_from(&mut self, other: impl Into); + /// Replace the caller with caller from the other origin + fn set_caller(&mut self, caller: Self::PalletsOrigin); + + /// Replace the caller with caller from the other origin + fn set_caller_from_signed(&mut self, caller_account: Self::AccountId) { + self.set_caller(Self::PalletsOrigin::from(RawOrigin::Signed(caller_account))) + } + /// Filter the call if caller is not root, if false is returned then the call must be filtered /// out. /// @@ -544,6 +552,17 @@ pub trait OriginTrait: Sized { fn as_system_ref(&self) -> Option<&RawOrigin> { self.caller().as_system_ref() } + + /// Extract a reference to the signer, if that's what the caller is. + fn as_signer(&self) -> Option<&Self::AccountId> { + self.caller().as_system_ref().and_then(|s| { + if let RawOrigin::Signed(ref who) = s { + Some(who) + } else { + None + } + }) + } } #[cfg(test)] diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs index 4d3b122daf6..a914b3a914c 100644 --- a/substrate/frame/support/src/traits/misc.rs +++ b/substrate/frame/support/src/traits/misc.rs @@ -919,32 +919,82 @@ pub trait IsInherent { } /// An extrinsic on which we can get access to call. -pub trait ExtrinsicCall: sp_runtime::traits::Extrinsic { +pub trait ExtrinsicCall: sp_runtime::traits::ExtrinsicLike { + type Call; + /// Get the call of the extrinsic. fn call(&self) -> &Self::Call; } -#[cfg(feature = "std")] -impl ExtrinsicCall for sp_runtime::testing::TestXt +impl ExtrinsicCall + for sp_runtime::generic::UncheckedExtrinsic where - Call: codec::Codec + Sync + Send + TypeInfo, + Address: TypeInfo, + Call: TypeInfo, + Signature: TypeInfo, Extra: TypeInfo, { - fn call(&self) -> &Self::Call { - &self.call + type Call = Call; + + fn call(&self) -> &Call { + &self.function } } -impl ExtrinsicCall +/// Interface for types capable of constructing an inherent extrinsic. +pub trait InherentBuilder: ExtrinsicCall { + /// Create a new inherent from a given call. + fn new_inherent(call: Self::Call) -> Self; +} + +impl InherentBuilder for sp_runtime::generic::UncheckedExtrinsic where Address: TypeInfo, Call: TypeInfo, Signature: TypeInfo, - Extra: sp_runtime::traits::SignedExtension + TypeInfo, + Extra: TypeInfo, { - fn call(&self) -> &Self::Call { - &self.function + fn new_inherent(call: Self::Call) -> Self { + Self::new_bare(call) + } +} + +/// Interface for types capable of constructing a signed transaction. +pub trait SignedTransactionBuilder: ExtrinsicCall { + type Address; + type Signature; + type Extension; + + /// Create a new signed transaction from a given call and extension using the provided signature + /// data. + fn new_signed_transaction( + call: Self::Call, + signed: Self::Address, + signature: Self::Signature, + tx_ext: Self::Extension, + ) -> Self; +} + +impl SignedTransactionBuilder + for sp_runtime::generic::UncheckedExtrinsic +where + Address: TypeInfo, + Call: TypeInfo, + Signature: TypeInfo, + Extension: TypeInfo, +{ + type Address = Address; + type Signature = Signature; + type Extension = Extension; + + fn new_signed_transaction( + call: Self::Call, + signed: Address, + signature: Signature, + tx_ext: Extension, + ) -> Self { + Self::new_signed(call, signed, signature, tx_ext) } } diff --git a/substrate/frame/support/test/Cargo.toml b/substrate/frame/support/test/Cargo.toml index 5c12c082305..2187ee22b39 100644 --- a/substrate/frame/support/test/Cargo.toml +++ b/substrate/frame/support/test/Cargo.toml @@ -59,10 +59,7 @@ std = [ "sp-version/std", "test-pallet/std", ] -experimental = [ - "frame-support/experimental", - "frame-system/experimental", -] +experimental = ["frame-support/experimental", "frame-system/experimental"] try-runtime = [ "frame-executive/try-runtime", "frame-support/try-runtime", diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr index d8dc7bd45bc..9608fa58e3c 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_inherent_part.stderr @@ -32,8 +32,9 @@ error[E0599]: no function or associated item named `create_inherent` found for s | |_^ function or associated item not found in `Pallet` | = help: items from traits can only be used if the trait is implemented and in scope - = note: the following trait defines an item `create_inherent`, perhaps you need to implement it: - candidate #1: `ProvideInherent` + = note: the following traits define an item `create_inherent`, perhaps you need to implement one of them: + candidate #1: `CreateInherent` + candidate #2: `ProvideInherent` = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0599]: no function or associated item named `is_inherent` found for struct `pallet::Pallet` in the current scope diff --git a/substrate/frame/support/test/tests/enum_deprecation.rs b/substrate/frame/support/test/tests/enum_deprecation.rs index ed9b2b5a735..c1167dfe339 100644 --- a/substrate/frame/support/test/tests/enum_deprecation.rs +++ b/substrate/frame/support/test/tests/enum_deprecation.rs @@ -132,8 +132,12 @@ impl pallet::Config for Runtime { pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; -pub type UncheckedExtrinsic = - sp_runtime::testing::TestXt>; +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic< + u64, + RuntimeCall, + sp_runtime::testing::UintAuthorityId, + frame_system::CheckNonZeroSender, +>; frame_support::construct_runtime!( pub struct Runtime { diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs index 3d6aa1d8374..b0b83f77249 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -27,19 +27,21 @@ use frame_support::{ storage::{unhashed, unhashed::contains_prefixed_key}, traits::{ ConstU32, GetCallIndex, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, - OnInitialize, OnRuntimeUpgrade, PalletError, PalletInfoAccess, StorageVersion, - UnfilteredDispatchable, + OnInitialize, OnRuntimeUpgrade, PalletError, PalletInfoAccess, SignedTransactionBuilder, + StorageVersion, UnfilteredDispatchable, }, weights::{RuntimeDbWeight, Weight}, OrdNoBound, PartialOrdNoBound, }; +use frame_system::offchain::{CreateSignedTransaction, CreateTransactionBase, SigningTypes}; use scale_info::{meta_type, TypeInfo}; use sp_io::{ hashing::{blake2_128, twox_128, twox_64}, TestExternalities, }; use sp_runtime::{ - traits::{Dispatchable, Extrinsic as ExtrinsicT, SignaturePayload as SignaturePayloadT}, + testing::UintAuthorityId, + traits::{Block as BlockT, Dispatchable}, DispatchError, ModuleError, }; @@ -751,8 +753,51 @@ impl pallet5::Config for Runtime { pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; -pub type UncheckedExtrinsic = - sp_runtime::testing::TestXt>; +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic< + u64, + RuntimeCall, + UintAuthorityId, + frame_system::CheckNonZeroSender, +>; +pub type UncheckedSignaturePayload = sp_runtime::generic::UncheckedSignaturePayload< + u64, + UintAuthorityId, + frame_system::CheckNonZeroSender, +>; + +impl SigningTypes for Runtime { + type Public = UintAuthorityId; + type Signature = UintAuthorityId; +} + +impl CreateTransactionBase for Runtime +where + RuntimeCall: From, +{ + type RuntimeCall = RuntimeCall; + type Extrinsic = UncheckedExtrinsic; +} + +impl CreateSignedTransaction for Runtime +where + RuntimeCall: From, +{ + fn create_signed_transaction< + C: frame_system::offchain::AppCrypto, + >( + call: RuntimeCall, + _public: UintAuthorityId, + account: u64, + nonce: u64, + ) -> Option { + Some(UncheckedExtrinsic::new_signed( + call, + nonce, + account.into(), + frame_system::CheckNonZeroSender::new(), + )) + } +} frame_support::construct_runtime!( pub struct Runtime { @@ -814,7 +859,8 @@ fn call_expand() { assert_eq!( call_foo.get_dispatch_info(), DispatchInfo { - weight: frame_support::weights::Weight::from_parts(3, 0), + call_weight: frame_support::weights::Weight::from_parts(3, 0), + extension_weight: Default::default(), class: DispatchClass::Normal, pays_fee: Pays::Yes } @@ -902,10 +948,8 @@ fn inherent_expand() { let inherents = InherentData::new().create_extrinsics(); - let expected = vec![UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }]; + let expected = + vec![UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {}))]; assert_eq!(expected, inherents); let block = Block::new( @@ -917,14 +961,11 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {})), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 1, + bar: 0, + })), ], ); @@ -939,14 +980,11 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 0, bar: 0 }), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {})), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 0, + bar: 0, + })), ], ); @@ -960,10 +998,9 @@ fn inherent_expand() { BlakeTwo256::hash(b"test"), Digest::default(), ), - vec![UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), - signature: None, - }], + vec![UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_storage_layer { + foo: 0, + }))], ); let mut inherent = InherentData::new(); @@ -978,10 +1015,12 @@ fn inherent_expand() { BlakeTwo256::hash(b"test"), Digest::default(), ), - vec![UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: Some((1, Default::default())), - }], + vec![UncheckedExtrinsic::new_signed( + RuntimeCall::Example(pallet::Call::foo_no_post_info {}), + 1, + 1.into(), + Default::default(), + )], ); let mut inherent = InherentData::new(); @@ -997,14 +1036,13 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 1, + bar: 1, + })), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_storage_layer { + foo: 0, + })), ], ); @@ -1019,18 +1057,14 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_storage_layer { foo: 0 }), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 1, + bar: 1, + })), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_storage_layer { + foo: 0, + })), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {})), ], ); @@ -1045,18 +1079,17 @@ fn inherent_expand() { Digest::default(), ), vec![ - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 1 }), - signature: None, - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), - signature: Some((1, Default::default())), - }, - UncheckedExtrinsic { - call: RuntimeCall::Example(pallet::Call::foo_no_post_info {}), - signature: None, - }, + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo { + foo: 1, + bar: 1, + })), + UncheckedExtrinsic::new_signed( + RuntimeCall::Example(pallet::Call::foo { foo: 1, bar: 0 }), + 1, + 1.into(), + Default::default(), + ), + UncheckedExtrinsic::new_bare(RuntimeCall::Example(pallet::Call::foo_no_post_info {})), ], ); @@ -1838,18 +1871,22 @@ fn metadata() { } let extrinsic = ExtrinsicMetadata { - version: 4, + version: 5, signed_extensions: vec![SignedExtensionMetadata { identifier: "UnitSignedExtension", ty: meta_type::<()>(), additional_signed: meta_type::<()>(), }], - address_ty: meta_type::<<::SignaturePayload as SignaturePayloadT>::SignatureAddress>(), - call_ty: meta_type::<::Call>(), + address_ty: meta_type::< + <<::Block as BlockT>::Extrinsic as SignedTransactionBuilder>::Address + >(), + call_ty: meta_type::<>::RuntimeCall>(), signature_ty: meta_type::< - <::SignaturePayload as SignaturePayloadT>::Signature + <<::Block as BlockT>::Extrinsic as SignedTransactionBuilder>::Signature + >(), + extra_ty: meta_type::< + <<::Block as BlockT>::Extrinsic as SignedTransactionBuilder>::Extension >(), - extra_ty: meta_type::<<::SignaturePayload as SignaturePayloadT>::SignatureExtra>(), }; let outer_enums = OuterEnums { @@ -1929,21 +1966,28 @@ fn metadata_ir_pallet_runtime_docs() { fn extrinsic_metadata_ir_types() { let ir = Runtime::metadata_ir().extrinsic; - assert_eq!(meta_type::<<::SignaturePayload as SignaturePayloadT>::SignatureAddress>(), ir.address_ty); + assert_eq!( + meta_type::<<<::Block as BlockT>::Extrinsic as SignedTransactionBuilder>::Address>(), + ir.address_ty + ); assert_eq!(meta_type::(), ir.address_ty); - assert_eq!(meta_type::<::Call>(), ir.call_ty); + assert_eq!( + meta_type::<>::RuntimeCall>(), + ir.call_ty + ); assert_eq!(meta_type::(), ir.call_ty); assert_eq!( - meta_type::< - <::SignaturePayload as SignaturePayloadT>::Signature, - >(), + meta_type::<<<::Block as BlockT>::Extrinsic as SignedTransactionBuilder>::Signature>(), ir.signature_ty ); - assert_eq!(meta_type::<()>(), ir.signature_ty); + assert_eq!(meta_type::(), ir.signature_ty); - assert_eq!(meta_type::<<::SignaturePayload as SignaturePayloadT>::SignatureExtra>(), ir.extra_ty); + assert_eq!( + meta_type::<<<::Block as BlockT>::Extrinsic as SignedTransactionBuilder>::Extension>(), + ir.extra_ty + ); assert_eq!(meta_type::>(), ir.extra_ty); } diff --git a/substrate/frame/support/test/tests/pallet_instance.rs b/substrate/frame/support/test/tests/pallet_instance.rs index 09a49617044..2e4baae1db7 100644 --- a/substrate/frame/support/test/tests/pallet_instance.rs +++ b/substrate/frame/support/test/tests/pallet_instance.rs @@ -360,7 +360,8 @@ fn call_expand() { assert_eq!( call_foo.get_dispatch_info(), DispatchInfo { - weight: Weight::from_parts(3, 0), + call_weight: Weight::from_parts(3, 0), + extension_weight: Default::default(), class: DispatchClass::Normal, pays_fee: Pays::Yes } @@ -372,7 +373,8 @@ fn call_expand() { assert_eq!( call_foo.get_dispatch_info(), DispatchInfo { - weight: Weight::from_parts(3, 0), + call_weight: Weight::from_parts(3, 0), + extension_weight: Default::default(), class: DispatchClass::Normal, pays_fee: Pays::Yes } @@ -940,9 +942,9 @@ fn metadata() { let extrinsic = ExtrinsicMetadata { ty: scale_info::meta_type::(), - version: 4, + version: 5, signed_extensions: vec![SignedExtensionMetadata { - identifier: "UnitSignedExtension", + identifier: "UnitTransactionExtension", ty: scale_info::meta_type::<()>(), additional_signed: scale_info::meta_type::<()>(), }], diff --git a/substrate/frame/support/test/tests/runtime.rs b/substrate/frame/support/test/tests/runtime.rs index 06c2b5b7071..5335e08837e 100644 --- a/substrate/frame/support/test/tests/runtime.rs +++ b/substrate/frame/support/test/tests/runtime.rs @@ -25,7 +25,10 @@ use codec::MaxEncodedLen; use frame_support::{ derive_impl, parameter_types, traits::PalletInfo as _, weights::RuntimeDbWeight, }; -use frame_system::limits::{BlockLength, BlockWeights}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + DispatchEventInfo, +}; use scale_info::TypeInfo; use sp_core::sr25519; use sp_runtime::{ @@ -533,8 +536,13 @@ fn origin_codec() { fn event_codec() { use codec::Encode; - let event = - frame_system::Event::::ExtrinsicSuccess { dispatch_info: Default::default() }; + let event = frame_system::Event::::ExtrinsicSuccess { + dispatch_info: DispatchEventInfo { + weight: Default::default(), + class: Default::default(), + pays_fee: Default::default(), + }, + }; assert_eq!(RuntimeEvent::from(event).encode()[0], 30); let event = module1::Event::::A(test_pub()); @@ -624,7 +632,8 @@ fn call_weight_should_attach_to_call_enum() { assert_eq!( module3::Call::::operational {}.get_dispatch_info(), DispatchInfo { - weight: Weight::from_parts(5, 0), + call_weight: Weight::from_parts(5, 0), + extension_weight: Default::default(), class: DispatchClass::Operational, pays_fee: Pays::Yes }, @@ -633,7 +642,8 @@ fn call_weight_should_attach_to_call_enum() { assert_eq!( module3::Call::::aux_4 {}.get_dispatch_info(), DispatchInfo { - weight: Weight::from_parts(3, 0), + call_weight: Weight::from_parts(3, 0), + extension_weight: Default::default(), class: DispatchClass::Normal, pays_fee: Pays::Yes }, @@ -894,7 +904,7 @@ fn test_metadata() { ty: meta_type::(), version: 4, signed_extensions: vec![SignedExtensionMetadata { - identifier: "UnitSignedExtension", + identifier: "UnitTransactionExtension", ty: meta_type::<()>(), additional_signed: meta_type::<()>(), }], diff --git a/substrate/frame/support/test/tests/runtime_legacy_ordering.rs b/substrate/frame/support/test/tests/runtime_legacy_ordering.rs index 4233db21e20..7b92073a82b 100644 --- a/substrate/frame/support/test/tests/runtime_legacy_ordering.rs +++ b/substrate/frame/support/test/tests/runtime_legacy_ordering.rs @@ -25,7 +25,10 @@ use codec::MaxEncodedLen; use frame_support::{ derive_impl, parameter_types, traits::PalletInfo as _, weights::RuntimeDbWeight, }; -use frame_system::limits::{BlockLength, BlockWeights}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + DispatchEventInfo, +}; use scale_info::TypeInfo; use sp_core::sr25519; use sp_runtime::{ @@ -533,8 +536,13 @@ fn origin_codec() { fn event_codec() { use codec::Encode; - let event = - frame_system::Event::::ExtrinsicSuccess { dispatch_info: Default::default() }; + let event = frame_system::Event::::ExtrinsicSuccess { + dispatch_info: DispatchEventInfo { + weight: Default::default(), + class: Default::default(), + pays_fee: Default::default(), + }, + }; assert_eq!(RuntimeEvent::from(event).encode()[0], 30); let event = module1::Event::::A(test_pub()); @@ -624,7 +632,8 @@ fn call_weight_should_attach_to_call_enum() { assert_eq!( module3::Call::::operational {}.get_dispatch_info(), DispatchInfo { - weight: Weight::from_parts(5, 0), + call_weight: Weight::from_parts(5, 0), + extension_weight: Default::default(), class: DispatchClass::Operational, pays_fee: Pays::Yes }, @@ -633,7 +642,8 @@ fn call_weight_should_attach_to_call_enum() { assert_eq!( module3::Call::::aux_4 {}.get_dispatch_info(), DispatchInfo { - weight: Weight::from_parts(3, 0), + call_weight: Weight::from_parts(3, 0), + extension_weight: Default::default(), class: DispatchClass::Normal, pays_fee: Pays::Yes }, @@ -894,7 +904,7 @@ fn test_metadata() { ty: meta_type::(), version: 4, signed_extensions: vec![SignedExtensionMetadata { - identifier: "UnitSignedExtension", + identifier: "UnitTransactionExtension", ty: meta_type::<()>(), additional_signed: meta_type::<()>(), }], diff --git a/substrate/frame/system/benchmarking/src/extensions.rs b/substrate/frame/system/benchmarking/src/extensions.rs new file mode 100644 index 00000000000..3c6626030e2 --- /dev/null +++ b/substrate/frame/system/benchmarking/src/extensions.rs @@ -0,0 +1,248 @@ +// 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. + +// Benchmarks for System Extensions + +#![cfg(feature = "runtime-benchmarks")] + +use alloc::vec; +use frame_benchmarking::{account, v2::*, BenchmarkError}; +use frame_support::{ + dispatch::{DispatchClass, DispatchInfo, PostDispatchInfo}, + weights::Weight, +}; +use frame_system::{ + pallet_prelude::*, CheckGenesis, CheckMortality, CheckNonZeroSender, CheckNonce, + CheckSpecVersion, CheckTxVersion, CheckWeight, Config, ExtensionsWeightInfo, Pallet as System, + RawOrigin, +}; +use sp_runtime::{ + generic::Era, + traits::{ + AsSystemOriginSigner, AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, Get, + }, +}; + +pub struct Pallet(System); + +#[benchmarks(where + T: Send + Sync, + T::RuntimeCall: Dispatchable, + ::RuntimeOrigin: AsSystemOriginSigner + AsTransactionAuthorizedOrigin + Clone) +] +mod benchmarks { + use super::*; + + #[benchmark] + fn check_genesis() -> Result<(), BenchmarkError> { + let len = 0_usize; + let caller = account("caller", 0, 0); + let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + CheckGenesis::::new() + .test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + + Ok(()) + } + + #[benchmark] + fn check_mortality_mortal_transaction() -> Result<(), BenchmarkError> { + let len = 0_usize; + let ext = CheckMortality::::from(Era::mortal(16, 256)); + let block_number: BlockNumberFor = 17u32.into(); + System::::set_block_number(block_number); + let prev_block: BlockNumberFor = 16u32.into(); + let default_hash: T::Hash = Default::default(); + frame_system::BlockHash::::insert(prev_block, default_hash); + let caller = account("caller", 0, 0); + let info = DispatchInfo { + call_weight: Weight::from_parts(100, 0), + class: DispatchClass::Normal, + ..Default::default() + }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + Ok(()) + } + + #[benchmark] + fn check_mortality_immortal_transaction() -> Result<(), BenchmarkError> { + let len = 0_usize; + let ext = CheckMortality::::from(Era::immortal()); + let block_number: BlockNumberFor = 17u32.into(); + System::::set_block_number(block_number); + let prev_block: BlockNumberFor = 16u32.into(); + let default_hash: T::Hash = Default::default(); + frame_system::BlockHash::::insert(prev_block, default_hash); + let genesis_block: BlockNumberFor = 0u32.into(); + frame_system::BlockHash::::insert(genesis_block, default_hash); + let caller = account("caller", 0, 0); + let info = DispatchInfo { + call_weight: Weight::from_parts(100, 0), + class: DispatchClass::Normal, + ..Default::default() + }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + Ok(()) + } + + #[benchmark] + fn check_non_zero_sender() -> Result<(), BenchmarkError> { + let len = 0_usize; + let ext = CheckNonZeroSender::::new(); + let caller = account("caller", 0, 0); + let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + Ok(()) + } + + #[benchmark] + fn check_nonce() -> Result<(), BenchmarkError> { + let caller: T::AccountId = account("caller", 0, 0); + let mut info = frame_system::AccountInfo::default(); + info.nonce = 1u32.into(); + info.providers = 1; + let expected_nonce = info.nonce + 1u32.into(); + frame_system::Account::::insert(caller.clone(), info); + let len = 0_usize; + let ext = CheckNonce::::from(1u32.into()); + let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller.clone()).into(), &call, &info, len, |_| { + Ok(().into()) + }) + .unwrap() + .unwrap(); + } + + let updated_info = frame_system::Account::::get(caller.clone()); + assert_eq!(updated_info.nonce, expected_nonce); + Ok(()) + } + + #[benchmark] + fn check_spec_version() -> Result<(), BenchmarkError> { + let len = 0_usize; + let caller = account("caller", 0, 0); + let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + CheckSpecVersion::::new() + .test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + Ok(()) + } + + #[benchmark] + fn check_tx_version() -> Result<(), BenchmarkError> { + let len = 0_usize; + let caller = account("caller", 0, 0); + let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + + #[block] + { + CheckTxVersion::::new() + .test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(().into())) + .unwrap() + .unwrap(); + } + Ok(()) + } + + #[benchmark] + fn check_weight() -> Result<(), BenchmarkError> { + let caller = account("caller", 0, 0); + let base_extrinsic = ::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + let extension_weight = ::ExtensionsWeightInfo::check_weight(); + let info = DispatchInfo { + call_weight: Weight::from_parts(base_extrinsic.ref_time() * 5, 0), + extension_weight, + class: DispatchClass::Normal, + ..Default::default() + }; + let call: T::RuntimeCall = frame_system::Call::remark { remark: vec![] }.into(); + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(base_extrinsic.ref_time() * 2, 0)), + pays_fee: Default::default(), + }; + let len = 0_usize; + let base_extrinsic = ::BlockWeights::get() + .get(DispatchClass::Normal) + .base_extrinsic; + + let ext = CheckWeight::::new(); + + let initial_block_weight = Weight::from_parts(base_extrinsic.ref_time() * 2, 0); + frame_system::BlockWeight::::mutate(|current_weight| { + current_weight.set(Weight::zero(), DispatchClass::Mandatory); + current_weight.set(initial_block_weight, DispatchClass::Normal); + }); + + #[block] + { + ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, |_| Ok(post_info)) + .unwrap() + .unwrap(); + } + + assert_eq!( + System::::block_weight().total(), + initial_block_weight + + base_extrinsic + + post_info.actual_weight.unwrap().saturating_add(extension_weight), + ); + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test,); +} diff --git a/substrate/frame/system/benchmarking/src/lib.rs b/substrate/frame/system/benchmarking/src/lib.rs index f66d20ac8ae..dc3c7420317 100644 --- a/substrate/frame/system/benchmarking/src/lib.rs +++ b/substrate/frame/system/benchmarking/src/lib.rs @@ -20,6 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; +pub mod extensions; #[cfg(feature = "runtime-benchmarks")] pub mod inner; diff --git a/substrate/frame/system/benchmarking/src/mock.rs b/substrate/frame/system/benchmarking/src/mock.rs index 42e4aa0eaf4..6b126619ce5 100644 --- a/substrate/frame/system/benchmarking/src/mock.rs +++ b/substrate/frame/system/benchmarking/src/mock.rs @@ -20,7 +20,7 @@ #![cfg(test)] use codec::Encode; -use frame_support::derive_impl; +use frame_support::{derive_impl, weights::Weight}; use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; @@ -32,9 +32,45 @@ frame_support::construct_runtime!( } ); +pub struct MockWeights; +impl frame_system::ExtensionsWeightInfo for MockWeights { + fn check_genesis() -> Weight { + Weight::from_parts(10, 0) + } + + fn check_mortality_mortal_transaction() -> Weight { + Weight::from_parts(10, 0) + } + + fn check_mortality_immortal_transaction() -> Weight { + Weight::from_parts(10, 0) + } + + fn check_non_zero_sender() -> Weight { + Weight::from_parts(10, 0) + } + + fn check_nonce() -> Weight { + Weight::from_parts(10, 0) + } + + fn check_spec_version() -> Weight { + Weight::from_parts(10, 0) + } + + fn check_tx_version() -> Weight { + Weight::from_parts(10, 0) + } + + fn check_weight() -> Weight { + Weight::from_parts(10, 0) + } +} + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; + type ExtensionsWeightInfo = MockWeights; } impl crate::Config for Test {} diff --git a/substrate/frame/system/src/extensions/check_genesis.rs b/substrate/frame/system/src/extensions/check_genesis.rs index 000ec56da64..881faa2c0ea 100644 --- a/substrate/frame/system/src/extensions/check_genesis.rs +++ b/substrate/frame/system/src/extensions/check_genesis.rs @@ -19,7 +19,8 @@ use crate::{pallet_prelude::BlockNumberFor, Config, Pallet}; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension, Zero}, + impl_tx_ext_default, + traits::{TransactionExtension, Zero}, transaction_validity::TransactionValidityError, }; @@ -46,30 +47,24 @@ impl core::fmt::Debug for CheckGenesis { } impl CheckGenesis { - /// Creates new `SignedExtension` to check genesis hash. + /// Creates new `TransactionExtension` to check genesis hash. pub fn new() -> Self { Self(core::marker::PhantomData) } } -impl SignedExtension for CheckGenesis { - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = T::Hash; - type Pre = (); +impl TransactionExtension for CheckGenesis { const IDENTIFIER: &'static str = "CheckGenesis"; - - fn additional_signed(&self) -> Result { + type Implicit = T::Hash; + fn implicit(&self) -> Result { Ok(>::block_hash(BlockNumberFor::::zero())) } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + type Val = (); + type Pre = (); + fn weight(&self, _: &T::RuntimeCall) -> sp_weights::Weight { + // All transactions will always read the hash of the genesis block, so to avoid + // charging this multiple times in a block we manually set the proof size to 0. + ::check_genesis().set_proof_size(0) } + impl_tx_ext_default!(T::RuntimeCall; validate prepare); } diff --git a/substrate/frame/system/src/extensions/check_mortality.rs b/substrate/frame/system/src/extensions/check_mortality.rs index 6666c4812fb..7da5521f353 100644 --- a/substrate/frame/system/src/extensions/check_mortality.rs +++ b/substrate/frame/system/src/extensions/check_mortality.rs @@ -20,10 +20,9 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{ generic::Era, - traits::{DispatchInfoOf, SaturatedConversion, SignedExtension}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, - }, + impl_tx_ext_default, + traits::{DispatchInfoOf, SaturatedConversion, TransactionExtension, ValidateResult}, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, }; /// Check for transaction mortality. @@ -57,29 +56,11 @@ impl core::fmt::Debug for CheckMortality { } } -impl SignedExtension for CheckMortality { - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = T::Hash; - type Pre = (); +impl TransactionExtension for CheckMortality { const IDENTIFIER: &'static str = "CheckMortality"; + type Implicit = T::Hash; - fn validate( - &self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> TransactionValidity { - let current_u64 = >::block_number().saturated_into::(); - let valid_till = self.0.death(current_u64); - Ok(ValidTransaction { - longevity: valid_till.saturating_sub(current_u64), - ..Default::default() - }) - } - - fn additional_signed(&self) -> Result { + fn implicit(&self) -> Result { let current_u64 = >::block_number().saturated_into::(); let n = self.0.birth(current_u64).saturated_into::>(); if !>::contains_key(n) { @@ -88,16 +69,41 @@ impl SignedExtension for CheckMortality { Ok(>::block_hash(n)) } } + type Pre = (); + type Val = (); + + fn weight(&self, _: &T::RuntimeCall) -> sp_weights::Weight { + if self.0.is_immortal() { + // All immortal transactions will always read the hash of the genesis block, so to avoid + // charging this multiple times in a block we manually set the proof size to 0. + ::check_mortality_immortal_transaction() + .set_proof_size(0) + } else { + ::check_mortality_mortal_transaction() + } + } - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + fn validate( + &self, + origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let current_u64 = >::block_number().saturated_into::(); + let valid_till = self.0.death(current_u64); + Ok(( + ValidTransaction { + longevity: valid_till.saturating_sub(current_u64), + ..Default::default() + }, + (), + origin, + )) } + impl_tx_ext_default!(T::RuntimeCall; prepare); } #[cfg(test)] @@ -109,23 +115,21 @@ mod tests { weights::Weight, }; use sp_core::H256; + use sp_runtime::traits::DispatchTransaction; #[test] fn signed_ext_check_era_should_work() { new_test_ext().execute_with(|| { // future assert_eq!( - CheckMortality::::from(Era::mortal(4, 2)) - .additional_signed() - .err() - .unwrap(), + CheckMortality::::from(Era::mortal(4, 2)).implicit().err().unwrap(), InvalidTransaction::AncientBirthBlock.into(), ); // correct System::set_block_number(13); >::insert(12, H256::repeat_byte(1)); - assert!(CheckMortality::::from(Era::mortal(4, 12)).additional_signed().is_ok()); + assert!(CheckMortality::::from(Era::mortal(4, 12)).implicit().is_ok()); }) } @@ -133,7 +137,8 @@ mod tests { fn signed_ext_check_era_should_change_longevity() { new_test_ext().execute_with(|| { let normal = DispatchInfo { - weight: Weight::from_parts(100, 0), + call_weight: Weight::from_parts(100, 0), + extension_weight: Weight::zero(), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; @@ -145,7 +150,10 @@ mod tests { System::set_block_number(17); >::insert(16, H256::repeat_byte(1)); - assert_eq!(ext.validate(&1, CALL, &normal, len).unwrap().longevity, 15); + assert_eq!( + ext.validate_only(Some(1).into(), CALL, &normal, len).unwrap().0.longevity, + 15 + ); }) } } diff --git a/substrate/frame/system/src/extensions/check_non_zero_sender.rs b/substrate/frame/system/src/extensions/check_non_zero_sender.rs index 06dc2bf177a..ec8c12b790d 100644 --- a/substrate/frame/system/src/extensions/check_non_zero_sender.rs +++ b/substrate/frame/system/src/extensions/check_non_zero_sender.rs @@ -18,13 +18,12 @@ use crate::Config; use codec::{Decode, Encode}; use core::marker::PhantomData; -use frame_support::{dispatch::DispatchInfo, DefaultNoBound}; +use frame_support::{traits::OriginTrait, DefaultNoBound}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, SignedExtension}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, - }, + impl_tx_ext_default, + traits::{DispatchInfoOf, TransactionExtension}, + transaction_validity::InvalidTransaction, }; /// Check to ensure that the sender is not the zero address. @@ -45,66 +44,80 @@ impl core::fmt::Debug for CheckNonZeroSender { } impl CheckNonZeroSender { - /// Create new `SignedExtension` to check runtime version. + /// Create new `TransactionExtension` to check runtime version. pub fn new() -> Self { Self(core::marker::PhantomData) } } -impl SignedExtension for CheckNonZeroSender -where - T::RuntimeCall: Dispatchable, -{ - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); - type Pre = (); +impl TransactionExtension for CheckNonZeroSender { const IDENTIFIER: &'static str = "CheckNonZeroSender"; + type Implicit = (); + type Val = (); + type Pre = (); - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + fn weight(&self, _: &T::RuntimeCall) -> sp_weights::Weight { + ::check_non_zero_sender() } fn validate( &self, - who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { - if who.using_encoded(|d| d.iter().all(|x| *x == 0)) { - return Err(TransactionValidityError::Invalid(InvalidTransaction::BadSigner)) + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> sp_runtime::traits::ValidateResult { + if let Some(who) = origin.as_signer() { + if who.using_encoded(|d| d.iter().all(|x| *x == 0)) { + return Err(InvalidTransaction::BadSigner.into()) + } } - Ok(ValidTransaction::default()) + Ok((Default::default(), (), origin)) } + impl_tx_ext_default!(T::RuntimeCall; prepare); } #[cfg(test)] mod tests { use super::*; use crate::mock::{new_test_ext, Test, CALL}; - use frame_support::{assert_noop, assert_ok}; + use frame_support::{assert_ok, dispatch::DispatchInfo}; + use sp_runtime::{ + traits::{AsTransactionAuthorizedOrigin, DispatchTransaction}, + transaction_validity::TransactionValidityError, + }; #[test] fn zero_account_ban_works() { new_test_ext().execute_with(|| { let info = DispatchInfo::default(); let len = 0_usize; - assert_noop!( - CheckNonZeroSender::::new().validate(&0, CALL, &info, len), - InvalidTransaction::BadSigner + assert_eq!( + CheckNonZeroSender::::new() + .validate_only(Some(0).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::from(InvalidTransaction::BadSigner) ); - assert_ok!(CheckNonZeroSender::::new().validate(&1, CALL, &info, len)); + assert_ok!(CheckNonZeroSender::::new().validate_only( + Some(1).into(), + CALL, + &info, + len + )); + }) + } + + #[test] + fn unsigned_origin_works() { + new_test_ext().execute_with(|| { + let info = DispatchInfo::default(); + let len = 0_usize; + let (_, _, origin) = CheckNonZeroSender::::new() + .validate(None.into(), CALL, &info, len, (), CALL) + .unwrap(); + assert!(!origin.is_transaction_authorized()); }) } } diff --git a/substrate/frame/system/src/extensions/check_nonce.rs b/substrate/frame/system/src/extensions/check_nonce.rs index 3535870d1b5..d96d2c2c066 100644 --- a/substrate/frame/system/src/extensions/check_nonce.rs +++ b/substrate/frame/system/src/extensions/check_nonce.rs @@ -18,23 +18,32 @@ use crate::Config; use alloc::vec; use codec::{Decode, Encode}; -use frame_support::dispatch::DispatchInfo; +use frame_support::{dispatch::DispatchInfo, RuntimeDebugNoBound}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, One, SignedExtension, Zero}, + traits::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, One, PostDispatchInfoOf, + TransactionExtension, ValidateResult, Zero, + }, transaction_validity::{ - InvalidTransaction, TransactionLongevity, TransactionValidity, TransactionValidityError, - ValidTransaction, + InvalidTransaction, TransactionLongevity, TransactionValidityError, ValidTransaction, }, + DispatchResult, Saturating, }; +use sp_weights::Weight; /// Nonce check and increment to give replay protection for transactions. /// /// # Transaction Validity /// /// This extension affects `requires` and `provides` tags of validity, but DOES NOT -/// set the `priority` field. Make sure that AT LEAST one of the signed extension sets +/// set the `priority` field. Make sure that AT LEAST one of the transaction extension sets /// some kind of priority upon validating transactions. +/// +/// The preparation step assumes that the nonce information has not changed since the validation +/// step. This means that other extensions ahead of `CheckNonce` in the pipeline must not alter the +/// nonce during their own preparation step, or else the transaction may be rejected during dispatch +/// or lead to an inconsistent account state. #[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct CheckNonce(#[codec(compact)] pub T::Nonce); @@ -58,83 +67,122 @@ impl core::fmt::Debug for CheckNonce { } } -impl SignedExtension for CheckNonce +/// Operation to perform from `validate` to `prepare` in [`CheckNonce`] transaction extension. +#[derive(RuntimeDebugNoBound)] +pub enum Val { + /// Account and its nonce to check for. + CheckNonce((T::AccountId, T::Nonce)), + /// Weight to refund. + Refund(Weight), +} + +/// Operation to perform from `prepare` to `post_dispatch_details` in [`CheckNonce`] transaction +/// extension. +#[derive(RuntimeDebugNoBound)] +pub enum Pre { + /// The transaction extension weight should not be refunded. + NonceChecked, + /// The transaction extension weight should be refunded. + Refund(Weight), +} + +impl TransactionExtension for CheckNonce where T::RuntimeCall: Dispatchable, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, { - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); - type Pre = (); const IDENTIFIER: &'static str = "CheckNonce"; + type Implicit = (); + type Val = Val; + type Pre = Pre; - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result<(), TransactionValidityError> { - let mut account = crate::Account::::get(who); - if account.providers.is_zero() && account.sufficients.is_zero() { - // Nonce storage not paid for - return Err(InvalidTransaction::Payment.into()) - } - if self.0 != account.nonce { - return Err(if self.0 < account.nonce { - InvalidTransaction::Stale - } else { - InvalidTransaction::Future - } - .into()) - } - account.nonce += T::Nonce::one(); - crate::Account::::insert(who, account); - Ok(()) + fn weight(&self, _: &T::RuntimeCall) -> sp_weights::Weight { + ::check_nonce() } fn validate( &self, - who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &T::RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let Some(who) = origin.as_system_origin_signer() else { + return Ok((Default::default(), Val::Refund(self.weight(call)), origin)) + }; let account = crate::Account::::get(who); if account.providers.is_zero() && account.sufficients.is_zero() { // Nonce storage not paid for - return InvalidTransaction::Payment.into() + return Err(InvalidTransaction::Payment.into()) } if self.0 < account.nonce { - return InvalidTransaction::Stale.into() + return Err(InvalidTransaction::Stale.into()) } - let provides = vec![Encode::encode(&(who, self.0))]; + let provides = vec![Encode::encode(&(&who, self.0))]; let requires = if account.nonce < self.0 { - vec![Encode::encode(&(who, self.0 - One::one()))] + vec![Encode::encode(&(&who, self.0.saturating_sub(One::one())))] } else { vec![] }; - Ok(ValidTransaction { + let validity = ValidTransaction { priority: 0, requires, provides, longevity: TransactionLongevity::max_value(), propagate: true, - }) + }; + + Ok((validity, Val::CheckNonce((who.clone(), account.nonce)), origin)) + } + + fn prepare( + self, + val: Self::Val, + _origin: &T::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + let (who, mut nonce) = match val { + Val::CheckNonce((who, nonce)) => (who, nonce), + Val::Refund(weight) => return Ok(Pre::Refund(weight)), + }; + + // `self.0 < nonce` already checked in `validate`. + if self.0 > nonce { + return Err(InvalidTransaction::Future.into()) + } + nonce += T::Nonce::one(); + crate::Account::::mutate(who, |account| account.nonce = nonce); + Ok(Pre::NonceChecked) + } + + fn post_dispatch_details( + pre: Self::Pre, + _info: &DispatchInfo, + _post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result { + match pre { + Pre::NonceChecked => Ok(Weight::zero()), + Pre::Refund(weight) => Ok(weight), + } } } #[cfg(test)] mod tests { use super::*; - use crate::mock::{new_test_ext, Test, CALL}; - use frame_support::{assert_noop, assert_ok}; + use crate::mock::{new_test_ext, RuntimeCall, Test, CALL}; + use frame_support::{ + assert_ok, assert_storage_noop, dispatch::GetDispatchInfo, traits::OriginTrait, + }; + use sp_runtime::traits::{AsTransactionAuthorizedOrigin, DispatchTransaction}; #[test] fn signed_ext_check_nonce_works() { @@ -152,22 +200,45 @@ mod tests { let info = DispatchInfo::default(); let len = 0_usize; // stale - assert_noop!( - CheckNonce::(0u64.into()).validate(&1, CALL, &info, len), - InvalidTransaction::Stale - ); - assert_noop!( - CheckNonce::(0u64.into()).pre_dispatch(&1, CALL, &info, len), - InvalidTransaction::Stale - ); + assert_storage_noop!({ + assert_eq!( + CheckNonce::(0u64.into()) + .validate_only(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Stale) + ); + assert_eq!( + CheckNonce::(0u64.into()) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Stale) + ); + }); // correct - assert_ok!(CheckNonce::(1u64.into()).validate(&1, CALL, &info, len)); - assert_ok!(CheckNonce::(1u64.into()).pre_dispatch(&1, CALL, &info, len)); + assert_ok!(CheckNonce::(1u64.into()).validate_only( + Some(1).into(), + CALL, + &info, + len + )); + assert_ok!(CheckNonce::(1u64.into()).validate_and_prepare( + Some(1).into(), + CALL, + &info, + len + )); // future - assert_ok!(CheckNonce::(5u64.into()).validate(&1, CALL, &info, len)); - assert_noop!( - CheckNonce::(5u64.into()).pre_dispatch(&1, CALL, &info, len), - InvalidTransaction::Future + assert_ok!(CheckNonce::(5u64.into()).validate_only( + Some(1).into(), + CALL, + &info, + len + )); + assert_eq!( + CheckNonce::(5u64.into()) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Future) ); }) } @@ -198,20 +269,133 @@ mod tests { let info = DispatchInfo::default(); let len = 0_usize; // Both providers and sufficients zero - assert_noop!( - CheckNonce::(1u64.into()).validate(&1, CALL, &info, len), - InvalidTransaction::Payment - ); - assert_noop!( - CheckNonce::(1u64.into()).pre_dispatch(&1, CALL, &info, len), - InvalidTransaction::Payment - ); + assert_storage_noop!({ + assert_eq!( + CheckNonce::(1u64.into()) + .validate_only(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Payment) + ); + assert_eq!( + CheckNonce::(1u64.into()) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Payment) + ); + }); // Non-zero providers - assert_ok!(CheckNonce::(1u64.into()).validate(&2, CALL, &info, len)); - assert_ok!(CheckNonce::(1u64.into()).pre_dispatch(&2, CALL, &info, len)); + assert_ok!(CheckNonce::(1u64.into()).validate_only( + Some(2).into(), + CALL, + &info, + len + )); + assert_ok!(CheckNonce::(1u64.into()).validate_and_prepare( + Some(2).into(), + CALL, + &info, + len + )); // Non-zero sufficients - assert_ok!(CheckNonce::(1u64.into()).validate(&3, CALL, &info, len)); - assert_ok!(CheckNonce::(1u64.into()).pre_dispatch(&3, CALL, &info, len)); + assert_ok!(CheckNonce::(1u64.into()).validate_only( + Some(3).into(), + CALL, + &info, + len + )); + assert_ok!(CheckNonce::(1u64.into()).validate_and_prepare( + Some(3).into(), + CALL, + &info, + len + )); + }) + } + + #[test] + fn unsigned_check_nonce_works() { + new_test_ext().execute_with(|| { + let info = DispatchInfo::default(); + let len = 0_usize; + let (_, val, origin) = CheckNonce::(1u64.into()) + .validate(None.into(), CALL, &info, len, (), CALL) + .unwrap(); + assert!(!origin.is_transaction_authorized()); + assert_ok!(CheckNonce::(1u64.into()).prepare(val, &origin, CALL, &info, len)); + }) + } + + #[test] + fn check_nonce_preserves_account_data() { + new_test_ext().execute_with(|| { + crate::Account::::insert( + 1, + crate::AccountInfo { + nonce: 1u64.into(), + consumers: 0, + providers: 1, + sufficients: 0, + data: 0, + }, + ); + let info = DispatchInfo::default(); + let len = 0_usize; + // run the validation step + let (_, val, origin) = CheckNonce::(1u64.into()) + .validate(Some(1).into(), CALL, &info, len, (), CALL) + .unwrap(); + // mutate `AccountData` for the caller + crate::Account::::mutate(1, |info| { + info.data = 42; + }); + // run the preparation step + assert_ok!(CheckNonce::(1u64.into()).prepare(val, &origin, CALL, &info, len)); + // only the nonce should be altered by the preparation step + let expected_info = crate::AccountInfo { + nonce: 2u64.into(), + consumers: 0, + providers: 1, + sufficients: 0, + data: 42, + }; + assert_eq!(crate::Account::::get(1), expected_info); + }) + } + + #[test] + fn check_nonce_skipped_and_refund_for_other_origins() { + new_test_ext().execute_with(|| { + let ext = CheckNonce::(1u64.into()); + + let mut info = CALL.get_dispatch_info(); + info.extension_weight = ext.weight(CALL); + + // Ensure we test the refund. + assert!(info.extension_weight != Weight::zero()); + + let len = CALL.encoded_size(); + + let origin = crate::RawOrigin::Root.into(); + let (pre, origin) = ext.validate_and_prepare(origin, CALL, &info, len).unwrap(); + + assert!(origin.as_system_ref().unwrap().is_root()); + + let pd_res = Ok(()); + let mut post_info = frame_support::dispatch::PostDispatchInfo { + actual_weight: Some(info.total_weight()), + pays_fee: Default::default(), + }; + + as TransactionExtension>::post_dispatch( + pre, + &info, + &mut post_info, + len, + &pd_res, + ) + .unwrap(); + + assert_eq!(post_info.actual_weight, Some(info.call_weight)); }) } } diff --git a/substrate/frame/system/src/extensions/check_spec_version.rs b/substrate/frame/system/src/extensions/check_spec_version.rs index ee7e6f2efd0..ff86c6cd469 100644 --- a/substrate/frame/system/src/extensions/check_spec_version.rs +++ b/substrate/frame/system/src/extensions/check_spec_version.rs @@ -19,7 +19,7 @@ use crate::{Config, Pallet}; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension}, + impl_tx_ext_default, traits::TransactionExtension, transaction_validity::TransactionValidityError, }; @@ -46,30 +46,24 @@ impl core::fmt::Debug for CheckSpecVersion { } impl CheckSpecVersion { - /// Create new `SignedExtension` to check runtime version. + /// Create new `TransactionExtension` to check runtime version. pub fn new() -> Self { Self(core::marker::PhantomData) } } -impl SignedExtension for CheckSpecVersion { - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = u32; - type Pre = (); +impl TransactionExtension<::RuntimeCall> + for CheckSpecVersion +{ const IDENTIFIER: &'static str = "CheckSpecVersion"; - - fn additional_signed(&self) -> Result { + type Implicit = u32; + fn implicit(&self) -> Result { Ok(>::runtime_version().spec_version) } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + type Val = (); + type Pre = (); + fn weight(&self, _: &::RuntimeCall) -> sp_weights::Weight { + ::check_spec_version() } + impl_tx_ext_default!(::RuntimeCall; validate prepare); } diff --git a/substrate/frame/system/src/extensions/check_tx_version.rs b/substrate/frame/system/src/extensions/check_tx_version.rs index 15983c2cd08..e3b7dfe7c92 100644 --- a/substrate/frame/system/src/extensions/check_tx_version.rs +++ b/substrate/frame/system/src/extensions/check_tx_version.rs @@ -19,7 +19,7 @@ use crate::{Config, Pallet}; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension}, + impl_tx_ext_default, traits::TransactionExtension, transaction_validity::TransactionValidityError, }; @@ -46,29 +46,24 @@ impl core::fmt::Debug for CheckTxVersion { } impl CheckTxVersion { - /// Create new `SignedExtension` to check transaction version. + /// Create new `TransactionExtension` to check transaction version. pub fn new() -> Self { Self(core::marker::PhantomData) } } -impl SignedExtension for CheckTxVersion { - type AccountId = T::AccountId; - type Call = ::RuntimeCall; - type AdditionalSigned = u32; - type Pre = (); +impl TransactionExtension<::RuntimeCall> + for CheckTxVersion +{ const IDENTIFIER: &'static str = "CheckTxVersion"; - - fn additional_signed(&self) -> Result { + type Implicit = u32; + fn implicit(&self) -> Result { Ok(>::runtime_version().transaction_version) } - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + type Val = (); + type Pre = (); + fn weight(&self, _: &::RuntimeCall) -> sp_weights::Weight { + ::check_tx_version() } + impl_tx_ext_default!(::RuntimeCall; validate prepare); } diff --git a/substrate/frame/system/src/extensions/check_weight.rs b/substrate/frame/system/src/extensions/check_weight.rs index 22da2a5b987..131057f54a7 100644 --- a/substrate/frame/system/src/extensions/check_weight.rs +++ b/substrate/frame/system/src/extensions/check_weight.rs @@ -23,8 +23,10 @@ use frame_support::{ }; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension}, - transaction_validity::{InvalidTransaction, TransactionValidity, TransactionValidityError}, + traits::{ + DispatchInfoOf, Dispatchable, PostDispatchInfoOf, TransactionExtension, ValidateResult, + }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, DispatchResult, }; use sp_weights::Weight; @@ -50,11 +52,11 @@ where ) -> Result<(), TransactionValidityError> { let max = T::BlockWeights::get().get(info.class).max_extrinsic; match max { - Some(max) if info.weight.any_gt(max) => { + Some(max) if info.total_weight().any_gt(max) => { log::debug!( target: LOG_TARGET, "Extrinsic {} is greater than the max extrinsic {}", - info.weight, + info.total_weight(), max, ); @@ -89,43 +91,73 @@ where } } - /// Creates new `SignedExtension` to check weight of the extrinsic. + /// Creates new `TransactionExtension` to check weight of the extrinsic. pub fn new() -> Self { Self(Default::default()) } + /// Do the validate checks. This can be applied to both signed and unsigned. + /// + /// It only checks that the block weight and length limit will not exceed. + /// + /// Returns the transaction validity and the next block length, to be used in `prepare`. + pub fn do_validate( + info: &DispatchInfoOf, + len: usize, + ) -> Result<(ValidTransaction, u32), TransactionValidityError> { + // If they return `Ok`, then it is below the limit. + let next_len = Self::check_block_length(info, len)?; + // during validation we skip block limit check. Since the `validate_transaction` + // call runs on an empty block anyway, by this we prevent `on_initialize` weight + // consumption from causing false negatives. + Self::check_extrinsic_weight(info)?; + + Ok((Default::default(), next_len)) + } + /// Do the pre-dispatch checks. This can be applied to both signed and unsigned. /// /// It checks and notes the new weight and length. - pub fn do_pre_dispatch( + pub fn do_prepare( info: &DispatchInfoOf, len: usize, + next_len: u32, ) -> Result<(), TransactionValidityError> { - let next_len = Self::check_block_length(info, len)?; - let all_weight = Pallet::::block_weight(); let maximum_weight = T::BlockWeights::get(); let next_weight = calculate_consumed_weight::(&maximum_weight, all_weight, info, len)?; - Self::check_extrinsic_weight(info)?; + // Extrinsic weight already checked in `validate`. crate::AllExtrinsicsLen::::put(next_len); crate::BlockWeight::::put(next_weight); Ok(()) } - /// Do the validate checks. This can be applied to both signed and unsigned. - /// - /// It only checks that the block weight and length limit will not exceed. - pub fn do_validate(info: &DispatchInfoOf, len: usize) -> TransactionValidity { - // ignore the next length. If they return `Ok`, then it is below the limit. - let _ = Self::check_block_length(info, len)?; - // during validation we skip block limit check. Since the `validate_transaction` - // call runs on an empty block anyway, by this we prevent `on_initialize` weight - // consumption from causing false negatives. - Self::check_extrinsic_weight(info)?; + pub fn do_post_dispatch( + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + ) -> Result<(), TransactionValidityError> { + let unspent = post_info.calc_unspent(info); + if unspent.any_gt(Weight::zero()) { + crate::BlockWeight::::mutate(|current_weight| { + current_weight.reduce(unspent, info.class); + }) + } - Ok(Default::default()) + log::trace!( + target: LOG_TARGET, + "Used block weight: {:?}", + crate::BlockWeight::::get(), + ); + + log::trace!( + target: LOG_TARGET, + "Used block length: {:?}", + Pallet::::all_extrinsics_len(), + ); + + Ok(()) } } @@ -143,7 +175,7 @@ where { // Also Consider extrinsic length as proof weight. let extrinsic_weight = info - .weight + .total_weight() .saturating_add(maximum_weight.get(info.class).base_extrinsic) .saturating_add(Weight::from_parts(0, len as u64)); let limit_per_class = maximum_weight.get(info.class); @@ -201,83 +233,78 @@ where Ok(all_weight) } -impl SignedExtension for CheckWeight +impl TransactionExtension for CheckWeight where T::RuntimeCall: Dispatchable, { - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); - type Pre = (); const IDENTIFIER: &'static str = "CheckWeight"; + type Implicit = (); + type Pre = (); + type Val = u32; /* next block length */ - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) + fn weight(&self, _: &T::RuntimeCall) -> Weight { + ::check_weight() } - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - info: &DispatchInfoOf, + fn validate( + &self, + origin: T::RuntimeOrigin, + _call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> Result<(), TransactionValidityError> { - Self::do_pre_dispatch(info, len) + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let (validity, next_len) = Self::do_validate(info, len)?; + Ok((validity, next_len, origin)) } - fn validate( - &self, - _who: &Self::AccountId, - _call: &Self::Call, - info: &DispatchInfoOf, + fn prepare( + self, + val: Self::Val, + _origin: &T::RuntimeOrigin, + _call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> TransactionValidity { - Self::do_validate(info, len) + ) -> Result { + Self::do_prepare(info, len, val) } - fn pre_dispatch_unsigned( - _call: &Self::Call, - info: &DispatchInfoOf, + fn post_dispatch_details( + _pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result { + Self::do_post_dispatch(info, post_info)?; + Ok(Weight::zero()) + } + + fn bare_validate( + _call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> Result<(), TransactionValidityError> { - Self::do_pre_dispatch(info, len) + ) -> frame_support::pallet_prelude::TransactionValidity { + Ok(Self::do_validate(info, len)?.0) } - fn validate_unsigned( - _call: &Self::Call, - info: &DispatchInfoOf, + fn bare_validate_and_prepare( + _call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> TransactionValidity { - Self::do_validate(info, len) + ) -> Result<(), TransactionValidityError> { + let (_, next_len) = Self::do_validate(info, len)?; + Self::do_prepare(info, len, next_len) } - fn post_dispatch( - _pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + fn bare_post_dispatch( + info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, _len: usize, _result: &DispatchResult, ) -> Result<(), TransactionValidityError> { - let unspent = post_info.calc_unspent(info); - if unspent.any_gt(Weight::zero()) { - crate::BlockWeight::::mutate(|current_weight| { - current_weight.reduce(unspent, info.class); - }) - } - - log::trace!( - target: LOG_TARGET, - "Used block weight: {:?}", - crate::BlockWeight::::get(), - ); - - log::trace!( - target: LOG_TARGET, - "Used block length: {:?}", - Pallet::::all_extrinsics_len(), - ); - - Ok(()) + Self::do_post_dispatch(info, post_info) } } @@ -302,6 +329,7 @@ mod tests { }; use core::marker::PhantomData; use frame_support::{assert_err, assert_ok, dispatch::Pays, weights::Weight}; + use sp_runtime::traits::DispatchTransaction; fn block_weights() -> crate::limits::BlockWeights { ::BlockWeights::get() @@ -327,7 +355,7 @@ mod tests { fn check(call: impl FnOnce(&DispatchInfo, usize)) { new_test_ext().execute_with(|| { let max = DispatchInfo { - weight: Weight::MAX, + call_weight: Weight::MAX, class: DispatchClass::Mandatory, ..Default::default() }; @@ -338,7 +366,8 @@ mod tests { } check(|max, len| { - assert_ok!(CheckWeight::::do_pre_dispatch(max, len)); + let next_len = CheckWeight::::check_block_length(max, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(max, len, next_len)); assert_eq!(System::block_weight().total(), Weight::MAX); assert!(System::block_weight().total().ref_time() > block_weight_limit().ref_time()); }); @@ -351,7 +380,7 @@ mod tests { fn normal_extrinsic_limited_by_maximum_extrinsic_weight() { new_test_ext().execute_with(|| { let max = DispatchInfo { - weight: block_weights().get(DispatchClass::Normal).max_extrinsic.unwrap() + + call_weight: block_weights().get(DispatchClass::Normal).max_extrinsic.unwrap() + Weight::from_parts(1, 0), class: DispatchClass::Normal, ..Default::default() @@ -374,11 +403,14 @@ mod tests { .unwrap_or_else(|| weights.max_block); let base_weight = weights.get(DispatchClass::Operational).base_extrinsic; - let weight = operational_limit - base_weight; - let okay = - DispatchInfo { weight, class: DispatchClass::Operational, ..Default::default() }; + let call_weight = operational_limit - base_weight; + let okay = DispatchInfo { + call_weight, + class: DispatchClass::Operational, + ..Default::default() + }; let max = DispatchInfo { - weight: weight + Weight::from_parts(1, 0), + call_weight: call_weight + Weight::from_parts(1, 0), class: DispatchClass::Operational, ..Default::default() }; @@ -410,18 +442,20 @@ mod tests { // So normal extrinsic can be 758 weight (-5 for base extrinsic weight) // And Operational can be 246 to produce a full block (-10 for base) let max_normal = - DispatchInfo { weight: Weight::from_parts(753, 0), ..Default::default() }; + DispatchInfo { call_weight: Weight::from_parts(753, 0), ..Default::default() }; let rest_operational = DispatchInfo { - weight: Weight::from_parts(246, 0), + call_weight: Weight::from_parts(246, 0), class: DispatchClass::Operational, ..Default::default() }; let len = 0_usize; - assert_ok!(CheckWeight::::do_pre_dispatch(&max_normal, len)); + let next_len = CheckWeight::::check_block_length(&max_normal, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&max_normal, len, next_len)); assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0)); - assert_ok!(CheckWeight::::do_pre_dispatch(&rest_operational, len)); + let next_len = CheckWeight::::check_block_length(&rest_operational, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&rest_operational, len, next_len)); assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX)); assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0)); // Checking single extrinsic should not take current block weight into account. @@ -434,19 +468,21 @@ mod tests { new_test_ext().execute_with(|| { // We switch the order of `full_block_with_normal_and_operational` let max_normal = - DispatchInfo { weight: Weight::from_parts(753, 0), ..Default::default() }; + DispatchInfo { call_weight: Weight::from_parts(753, 0), ..Default::default() }; let rest_operational = DispatchInfo { - weight: Weight::from_parts(246, 0), + call_weight: Weight::from_parts(246, 0), class: DispatchClass::Operational, ..Default::default() }; let len = 0_usize; - assert_ok!(CheckWeight::::do_pre_dispatch(&rest_operational, len)); + let next_len = CheckWeight::::check_block_length(&rest_operational, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&rest_operational, len, next_len)); // Extra 20 here from block execution + base extrinsic weight assert_eq!(System::block_weight().total(), Weight::from_parts(266, 0)); - assert_ok!(CheckWeight::::do_pre_dispatch(&max_normal, len)); + let next_len = CheckWeight::::check_block_length(&max_normal, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&max_normal, len, next_len)); assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX)); assert_eq!(System::block_weight().total(), block_weight_limit().set_proof_size(0)); }); @@ -458,27 +494,30 @@ mod tests { // An on_initialize takes up the whole block! (Every time!) System::register_extra_weight_unchecked(Weight::MAX, DispatchClass::Mandatory); let dispatch_normal = DispatchInfo { - weight: Weight::from_parts(251, 0), + call_weight: Weight::from_parts(251, 0), class: DispatchClass::Normal, ..Default::default() }; let dispatch_operational = DispatchInfo { - weight: Weight::from_parts(246, 0), + call_weight: Weight::from_parts(246, 0), class: DispatchClass::Operational, ..Default::default() }; let len = 0_usize; + let next_len = CheckWeight::::check_block_length(&dispatch_normal, len).unwrap(); assert_err!( - CheckWeight::::do_pre_dispatch(&dispatch_normal, len), + CheckWeight::::do_prepare(&dispatch_normal, len, next_len), InvalidTransaction::ExhaustsResources ); + let next_len = + CheckWeight::::check_block_length(&dispatch_operational, len).unwrap(); // Thank goodness we can still do an operational transaction to possibly save the // blockchain. - assert_ok!(CheckWeight::::do_pre_dispatch(&dispatch_operational, len)); + assert_ok!(CheckWeight::::do_prepare(&dispatch_operational, len, next_len)); // Not too much though assert_err!( - CheckWeight::::do_pre_dispatch(&dispatch_operational, len), + CheckWeight::::do_prepare(&dispatch_operational, len, next_len), InvalidTransaction::ExhaustsResources ); // Even with full block, validity of single transaction should be correct. @@ -489,9 +528,11 @@ mod tests { #[test] fn signed_ext_check_weight_works_operational_tx() { new_test_ext().execute_with(|| { - let normal = DispatchInfo { weight: Weight::from_parts(100, 0), ..Default::default() }; + let normal = + DispatchInfo { call_weight: Weight::from_parts(100, 0), ..Default::default() }; let op = DispatchInfo { - weight: Weight::from_parts(100, 0), + call_weight: Weight::from_parts(100, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -503,21 +544,35 @@ mod tests { current_weight.set(normal_limit, DispatchClass::Normal) }); // will not fit. - assert_err!( - CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &normal, len), - InvalidTransaction::ExhaustsResources + assert_eq!( + CheckWeight::(PhantomData) + .validate_and_prepare(Some(1).into(), CALL, &normal, len) + .unwrap_err(), + InvalidTransaction::ExhaustsResources.into() ); // will fit. - assert_ok!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &op, len)); + assert_ok!(CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + &op, + len + )); // likewise for length limit. let len = 100_usize; AllExtrinsicsLen::::put(normal_length_limit()); - assert_err!( - CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &normal, len), - InvalidTransaction::ExhaustsResources + assert_eq!( + CheckWeight::(PhantomData) + .validate_and_prepare(Some(1).into(), CALL, &normal, len) + .unwrap_err(), + InvalidTransaction::ExhaustsResources.into() ); - assert_ok!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &op, len)); + assert_ok!(CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + &op, + len + )); }) } @@ -528,7 +583,12 @@ mod tests { let normal_limit = normal_weight_limit().ref_time() as usize; let reset_check_weight = |tx, s, f| { AllExtrinsicsLen::::put(0); - let r = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, tx, s); + let r = CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + tx, + s, + ); if f { assert!(r.is_err()) } else { @@ -542,7 +602,8 @@ mod tests { // Operational ones don't have this limit. let op = DispatchInfo { - weight: Weight::zero(), + call_weight: Weight::zero(), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -557,12 +618,13 @@ mod tests { fn signed_ext_check_weight_works_normal_tx() { new_test_ext().execute_with(|| { let normal_limit = normal_weight_limit(); - let small = DispatchInfo { weight: Weight::from_parts(100, 0), ..Default::default() }; + let small = + DispatchInfo { call_weight: Weight::from_parts(100, 0), ..Default::default() }; let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic; let medium = - DispatchInfo { weight: normal_limit - base_extrinsic, ..Default::default() }; + DispatchInfo { call_weight: normal_limit - base_extrinsic, ..Default::default() }; let big = DispatchInfo { - weight: normal_limit - base_extrinsic + Weight::from_parts(1, 0), + call_weight: normal_limit - base_extrinsic + Weight::from_parts(1, 0), ..Default::default() }; let len = 0_usize; @@ -571,7 +633,12 @@ mod tests { BlockWeight::::mutate(|current_weight| { current_weight.set(s, DispatchClass::Normal) }); - let r = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, i, len); + let r = CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + i, + len, + ); if f { assert!(r.is_err()) } else { @@ -589,7 +656,8 @@ mod tests { fn signed_ext_check_weight_refund_works() { new_test_ext().execute_with(|| { // This is half of the max block weight - let info = DispatchInfo { weight: Weight::from_parts(512, 0), ..Default::default() }; + let info = + DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() }; let post_info = PostDispatchInfo { actual_weight: Some(Weight::from_parts(128, 0)), pays_fee: Default::default(), @@ -604,14 +672,17 @@ mod tests { .set(Weight::from_parts(256, 0) - base_extrinsic, DispatchClass::Normal); }); - let pre = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap(); + let pre = CheckWeight::(PhantomData) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap() + .0; assert_eq!( BlockWeight::::get().total(), - info.weight + Weight::from_parts(256, 0) + info.total_weight() + Weight::from_parts(256, 0) ); - assert_ok!(CheckWeight::::post_dispatch( - Some(pre), + assert_ok!(CheckWeight::::post_dispatch_details( + pre, &info, &post_info, len, @@ -627,7 +698,8 @@ mod tests { #[test] fn signed_ext_check_weight_actual_weight_higher_than_max_is_capped() { new_test_ext().execute_with(|| { - let info = DispatchInfo { weight: Weight::from_parts(512, 0), ..Default::default() }; + let info = + DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() }; let post_info = PostDispatchInfo { actual_weight: Some(Weight::from_parts(700, 0)), pays_fee: Default::default(), @@ -639,16 +711,19 @@ mod tests { current_weight.set(Weight::from_parts(128, 0), DispatchClass::Normal); }); - let pre = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &info, len).unwrap(); + let pre = CheckWeight::(PhantomData) + .validate_and_prepare(Some(1).into(), CALL, &info, len) + .unwrap() + .0; assert_eq!( BlockWeight::::get().total(), - info.weight + + info.total_weight() + Weight::from_parts(128, 0) + block_weights().get(DispatchClass::Normal).base_extrinsic, ); - assert_ok!(CheckWeight::::post_dispatch( - Some(pre), + assert_ok!(CheckWeight::::post_dispatch_details( + pre, &info, &post_info, len, @@ -656,7 +731,7 @@ mod tests { )); assert_eq!( BlockWeight::::get().total(), - info.weight + + info.total_weight() + Weight::from_parts(128, 0) + block_weights().get(DispatchClass::Normal).base_extrinsic, ); @@ -667,12 +742,17 @@ mod tests { fn zero_weight_extrinsic_still_has_base_weight() { new_test_ext().execute_with(|| { let weights = block_weights(); - let free = DispatchInfo { weight: Weight::zero(), ..Default::default() }; + let free = DispatchInfo { call_weight: Weight::zero(), ..Default::default() }; let len = 0_usize; // Initial weight from `weights.base_block` assert_eq!(System::block_weight().total(), weights.base_block); - assert_ok!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, &free, len)); + assert_ok!(CheckWeight::(PhantomData).validate_and_prepare( + Some(1).into(), + CALL, + &free, + len + )); assert_eq!( System::block_weight().total(), weights.get(DispatchClass::Normal).base_extrinsic + weights.base_block @@ -687,18 +767,20 @@ mod tests { // Max normal is 768 (75%) // Max mandatory is unlimited let max_normal = - DispatchInfo { weight: Weight::from_parts(753, 0), ..Default::default() }; + DispatchInfo { call_weight: Weight::from_parts(753, 0), ..Default::default() }; let mandatory = DispatchInfo { - weight: Weight::from_parts(1019, 0), + call_weight: Weight::from_parts(1019, 0), class: DispatchClass::Mandatory, ..Default::default() }; let len = 0_usize; - assert_ok!(CheckWeight::::do_pre_dispatch(&max_normal, len)); + let next_len = CheckWeight::::check_block_length(&max_normal, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&max_normal, len, next_len)); assert_eq!(System::block_weight().total(), Weight::from_parts(768, 0)); - assert_ok!(CheckWeight::::do_pre_dispatch(&mandatory, len)); + let next_len = CheckWeight::::check_block_length(&mandatory, len).unwrap(); + assert_ok!(CheckWeight::::do_prepare(&mandatory, len, next_len)); assert_eq!(block_weight_limit(), Weight::from_parts(1024, u64::MAX)); assert_eq!(System::block_weight().total(), Weight::from_parts(1024 + 768, 0)); assert_eq!(CheckWeight::::check_extrinsic_weight(&mandatory), Ok(())); @@ -729,13 +811,13 @@ mod tests { // fits into reserved let mandatory1 = DispatchInfo { - weight: Weight::from_parts(5, 0), + call_weight: Weight::from_parts(5, 0), class: DispatchClass::Mandatory, ..Default::default() }; // does not fit into reserved and the block is full. let mandatory2 = DispatchInfo { - weight: Weight::from_parts(6, 0), + call_weight: Weight::from_parts(6, 0), class: DispatchClass::Mandatory, ..Default::default() }; @@ -778,13 +860,13 @@ mod tests { }); let normal = DispatchInfo { - weight: Weight::from_parts(5, 0), + call_weight: Weight::from_parts(5, 0), class: DispatchClass::Normal, ..Default::default() }; let mandatory = DispatchInfo { - weight: Weight::from_parts(5, 0), + call_weight: Weight::from_parts(5, 0), class: DispatchClass::Mandatory, ..Default::default() }; @@ -798,7 +880,7 @@ mod tests { ) .unwrap(); - assert_eq!(consumed.total().saturating_sub(all_weight.total()), normal.weight); + assert_eq!(consumed.total().saturating_sub(all_weight.total()), normal.total_weight()); let consumed = calculate_consumed_weight::<::RuntimeCall>( &maximum_weight, @@ -807,7 +889,7 @@ mod tests { 0, ) .unwrap(); - assert_eq!(consumed.total().saturating_sub(all_weight.total()), mandatory.weight); + assert_eq!(consumed.total().saturating_sub(all_weight.total()), mandatory.total_weight()); // Using non zero length extrinsics. let consumed = calculate_consumed_weight::<::RuntimeCall>( @@ -820,7 +902,7 @@ mod tests { // Must account for the len in the proof size assert_eq!( consumed.total().saturating_sub(all_weight.total()), - normal.weight.add_proof_size(100) + normal.total_weight().add_proof_size(100) ); let consumed = calculate_consumed_weight::<::RuntimeCall>( @@ -833,7 +915,7 @@ mod tests { // Must account for the len in the proof size assert_eq!( consumed.total().saturating_sub(all_weight.total()), - mandatory.weight.add_proof_size(100) + mandatory.total_weight().add_proof_size(100) ); // Using oversized zero length extrinsics. diff --git a/substrate/frame/system/src/extensions/mod.rs b/substrate/frame/system/src/extensions/mod.rs index a88c9fbf96e..d79104d2240 100644 --- a/substrate/frame/system/src/extensions/mod.rs +++ b/substrate/frame/system/src/extensions/mod.rs @@ -22,3 +22,6 @@ pub mod check_nonce; pub mod check_spec_version; pub mod check_tx_version; pub mod check_weight; +pub mod weights; + +pub use weights::WeightInfo; diff --git a/substrate/frame/system/src/extensions/weights.rs b/substrate/frame/system/src/extensions/weights.rs new file mode 100644 index 00000000000..1c0136ae780 --- /dev/null +++ b/substrate/frame/system/src/extensions/weights.rs @@ -0,0 +1,217 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `frame_system_extensions` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, 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` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// ./target/production/substrate-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=frame_system_extensions +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/system/src/extensions/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `frame_system_extensions`. +pub trait WeightInfo { + fn check_genesis() -> Weight; + fn check_mortality_mortal_transaction() -> Weight; + fn check_mortality_immortal_transaction() -> Weight; + fn check_non_zero_sender() -> Weight; + fn check_nonce() -> Weight; + fn check_spec_version() -> Weight; + fn check_tx_version() -> Weight; + fn check_weight() -> Weight; +} + +/// Weights for `frame_system_extensions` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_876_000 picoseconds. + Weight::from_parts(4_160_000, 3509) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 6_296_000 picoseconds. + Weight::from_parts(6_523_000, 3509) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 6_296_000 picoseconds. + Weight::from_parts(6_523_000, 3509) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 449_000 picoseconds. + Weight::from_parts(527_000, 0) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3593` + // Minimum execution time: 5_689_000 picoseconds. + Weight::from_parts(6_000_000, 3593) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 399_000 picoseconds. + Weight::from_parts(461_000, 0) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 390_000 picoseconds. + Weight::from_parts(439_000, 0) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 4_375_000 picoseconds. + Weight::from_parts(4_747_000, 1489) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_genesis() -> Weight { + // Proof Size summary in bytes: + // Measured: `54` + // Estimated: `3509` + // Minimum execution time: 3_876_000 picoseconds. + Weight::from_parts(4_160_000, 3509) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_mortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 6_296_000 picoseconds. + Weight::from_parts(6_523_000, 3509) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn check_mortality_immortal_transaction() -> Weight { + // Proof Size summary in bytes: + // Measured: `92` + // Estimated: `3509` + // Minimum execution time: 6_296_000 picoseconds. + Weight::from_parts(6_523_000, 3509) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + fn check_non_zero_sender() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 449_000 picoseconds. + Weight::from_parts(527_000, 0) + } + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn check_nonce() -> Weight { + // Proof Size summary in bytes: + // Measured: `101` + // Estimated: `3593` + // Minimum execution time: 5_689_000 picoseconds. + Weight::from_parts(6_000_000, 3593) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + fn check_spec_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 399_000 picoseconds. + Weight::from_parts(461_000, 0) + } + fn check_tx_version() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 390_000 picoseconds. + Weight::from_parts(439_000, 0) + } + /// Storage: `System::AllExtrinsicsLen` (r:1 w:1) + /// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn check_weight() -> Weight { + // Proof Size summary in bytes: + // Measured: `24` + // Estimated: `1489` + // Minimum execution time: 4_375_000 picoseconds. + Weight::from_parts(4_747_000, 1489) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index a5c5f1ed2e4..02d61921741 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -169,7 +169,7 @@ pub use extensions::{ check_genesis::CheckGenesis, check_mortality::CheckMortality, check_non_zero_sender::CheckNonZeroSender, check_nonce::CheckNonce, check_spec_version::CheckSpecVersion, check_tx_version::CheckTxVersion, - check_weight::CheckWeight, + check_weight::CheckWeight, WeightInfo as ExtensionsWeightInfo, }; // Backward compatible re-export. pub use extensions::check_mortality::CheckMortality as CheckEra; @@ -261,6 +261,19 @@ where check_version: bool, } +/// Information about the dispatch of a call, to be displayed in the +/// [`ExtrinsicSuccess`](Event::ExtrinsicSuccess) and [`ExtrinsicFailed`](Event::ExtrinsicFailed) +/// events. +#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo)] +pub struct DispatchEventInfo { + /// Weight of this transaction. + pub weight: Weight, + /// Class of this transaction. + pub class: DispatchClass, + /// Does this transaction pay fees. + pub pays_fee: Pays, +} + #[frame_support::pallet] pub mod pallet { use crate::{self as frame_system, pallet_prelude::*, *}; @@ -303,6 +316,7 @@ pub mod pallet { type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); + type ExtensionsWeightInfo = (); type SS58Prefix = (); type Version = (); type BlockWeights = (); @@ -375,6 +389,9 @@ pub mod pallet { /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = (); + /// Weight information for the extensions of this pallet. + type ExtensionsWeightInfo = (); + /// This is used as an identifier of the chain. type SS58Prefix = (); @@ -582,8 +599,12 @@ pub mod pallet { /// All resources should be cleaned up associated with the given account. type OnKilledAccount: OnKilledAccount; + /// Weight information for the extrinsics of this pallet. type SystemWeightInfo: WeightInfo; + /// Weight information for the transaction extensions of this pallet. + type ExtensionsWeightInfo: extensions::WeightInfo; + /// The designated SS58 prefix of this chain. /// /// This replaces the "ss58Format" property declared in the chain spec. Reason is @@ -833,9 +854,9 @@ pub mod pallet { #[pallet::event] pub enum Event { /// An extrinsic completed successfully. - ExtrinsicSuccess { dispatch_info: DispatchInfo }, + ExtrinsicSuccess { dispatch_info: DispatchEventInfo }, /// An extrinsic failed. - ExtrinsicFailed { dispatch_error: DispatchError, dispatch_info: DispatchInfo }, + ExtrinsicFailed { dispatch_error: DispatchError, dispatch_info: DispatchEventInfo }, /// `:code` was updated. CodeUpdated, /// A new account was created. @@ -921,6 +942,7 @@ pub mod pallet { /// Total length (in bytes) for all extrinsics put together, for the current block. #[pallet::storage] + #[pallet::whitelist_storage] pub type AllExtrinsicsLen = StorageValue<_, u32>; /// Map of block numbers to block hashes. @@ -2025,13 +2047,15 @@ impl Pallet { /// Emits an `ExtrinsicSuccess` or `ExtrinsicFailed` event depending on the outcome. /// The emitted event contains the post-dispatch corrected weight including /// the base-weight for its dispatch class. - pub fn note_applied_extrinsic(r: &DispatchResultWithPostInfo, mut info: DispatchInfo) { - info.weight = extract_actual_weight(r, &info) + pub fn note_applied_extrinsic(r: &DispatchResultWithPostInfo, info: DispatchInfo) { + let weight = extract_actual_weight(r, &info) .saturating_add(T::BlockWeights::get().get(info.class).base_extrinsic); - info.pays_fee = extract_actual_pays_fee(r, &info); + let class = info.class; + let pays_fee = extract_actual_pays_fee(r, &info); + let dispatch_event_info = DispatchEventInfo { weight, class, pays_fee }; Self::deposit_event(match r { - Ok(_) => Event::ExtrinsicSuccess { dispatch_info: info }, + Ok(_) => Event::ExtrinsicSuccess { dispatch_info: dispatch_event_info }, Err(err) => { log::trace!( target: LOG_TARGET, @@ -2039,7 +2063,10 @@ impl Pallet { Self::block_number(), err, ); - Event::ExtrinsicFailed { dispatch_error: err.error, dispatch_info: info } + Event::ExtrinsicFailed { + dispatch_error: err.error, + dispatch_info: dispatch_event_info, + } }, }); diff --git a/substrate/frame/system/src/offchain.rs b/substrate/frame/system/src/offchain.rs index 1f72ea2d374..bedfdded818 100644 --- a/substrate/frame/system/src/offchain.rs +++ b/substrate/frame/system/src/offchain.rs @@ -58,9 +58,10 @@ use alloc::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec}; use codec::Encode; +use scale_info::TypeInfo; use sp_runtime::{ app_crypto::RuntimeAppPublic, - traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}, + traits::{ExtrinsicLike, IdentifyAccount, One}, RuntimeDebug, }; @@ -75,29 +76,18 @@ pub struct ForAny {} /// For submitting unsigned transactions, `submit_unsigned_transaction` /// utility function can be used. However, this struct is used by `Signer` /// to submit a signed transactions providing the signature along with the call. -pub struct SubmitTransaction, OverarchingCall> { - _phantom: core::marker::PhantomData<(T, OverarchingCall)>, +pub struct SubmitTransaction, RuntimeCall> { + _phantom: core::marker::PhantomData<(T, RuntimeCall)>, } impl SubmitTransaction where - T: SendTransactionTypes, + T: CreateTransactionBase, { - /// Submit transaction onchain by providing the call and an optional signature - pub fn submit_transaction( - call: >::OverarchingCall, - signature: Option<::SignaturePayload>, - ) -> Result<(), ()> { - let xt = T::Extrinsic::new(call, signature).ok_or(())?; + /// A convenience method to submit an extrinsic onchain. + pub fn submit_transaction(xt: T::Extrinsic) -> Result<(), ()> { sp_io::offchain::submit_transaction(xt.encode()) } - - /// A convenience method to submit an unsigned transaction onchain. - pub fn submit_unsigned_transaction( - call: >::OverarchingCall, - ) -> Result<(), ()> { - SubmitTransaction::::submit_transaction(call, None) - } } /// Provides an implementation for signing transaction payloads. @@ -284,7 +274,7 @@ impl< } impl< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + CreateInherent, C: AppCrypto, LocalCall, > SendUnsignedTransaction for Signer @@ -310,7 +300,7 @@ impl< } impl< - T: SigningTypes + SendTransactionTypes, + T: SigningTypes + CreateInherent, C: AppCrypto, LocalCall, > SendUnsignedTransaction for Signer @@ -457,25 +447,32 @@ pub trait SigningTypes: crate::Config { type Signature: Clone + PartialEq + core::fmt::Debug + codec::Codec + scale_info::TypeInfo; } -/// A definition of types required to submit transactions from within the runtime. -pub trait SendTransactionTypes { - /// The extrinsic type expected by the runtime. - type Extrinsic: ExtrinsicT + codec::Encode; +/// Common interface for the `CreateTransaction` trait family to unify the `Call` type. +pub trait CreateTransactionBase { + /// The extrinsic. + type Extrinsic: ExtrinsicLike + Encode; + /// The runtime's call type. /// /// This has additional bound to be able to be created from pallet-local `Call` types. - type OverarchingCall: From + codec::Encode; + type RuntimeCall: From + Encode; } -/// Create signed transaction. -/// -/// This trait is meant to be implemented by the runtime and is responsible for constructing -/// a payload to be signed and contained within the extrinsic. -/// This will most likely include creation of `SignedExtra` (a set of `SignedExtensions`). -/// Note that the result can be altered by inspecting the `Call` (for instance adjusting -/// fees, or mortality depending on the `pallet` being called). +/// Interface for creating a transaction. +pub trait CreateTransaction: CreateTransactionBase { + /// The extension. + type Extension: TypeInfo; + + /// Create a transaction using the call and the desired transaction extension. + fn create_transaction( + call: >::RuntimeCall, + extension: Self::Extension, + ) -> Self::Extrinsic; +} + +/// Interface for creating an old-school signed transaction. pub trait CreateSignedTransaction: - SendTransactionTypes + SigningTypes + CreateTransactionBase + SigningTypes { /// Attempt to create signed extrinsic data that encodes call from given account. /// @@ -483,12 +480,18 @@ pub trait CreateSignedTransaction: /// in any way it wants. /// Returns `None` if signed extrinsic could not be created (either because signing failed /// or because of any other runtime-specific reason). - fn create_transaction>( - call: Self::OverarchingCall, + fn create_signed_transaction>( + call: >::RuntimeCall, public: Self::Public, account: Self::AccountId, nonce: Self::Nonce, - ) -> Option<(Self::OverarchingCall, ::SignaturePayload)>; + ) -> Option; +} + +/// Interface for creating an inherent. +pub trait CreateInherent: CreateTransactionBase { + /// Create an inherent. + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic; } /// A message signer. @@ -516,7 +519,7 @@ pub trait SignMessage { /// Submit a signed transaction to the transaction pool. pub trait SendSignedTransaction< - T: SigningTypes + CreateSignedTransaction, + T: CreateSignedTransaction, C: AppCrypto, LocalCall, > @@ -547,13 +550,14 @@ pub trait SendSignedTransaction< account.id, account_data.nonce, ); - let (call, signature) = T::create_transaction::( + let transaction = T::create_signed_transaction::( call.into(), account.public.clone(), account.id.clone(), account_data.nonce, )?; - let res = SubmitTransaction::::submit_transaction(call, Some(signature)); + + let res = SubmitTransaction::::submit_transaction(transaction); if res.is_ok() { // increment the nonce. This is fine, since the code should always @@ -567,7 +571,7 @@ pub trait SendSignedTransaction< } /// Submit an unsigned transaction onchain with a signed payload -pub trait SendUnsignedTransaction, LocalCall> { +pub trait SendUnsignedTransaction, LocalCall> { /// A submission result. /// /// Should contain the submission result and the account(s) that signed the payload. @@ -590,7 +594,8 @@ pub trait SendUnsignedTransaction Option> { - Some(SubmitTransaction::::submit_unsigned_transaction(call.into())) + let xt = T::create_inherent(call.into()); + Some(SubmitTransaction::::submit_transaction(xt)) } } @@ -630,9 +635,15 @@ mod tests { type Extrinsic = TestXt; - impl SendTransactionTypes for TestRuntime { + impl CreateTransactionBase for TestRuntime { type Extrinsic = Extrinsic; - type OverarchingCall = RuntimeCall; + type RuntimeCall = RuntimeCall; + } + + impl CreateInherent for TestRuntime { + fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic { + Extrinsic::new_bare(call) + } } #[derive(codec::Encode, codec::Decode)] @@ -693,7 +704,7 @@ mod tests { let _tx3 = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); - assert_eq!(tx1.signature, None); + assert!(tx1.is_inherent()); }); } @@ -724,7 +735,7 @@ mod tests { let tx1 = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); - assert_eq!(tx1.signature, None); + assert!(tx1.is_inherent()); }); } @@ -758,7 +769,7 @@ mod tests { let _tx2 = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); - assert_eq!(tx1.signature, None); + assert!(tx1.is_inherent()); }); } @@ -790,7 +801,7 @@ mod tests { let tx1 = pool_state.write().transactions.pop().unwrap(); assert!(pool_state.read().transactions.is_empty()); let tx1 = Extrinsic::decode(&mut &*tx1).unwrap(); - assert_eq!(tx1.signature, None); + assert!(tx1.is_inherent()); }); } } diff --git a/substrate/frame/system/src/tests.rs b/substrate/frame/system/src/tests.rs index aa1094e3fe4..6b903f5b7e7 100644 --- a/substrate/frame/system/src/tests.rs +++ b/substrate/frame/system/src/tests.rs @@ -266,7 +266,10 @@ fn deposit_event_should_work() { EventRecord { phase: Phase::ApplyExtrinsic(0), event: SysEvent::ExtrinsicSuccess { - dispatch_info: DispatchInfo { weight: normal_base, ..Default::default() } + dispatch_info: DispatchEventInfo { + weight: normal_base, + ..Default::default() + } } .into(), topics: vec![] @@ -275,7 +278,10 @@ fn deposit_event_should_work() { phase: Phase::ApplyExtrinsic(1), event: SysEvent::ExtrinsicFailed { dispatch_error: DispatchError::BadOrigin.into(), - dispatch_info: DispatchInfo { weight: normal_base, ..Default::default() } + dispatch_info: DispatchEventInfo { + weight: normal_base, + ..Default::default() + } } .into(), topics: vec![] @@ -300,7 +306,8 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { let normal_base = ::BlockWeights::get() .get(DispatchClass::Normal) .base_extrinsic; - let pre_info = DispatchInfo { weight: Weight::from_parts(1000, 0), ..Default::default() }; + let pre_info = + DispatchInfo { call_weight: Weight::from_parts(1000, 0), ..Default::default() }; System::note_applied_extrinsic(&Ok(from_actual_ref_time(Some(300))), pre_info); System::note_applied_extrinsic(&Ok(from_actual_ref_time(Some(1000))), pre_info); System::note_applied_extrinsic( @@ -356,7 +363,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { .base_extrinsic; assert!(normal_base != operational_base, "Test pre-condition violated"); let pre_info = DispatchInfo { - weight: Weight::from_parts(1000, 0), + call_weight: Weight::from_parts(1000, 0), class: DispatchClass::Operational, ..Default::default() }; @@ -367,7 +374,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { EventRecord { phase: Phase::ApplyExtrinsic(0), event: SysEvent::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(300, 0).saturating_add(normal_base), ..Default::default() }, @@ -378,7 +385,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { EventRecord { phase: Phase::ApplyExtrinsic(1), event: SysEvent::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(1000, 0).saturating_add(normal_base), ..Default::default() }, @@ -389,7 +396,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { EventRecord { phase: Phase::ApplyExtrinsic(2), event: SysEvent::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(1000, 0).saturating_add(normal_base), ..Default::default() }, @@ -400,10 +407,10 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { EventRecord { phase: Phase::ApplyExtrinsic(3), event: SysEvent::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(1000, 0).saturating_add(normal_base), pays_fee: Pays::Yes, - ..Default::default() + class: Default::default(), }, } .into(), @@ -412,10 +419,10 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { EventRecord { phase: Phase::ApplyExtrinsic(4), event: SysEvent::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(1000, 0).saturating_add(normal_base), pays_fee: Pays::No, - ..Default::default() + class: Default::default(), }, } .into(), @@ -424,10 +431,10 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { EventRecord { phase: Phase::ApplyExtrinsic(5), event: SysEvent::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(1000, 0).saturating_add(normal_base), pays_fee: Pays::No, - ..Default::default() + class: Default::default(), }, } .into(), @@ -436,10 +443,10 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { EventRecord { phase: Phase::ApplyExtrinsic(6), event: SysEvent::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(500, 0).saturating_add(normal_base), pays_fee: Pays::No, - ..Default::default() + class: Default::default(), }, } .into(), @@ -449,7 +456,7 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(7), event: SysEvent::ExtrinsicFailed { dispatch_error: DispatchError::BadOrigin.into(), - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(999, 0).saturating_add(normal_base), ..Default::default() }, @@ -461,10 +468,10 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(8), event: SysEvent::ExtrinsicFailed { dispatch_error: DispatchError::BadOrigin.into(), - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(1000, 0).saturating_add(normal_base), pays_fee: Pays::Yes, - ..Default::default() + class: Default::default(), }, } .into(), @@ -474,10 +481,10 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(9), event: SysEvent::ExtrinsicFailed { dispatch_error: DispatchError::BadOrigin.into(), - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(800, 0).saturating_add(normal_base), pays_fee: Pays::Yes, - ..Default::default() + class: Default::default(), }, } .into(), @@ -487,10 +494,10 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { phase: Phase::ApplyExtrinsic(10), event: SysEvent::ExtrinsicFailed { dispatch_error: DispatchError::BadOrigin.into(), - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(800, 0).saturating_add(normal_base), pays_fee: Pays::No, - ..Default::default() + class: Default::default(), }, } .into(), @@ -499,10 +506,10 @@ fn deposit_event_uses_actual_weight_and_pays_fee() { EventRecord { phase: Phase::ApplyExtrinsic(11), event: SysEvent::ExtrinsicSuccess { - dispatch_info: DispatchInfo { + dispatch_info: DispatchEventInfo { weight: Weight::from_parts(300, 0).saturating_add(operational_base), class: DispatchClass::Operational, - ..Default::default() + pays_fee: Default::default(), }, } .into(), diff --git a/substrate/frame/transaction-payment/Cargo.toml b/substrate/frame/transaction-payment/Cargo.toml index 4161a97f3cd..afa03ceb12e 100644 --- a/substrate/frame/transaction-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/Cargo.toml @@ -21,6 +21,7 @@ codec = { features = [ ], workspace = true } scale-info = { features = ["derive"], workspace = true } serde = { optional = true, workspace = true, default-features = true } +frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } @@ -35,6 +36,7 @@ pallet-balances = { workspace = true, default-features = true } default = ["std"] std = [ "codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "pallet-balances/std", @@ -44,6 +46,13 @@ std = [ "sp-io/std", "sp-runtime/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml b/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml index e6a60e9c850..7c98d157f6f 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/Cargo.toml @@ -18,6 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # Substrate dependencies sp-runtime = { workspace = true } +frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } pallet-asset-conversion = { workspace = true } @@ -36,6 +37,7 @@ pallet-balances = { workspace = true, default-features = true } default = ["std"] std = [ "codec/std", + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "pallet-asset-conversion/std", @@ -48,6 +50,16 @@ std = [ "sp-runtime/std", "sp-storage/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-asset-conversion/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/README.md b/substrate/frame/transaction-payment/asset-conversion-tx-payment/README.md index eccba773673..fcd1527526e 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/README.md +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/README.md @@ -16,6 +16,6 @@ asset. ### Integration This pallet wraps FRAME's transaction payment pallet and functions as a replacement. This means you should include both pallets in your `construct_runtime` macro, but only include this -pallet's [`SignedExtension`] ([`ChargeAssetTxPayment`]). +pallet's [`TransactionExtension`] ([`ChargeAssetTxPayment`]). License: Apache-2.0 diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/benchmarking.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/benchmarking.rs new file mode 100644 index 00000000000..97eff03d849 --- /dev/null +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/benchmarking.rs @@ -0,0 +1,127 @@ +// 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. + +//! Benchmarks for Asset Conversion Tx Payment Pallet's transaction extension + +extern crate alloc; + +use super::*; +use crate::Pallet; +use frame_benchmarking::v2::*; +use frame_support::{ + dispatch::{DispatchInfo, PostDispatchInfo}, + pallet_prelude::*, +}; +use frame_system::RawOrigin; +use sp_runtime::traits::{ + AsSystemOriginSigner, AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, +}; + +#[benchmarks(where + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + T::RuntimeCall: Dispatchable, + T::AssetId: Send + Sync, + BalanceOf: Send + + Sync + + From, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn charge_asset_tx_payment_zero() { + let caller: T::AccountId = account("caller", 0, 0); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(0u64.into(), None); + let inner = frame_system::Call::remark { remark: alloc::vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + call_weight: Weight::zero(), + extension_weight: Weight::zero(), + class: DispatchClass::Normal, + pays_fee: Pays::No, + }; + let post_info = PostDispatchInfo { actual_weight: None, pays_fee: Pays::No }; + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok(post_info)) + .unwrap() + .is_ok()); + } + } + + #[benchmark] + fn charge_asset_tx_payment_native() { + let caller: T::AccountId = account("caller", 0, 0); + let (fun_asset_id, _) = ::BenchmarkHelper::create_asset_id_parameter(1); + ::BenchmarkHelper::setup_balances_and_pool(fun_asset_id, caller.clone()); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(10u64.into(), None); + let inner = frame_system::Call::remark { remark: alloc::vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + call_weight: Weight::from_parts(10, 0), + extension_weight: Weight::zero(), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + // Submit a lower post info weight to trigger the refund path. + let post_info = + PostDispatchInfo { actual_weight: Some(Weight::from_parts(5, 0)), pays_fee: Pays::Yes }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok(post_info)) + .unwrap() + .is_ok()); + } + } + + #[benchmark] + fn charge_asset_tx_payment_asset() { + let caller: T::AccountId = account("caller", 0, 0); + let (fun_asset_id, asset_id) = ::BenchmarkHelper::create_asset_id_parameter(1); + ::BenchmarkHelper::setup_balances_and_pool(fun_asset_id, caller.clone()); + + let tip = 10u64.into(); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(tip, Some(asset_id)); + let inner = frame_system::Call::remark { remark: alloc::vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + call_weight: Weight::from_parts(10, 0), + extension_weight: Weight::zero(), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + // Submit a lower post info weight to trigger the refund path. + let post_info = + PostDispatchInfo { actual_weight: Some(Weight::from_parts(5, 0)), pays_fee: Pays::Yes }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller.clone()).into(), &call, &info, 0, |_| Ok( + post_info + )) + .unwrap() + .is_ok()); + } + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime); +} diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs index 825a35e6213..787f6b122e8 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs @@ -20,8 +20,8 @@ //! //! ## Overview //! -//! This pallet provides a `SignedExtension` with an optional `AssetId` that specifies the asset -//! to be used for payment (defaulting to the native token on `None`). It expects an +//! This pallet provides a `TransactionExtension` with an optional `AssetId` that specifies the +//! asset to be used for payment (defaulting to the native token on `None`). It expects an //! [`OnChargeAssetTransaction`] implementation analogous to [`pallet-transaction-payment`]. The //! included [`SwapAssetAdapter`] (implementing [`OnChargeAssetTransaction`]) determines the //! fee amount by converting the fee calculated by [`pallet-transaction-payment`] in the native @@ -31,7 +31,7 @@ //! //! This pallet does not have any dispatchable calls or storage. It wraps FRAME's Transaction //! Payment pallet and functions as a replacement. This means you should include both pallets in -//! your `construct_runtime` macro, but only include this pallet's [`SignedExtension`] +//! your `construct_runtime` macro, but only include this pallet's [`TransactionExtension`] //! ([`ChargeAssetTxPayment`]). //! //! ## Terminology @@ -50,21 +50,29 @@ use frame_support::{ traits::IsType, DefaultNoBound, }; -use pallet_transaction_payment::OnChargeTransaction; +use pallet_transaction_payment::{ChargeTransactionPayment, OnChargeTransaction}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, - transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction}, + traits::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, RefundWeight, + TransactionExtension, ValidateResult, Zero, + }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, }; #[cfg(test)] mod mock; #[cfg(test)] mod tests; +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod payment; -use frame_support::traits::tokens::AssetId; +use frame_support::{pallet_prelude::Weight, traits::tokens::AssetId}; pub use payment::*; +pub use weights::WeightInfo; /// Balance type alias for balances of the chain's native asset. pub(crate) type BalanceOf = as OnChargeTransaction>::Balance; @@ -112,11 +120,30 @@ pub mod pallet { Balance = BalanceOf, AssetId = Self::AssetId, >; + /// The weight information of this pallet. + type WeightInfo: WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + /// Benchmark helper + type BenchmarkHelper: BenchmarkHelperTrait< + Self::AccountId, + Self::AssetId, + <::OnChargeAssetTransaction as OnChargeAssetTransaction>::AssetId, + >; } #[pallet::pallet] pub struct Pallet(_); + #[cfg(feature = "runtime-benchmarks")] + /// Helper trait to benchmark the `ChargeAssetTxPayment` transaction extension. + pub trait BenchmarkHelperTrait { + /// Returns the `AssetId` to be used in the liquidity pool by the benchmarking code. + fn create_asset_id_parameter(id: u32) -> (FunAssetIdParameter, AssetIdParameter); + /// Create a liquidity pool for a given asset and sufficiently endow accounts to benchmark + /// the extension. + fn setup_balances_and_pool(asset_id: FunAssetIdParameter, account: AccountId); + } + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -168,9 +195,8 @@ where who: &T::AccountId, call: &T::RuntimeCall, info: &DispatchInfoOf, - len: usize, + fee: BalanceOf, ) -> Result<(BalanceOf, InitialPayment), TransactionValidityError> { - let fee = pallet_transaction_payment::Pallet::::compute_fee(len as u32, info, self.tip); debug_assert!(self.tip <= fee, "tip should be included in the computed fee"); if fee.is_zero() { Ok((fee, InitialPayment::Nothing)) @@ -189,6 +215,28 @@ where .map(|payment| (fee, InitialPayment::Native(payment))) } } + + /// Fee withdrawal logic dry-run that dispatches to either `OnChargeAssetTransaction` or + /// `OnChargeTransaction`. + fn can_withdraw_fee( + &self, + who: &T::AccountId, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + fee: BalanceOf, + ) -> Result<(), TransactionValidityError> { + debug_assert!(self.tip <= fee, "tip should be included in the computed fee"); + if fee.is_zero() { + Ok(()) + } else if let Some(asset_id) = &self.asset_id { + T::OnChargeAssetTransaction::can_withdraw_fee(who, asset_id.clone(), fee.into()) + } else { + as OnChargeTransaction>::can_withdraw_fee( + who, call, info, fee, self.tip, + ) + .map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() }) + } + } } impl core::fmt::Debug for ChargeAssetTxPayment { @@ -202,108 +250,179 @@ impl core::fmt::Debug for ChargeAssetTxPayment { } } -impl SignedExtension for ChargeAssetTxPayment +/// The info passed between the validate and prepare steps for the `ChargeAssetTxPayment` extension. +pub enum Val { + Charge { + tip: BalanceOf, + // who paid the fee + who: T::AccountId, + // transaction fee + fee: BalanceOf, + }, + NoCharge, +} + +/// The info passed between the prepare and post-dispatch steps for the `ChargeAssetTxPayment` +/// extension. +pub enum Pre { + Charge { + tip: BalanceOf, + // who paid the fee + who: T::AccountId, + // imbalance resulting from withdrawing the fee + initial_payment: InitialPayment, + // weight used by the extension + weight: Weight, + }, + NoCharge { + // weight initially estimated by the extension, to be refunded + refund: Weight, + }, +} + +impl TransactionExtension for ChargeAssetTxPayment where T::RuntimeCall: Dispatchable, - BalanceOf: Send + Sync, + BalanceOf: Send + Sync + From, T::AssetId: Send + Sync, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, { const IDENTIFIER: &'static str = "ChargeAssetTxPayment"; - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); - type Pre = ( - // tip - BalanceOf, - // who paid the fee - Self::AccountId, - // imbalance resulting from withdrawing the fee - InitialPayment, - ); + type Implicit = (); + type Val = Val; + type Pre = Pre; - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) + fn weight(&self, _: &T::RuntimeCall) -> Weight { + if self.asset_id.is_some() { + ::WeightInfo::charge_asset_tx_payment_asset() + } else { + ::WeightInfo::charge_asset_tx_payment_native() + } } fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> TransactionValidity { - use pallet_transaction_payment::ChargeTransactionPayment; - let (fee, _) = self.withdraw_fee(who, call, info, len)?; + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let Some(who) = origin.as_system_origin_signer() else { + return Ok((ValidTransaction::default(), Val::NoCharge, origin)) + }; + // Non-mutating call of `compute_fee` to calculate the fee used in the transaction priority. + let fee = pallet_transaction_payment::Pallet::::compute_fee(len as u32, info, self.tip); + self.can_withdraw_fee(&who, call, info, fee)?; let priority = ChargeTransactionPayment::::get_priority(info, len, self.tip, fee); - Ok(ValidTransaction { priority, ..Default::default() }) + let validity = ValidTransaction { priority, ..Default::default() }; + let val = Val::Charge { tip: self.tip, who: who.clone(), fee }; + Ok((validity, val, origin)) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + val: Self::Val, + _origin: &::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + _len: usize, ) -> Result { - let (_fee, initial_payment) = self.withdraw_fee(who, call, info, len)?; - Ok((self.tip, who.clone(), initial_payment)) + match val { + Val::Charge { tip, who, fee } => { + // Mutating call of `withdraw_fee` to actually charge for the transaction. + let (_fee, initial_payment) = self.withdraw_fee(&who, call, info, fee)?; + Ok(Pre::Charge { tip, who, initial_payment, weight: self.weight(call) }) + }, + Val::NoCharge => Ok(Pre::NoCharge { refund: self.weight(call) }), + } } - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, len: usize, _result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - if let Some((tip, who, initial_payment)) = pre { - match initial_payment { - InitialPayment::Native(already_withdrawn) => { - let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( - len as u32, info, post_info, tip, - ); - T::OnChargeTransaction::correct_and_deposit_fee( - &who, - info, - post_info, - actual_fee, - tip, - already_withdrawn, - )?; - pallet_transaction_payment::Pallet::::deposit_fee_paid_event( - who, actual_fee, tip, - ); - }, - InitialPayment::Asset((asset_id, already_withdrawn)) => { - let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( - len as u32, info, post_info, tip, - ); - let converted_fee = T::OnChargeAssetTransaction::correct_and_deposit_fee( - &who, - info, - post_info, - actual_fee, - tip, - asset_id.clone(), - already_withdrawn, - )?; - Pallet::::deposit_event(Event::::AssetTxFeePaid { - who, - actual_fee: converted_fee, - tip, - asset_id, - }); - }, - InitialPayment::Nothing => { - // `actual_fee` should be zero here for any signed extrinsic. It would be - // non-zero here in case of unsigned extrinsics as they don't pay fees but - // `compute_actual_fee` is not aware of them. In both cases it's fine to just - // move ahead without adjusting the fee, though, so we do nothing. - debug_assert!(tip.is_zero(), "tip should be zero if initial fee was zero."); - }, - } - } + ) -> Result { + let (tip, who, initial_payment, extension_weight) = match pre { + Pre::Charge { tip, who, initial_payment, weight } => + (tip, who, initial_payment, weight), + Pre::NoCharge { refund } => { + // No-op: Refund everything + return Ok(refund) + }, + }; - Ok(()) + match initial_payment { + InitialPayment::Native(already_withdrawn) => { + // Take into account the weight used by this extension before calculating the + // refund. + let actual_ext_weight = ::WeightInfo::charge_asset_tx_payment_native(); + let unspent_weight = extension_weight.saturating_sub(actual_ext_weight); + let mut actual_post_info = *post_info; + actual_post_info.refund(unspent_weight); + let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( + len as u32, + info, + &actual_post_info, + tip, + ); + T::OnChargeTransaction::correct_and_deposit_fee( + &who, + info, + &actual_post_info, + actual_fee, + tip, + already_withdrawn, + )?; + pallet_transaction_payment::Pallet::::deposit_fee_paid_event( + who, actual_fee, tip, + ); + Ok(unspent_weight) + }, + InitialPayment::Asset((asset_id, already_withdrawn)) => { + // Take into account the weight used by this extension before calculating the + // refund. + let actual_ext_weight = ::WeightInfo::charge_asset_tx_payment_asset(); + let unspent_weight = extension_weight.saturating_sub(actual_ext_weight); + let mut actual_post_info = *post_info; + actual_post_info.refund(unspent_weight); + let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( + len as u32, + info, + &actual_post_info, + tip, + ); + let converted_fee = T::OnChargeAssetTransaction::correct_and_deposit_fee( + &who, + info, + &actual_post_info, + actual_fee, + tip, + asset_id.clone(), + already_withdrawn, + )?; + + Pallet::::deposit_event(Event::::AssetTxFeePaid { + who, + actual_fee: converted_fee, + tip, + asset_id, + }); + + Ok(unspent_weight) + }, + InitialPayment::Nothing => { + // `actual_fee` should be zero here for any signed extrinsic. It would be + // non-zero here in case of unsigned extrinsics as they don't pay fees but + // `compute_actual_fee` is not aware of them. In both cases it's fine to just + // move ahead without adjusting the fee, though, so we do nothing. + debug_assert!(tip.is_zero(), "tip should be zero if initial fee was zero."); + Ok(extension_weight + .saturating_sub(::WeightInfo::charge_asset_tx_payment_zero())) + }, + } } } diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs index acfd43d0a7c..a86b86c223e 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs @@ -145,6 +145,14 @@ impl OnUnbalanced::AccountId, } } +pub struct MockTxPaymentWeights; + +impl pallet_transaction_payment::WeightInfo for MockTxPaymentWeights { + fn charge_transaction_payment() -> Weight { + Weight::from_parts(10, 0) + } +} + pub struct DealWithFungiblesFees; impl OnUnbalanced> for DealWithFungiblesFees { fn on_unbalanceds( @@ -167,8 +175,8 @@ impl pallet_transaction_payment::Config for Runtime { type OnChargeTransaction = FungibleAdapter; type WeightToFee = WeightToFee; type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = MockTxPaymentWeights; } type AssetId = u32; @@ -266,9 +274,95 @@ impl pallet_asset_conversion::Config for Runtime { } } +/// Weights used in testing. +pub struct MockWeights; + +impl WeightInfo for MockWeights { + fn charge_asset_tx_payment_zero() -> Weight { + Weight::from_parts(0, 0) + } + + fn charge_asset_tx_payment_native() -> Weight { + Weight::from_parts(15, 0) + } + + fn charge_asset_tx_payment_asset() -> Weight { + Weight::from_parts(20, 0) + } +} + impl Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = NativeOrWithId; type OnChargeAssetTransaction = SwapAssetAdapter; + type WeightInfo = MockWeights; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = Helper; +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_test_ext() -> sp_io::TestExternalities { + let base_weight = 5; + let balance_factor = 100; + crate::tests::ExtBuilder::default() + .balance_factor(balance_factor) + .base_weight(Weight::from_parts(base_weight, 0)) + .build() +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct Helper; + +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelperTrait, NativeOrWithId> for Helper { + fn create_asset_id_parameter(id: u32) -> (NativeOrWithId, NativeOrWithId) { + (NativeOrWithId::WithId(id), NativeOrWithId::WithId(id)) + } + + fn setup_balances_and_pool(asset_id: NativeOrWithId, account: u64) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + use sp_runtime::traits::StaticLookup; + let NativeOrWithId::WithId(asset_idx) = asset_id.clone() else { unimplemented!() }; + assert_ok!(Assets::force_create( + RuntimeOrigin::root(), + asset_idx.into(), + 42, /* owner */ + true, /* is_sufficient */ + 1, + )); + + let lp_provider = 12; + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), lp_provider, u64::MAX / 2)); + let lp_provider_account = ::Lookup::unlookup(lp_provider); + assert_ok!(Assets::mint_into(asset_idx, &lp_provider_account, u64::MAX / 2)); + + let token_1 = Box::new(NativeOrWithId::Native); + let token_2 = Box::new(asset_id); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider), + token_1.clone(), + token_2.clone() + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(lp_provider), + token_1, + token_2, + (u32::MAX / 8).into(), // 1 desired + u32::MAX.into(), // 2 desired + 1, // 1 min + 1, // 2 min + lp_provider_account, + )); + + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&account, u32::MAX.into()); + + let beneficiary = ::Lookup::unlookup(account); + let balance = 1000; + + assert_ok!(Assets::mint_into(asset_idx, &beneficiary, balance)); + assert_eq!(Assets::balance(asset_idx, account), balance); + } } diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs index dc7faecd560..05182c3c9ee 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs @@ -23,7 +23,7 @@ use frame_support::{ defensive, ensure, traits::{ fungibles, - tokens::{Balance, Fortitude, Precision, Preservation}, + tokens::{Balance, Fortitude, Precision, Preservation, WithdrawConsequence}, Defensive, OnUnbalanced, SameOrOther, }, unsigned::TransactionValidityError, @@ -56,6 +56,15 @@ pub trait OnChargeAssetTransaction { tip: Self::Balance, ) -> Result; + /// Ensure payment of the transaction fees can be withdrawn. + /// + /// Note: The `fee` already includes the tip. + fn can_withdraw_fee( + who: &T::AccountId, + asset_id: Self::AssetId, + fee: Self::Balance, + ) -> Result<(), TransactionValidityError>; + /// Refund any overpaid fees and deposit the corrected amount. /// The actual fee gets calculated once the transaction is executed. /// @@ -162,6 +171,51 @@ where Ok((fee_credit, asset_fee)) } + /// Dry run of swap & withdraw the predicted fee from the transaction origin. + /// + /// Note: The `fee` already includes the tip. + /// + /// Returns an error if the total amount in native currency can't be exchanged for `asset_id`. + fn can_withdraw_fee( + who: &T::AccountId, + asset_id: Self::AssetId, + fee: BalanceOf, + ) -> Result<(), TransactionValidityError> { + if asset_id == A::get() { + // The `asset_id` is the target asset, we do not need to swap. + match F::can_withdraw(asset_id.clone(), who, fee) { + WithdrawConsequence::BalanceLow | + WithdrawConsequence::UnknownAsset | + WithdrawConsequence::Underflow | + WithdrawConsequence::Overflow | + WithdrawConsequence::Frozen => + return Err(TransactionValidityError::from(InvalidTransaction::Payment)), + WithdrawConsequence::Success | + WithdrawConsequence::ReducedToZero(_) | + WithdrawConsequence::WouldDie => return Ok(()), + } + } + + let asset_fee = + S::quote_price_tokens_for_exact_tokens(asset_id.clone(), A::get(), fee, true) + .ok_or(InvalidTransaction::Payment)?; + + // Ensure we can withdraw enough `asset_id` for the swap. + match F::can_withdraw(asset_id.clone(), who, asset_fee) { + WithdrawConsequence::BalanceLow | + WithdrawConsequence::UnknownAsset | + WithdrawConsequence::Underflow | + WithdrawConsequence::Overflow | + WithdrawConsequence::Frozen => + return Err(TransactionValidityError::from(InvalidTransaction::Payment)), + WithdrawConsequence::Success | + WithdrawConsequence::ReducedToZero(_) | + WithdrawConsequence::WouldDie => {}, + }; + + Ok(()) + } + fn correct_and_deposit_fee( who: &T::AccountId, _dispatch_info: &DispatchInfoOf<::RuntimeCall>, diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs index aab65719953..4312aa9a452 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs @@ -17,19 +17,23 @@ use super::*; use frame_support::{ assert_ok, - dispatch::{DispatchInfo, PostDispatchInfo}, + dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, traits::{ fungible::{Inspect, NativeOrWithId}, fungibles::{Inspect as FungiblesInspect, Mutate}, tokens::{Fortitude, Precision, Preservation}, + OriginTrait, }, weights::Weight, }; use frame_system as system; use mock::{ExtrinsicBaseWeight, *}; use pallet_balances::Call as BalancesCall; -use sp_runtime::{traits::StaticLookup, BuildStorage}; +use sp_runtime::{ + traits::{DispatchTransaction, StaticLookup}, + BuildStorage, +}; const CALL: &::RuntimeCall = &RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); @@ -92,7 +96,7 @@ impl ExtBuilder { /// create a transaction info struct from weight. Handy to avoid building the whole struct. pub fn info_from_weight(w: Weight) -> DispatchInfo { // pays_fee: Pays::Yes -- class: DispatchClass::Normal - DispatchInfo { weight: w, ..Default::default() } + DispatchInfo { call_weight: w, ..Default::default() } } fn post_info_from_weight(w: Weight) -> PostDispatchInfo { @@ -161,35 +165,45 @@ fn transaction_payment_in_native_possible() { .build() .execute_with(|| { let len = 10; - let pre = ChargeAssetTxPayment::::from(0, None) - .pre_dispatch(&1, CALL, &info_from_weight(WEIGHT_5), len) - .unwrap(); + let mut info = info_from_weight(WEIGHT_5); + let ext = ChargeAssetTxPayment::::from(0, None); + info.extension_weight = ext.weight(CALL); + let (pre, _) = ext.validate_and_prepare(Some(1).into(), CALL, &info, len).unwrap(); let initial_balance = 10 * balance_factor; - assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); + assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 15 - 10); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), - &info_from_weight(WEIGHT_5), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, + &info, &default_post_info(), len, - &Ok(()) + &Ok(()), )); - assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); + assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 15 - 10); - let pre = ChargeAssetTxPayment::::from(5 /* tipped */, None) - .pre_dispatch(&2, CALL, &info_from_weight(WEIGHT_100), len) - .unwrap(); + let mut info = info_from_weight(WEIGHT_100); + let ext = ChargeAssetTxPayment::::from(5 /* tipped */, None); + let extension_weight = ext.weight(CALL); + info.extension_weight = extension_weight; + let (pre, _) = ext.validate_and_prepare(Some(2).into(), CALL, &info, len).unwrap(); let initial_balance_for_2 = 20 * balance_factor; - assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 100 - 5); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), - &info_from_weight(WEIGHT_100), - &post_info_from_weight(WEIGHT_50), + assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 100 - 15 - 5); + let call_actual_weight = WEIGHT_50; + let post_info = post_info_from_weight( + info.call_weight + .saturating_sub(call_actual_weight) + .saturating_add(extension_weight), + ); + // The extension weight refund should be taken into account in `post_dispatch`. + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, + &info, + &post_info, len, - &Ok(()) + &Ok(()), )); - assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 50 - 5); + assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 50 - 15 - 5); }); } @@ -240,8 +254,8 @@ fn transaction_payment_in_asset_possible() { let fee_in_asset = input_quote.unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); // assert that native balance is not used assert_eq!(Balances::free_balance(caller), 10 * balance_factor); @@ -255,12 +269,12 @@ fn transaction_payment_in_asset_possible() { amount: fee_in_asset, })); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_weight(WEIGHT_5), // estimated tx weight &default_post_info(), // weight actually used == estimated len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); @@ -298,12 +312,8 @@ fn transaction_payment_in_asset_fails_if_no_pool_for_that_asset() { assert_eq!(Assets::balance(asset_id, caller), balance); let len = 10; - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())).pre_dispatch( - &caller, - CALL, - &info_from_weight(WEIGHT_5), - len, - ); + let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_weight(WEIGHT_5), len); // As there is no pool in the dex set up for this asset, conversion should fail. assert!(pre.is_err()); @@ -353,8 +363,8 @@ fn transaction_payment_without_fee() { assert_eq!(input_quote, Some(201)); let fee_in_asset = input_quote.unwrap(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); // assert that native balance is not used @@ -371,12 +381,12 @@ fn transaction_payment_without_fee() { .unwrap(); assert_eq!(refund, 199); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_weight(WEIGHT_5), &post_info_from_pays(Pays::No), len, - &Ok(()) + &Ok(()), )); // caller should get refunded @@ -419,24 +429,29 @@ fn asset_transaction_payment_with_tip_and_refund() { let weight = 100; let tip = 5; + let ext = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())); + let ext_weight = ext.weight(CALL); let len = 10; - let fee_in_native = base_weight + weight + len as u64 + tip; + let fee_in_native = base_weight + weight + ext_weight.ref_time() + len as u64 + tip; let input_quote = AssetConversion::quote_price_tokens_for_exact_tokens( NativeOrWithId::WithId(asset_id), NativeOrWithId::Native, fee_in_native, true, ); - assert_eq!(input_quote, Some(1206)); + assert_eq!(input_quote, Some(1407)); let fee_in_asset = input_quote.unwrap(); - let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len) - .unwrap(); + let mut info = info_from_weight(WEIGHT_100); + let ext = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())); + info.extension_weight = ext.weight(CALL); + let (pre, _) = ext.validate_and_prepare(Some(caller).into(), CALL, &info, len).unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); let final_weight = 50; - let expected_fee = fee_in_native - final_weight - tip; + let weight_refund = weight - final_weight; + let ext_weight_refund = ext_weight - MockWeights::charge_asset_tx_payment_asset(); + let expected_fee = fee_in_native - weight_refund - ext_weight_refund.ref_time() - tip; let expected_token_refund = AssetConversion::quote_price_exact_tokens_for_tokens( NativeOrWithId::Native, NativeOrWithId::WithId(asset_id), @@ -451,12 +466,13 @@ fn asset_transaction_payment_with_tip_and_refund() { amount: fee_in_asset, })); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), - &info_from_weight(WEIGHT_100), - &post_info_from_weight(WEIGHT_50), + let post_info = post_info_from_weight(WEIGHT_50.saturating_add(ext_weight)); + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, + &info, + &post_info, len, - &Ok(()) + &Ok(()), )); assert_eq!(TipUnbalancedAmount::get(), tip); @@ -522,18 +538,18 @@ fn payment_from_account_with_only_assets() { .unwrap(); assert_eq!(fee_in_asset, 201); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_5), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_weight(WEIGHT_5), len) .unwrap(); // check that fee was charged in the given asset assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_weight(WEIGHT_5), &default_post_info(), len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); assert_eq!(Balances::free_balance(caller), 0); @@ -578,18 +594,18 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { // there will be no conversion when the fee is zero { - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) - .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` implies there are no fees assert_eq!(Assets::balance(asset_id, caller), balance); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_pays(Pays::No), &post_info_from_pays(Pays::No), len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance); } @@ -604,17 +620,22 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { ) .unwrap(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); }); @@ -654,14 +675,16 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { // calculated fee is greater than 0 assert!(fee > 0); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) - .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id.into())) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` implies no pre-dispatch fees assert_eq!(Assets::balance(asset_id, caller), balance); - let (_tip, _who, initial_payment) = ⪯ + let Pre::Charge { initial_payment, .. } = &pre else { + panic!("Expected Charge"); + }; let not_paying = match initial_payment { &InitialPayment::Nothing => true, _ => false, @@ -670,63 +693,12 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { // `Pays::Yes` on post-dispatch does not mean we pay (we never charge more than the // initial fee) - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_pays(Pays::No), &post_info_from_pays(Pays::Yes), len, - &Ok(()) - )); - assert_eq!(Assets::balance(asset_id, caller), balance); - }); -} - -#[test] -fn post_dispatch_fee_is_zero_if_unsigned_pre_dispatch_fee_is_zero() { - let base_weight = 1; - ExtBuilder::default() - .balance_factor(100) - .base_weight(Weight::from_parts(base_weight, 0)) - .build() - .execute_with(|| { - // create the asset - let asset_id = 1; - let min_balance = 100; - assert_ok!(Assets::force_create( - RuntimeOrigin::root(), - asset_id.into(), - 42, /* owner */ - true, /* is_sufficient */ - min_balance - )); - - // mint into the caller account - let caller = 333; - let beneficiary = ::Lookup::unlookup(caller); - let balance = 1000; - - assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); - assert_eq!(Assets::balance(asset_id, caller), balance); - - let weight = 1; - let len = 1; - ChargeAssetTxPayment::::pre_dispatch_unsigned( - CALL, - &info_from_weight(Weight::from_parts(weight, 0)), - len, - ) - .unwrap(); - - assert_eq!(Assets::balance(asset_id, caller), balance); - - // `Pays::Yes` on post-dispatch does not mean we pay (we never charge more than the - // initial fee) - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - None, - &info_from_weight(Weight::from_parts(weight, 0)), - &post_info_from_pays(Pays::Yes), - len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance); }); @@ -749,25 +721,34 @@ fn fee_with_native_asset_passed_with_id() { assert_eq!(Balances::free_balance(caller), caller_balance); let tip = 10; - let weight = 100; + let call_weight = 100; + let ext = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())); + let extension_weight = ext.weight(CALL); let len = 5; - let initial_fee = base_weight + weight + len as u64 + tip; + let initial_fee = + base_weight + call_weight + extension_weight.ref_time() + len as u64 + tip; - let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len) - .unwrap(); + let mut info = info_from_weight(WEIGHT_100); + info.extension_weight = extension_weight; + let (pre, _) = ext.validate_and_prepare(Some(caller).into(), CALL, &info, len).unwrap(); assert_eq!(Balances::free_balance(caller), caller_balance - initial_fee); let final_weight = 50; + // No refunds from the extension weight itself. let expected_fee = initial_fee - final_weight; - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), - &info_from_weight(WEIGHT_100), - &post_info_from_weight(WEIGHT_50), - len, - &Ok(()) - )); + let post_info = post_info_from_weight(WEIGHT_50.saturating_add(extension_weight)); + assert_eq!( + ChargeAssetTxPayment::::post_dispatch_details( + pre, + &info_from_weight(WEIGHT_100), + &post_info, + len, + &Ok(()), + ) + .unwrap(), + Weight::zero() + ); assert_eq!(Balances::free_balance(caller), caller_balance - expected_fee); @@ -809,10 +790,13 @@ fn transfer_add_and_remove_account() { assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); assert_eq!(Assets::balance(asset_id, caller), balance); - let weight = 100; + let call_weight = 100; let tip = 5; + let ext = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())); + let extension_weight = ext.weight(CALL); let len = 10; - let fee_in_native = base_weight + weight + len as u64 + tip; + let fee_in_native = + base_weight + call_weight + extension_weight.ref_time() + len as u64 + tip; let input_quote = AssetConversion::quote_price_tokens_for_exact_tokens( NativeOrWithId::WithId(asset_id), NativeOrWithId::Native, @@ -822,8 +806,10 @@ fn transfer_add_and_remove_account() { assert!(!input_quote.unwrap().is_zero()); let fee_in_asset = input_quote.unwrap(); - let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())) - .pre_dispatch(&caller, CALL, &info_from_weight(WEIGHT_100), len) + let mut info = info_from_weight(WEIGHT_100); + info.extension_weight = extension_weight; + let (pre, _) = ChargeAssetTxPayment::::from(tip, Some(asset_id.into())) + .validate_and_prepare(Some(caller).into(), CALL, &info, len) .unwrap(); assert_eq!(Assets::balance(asset_id, &caller), balance - fee_in_asset); @@ -838,7 +824,8 @@ fn transfer_add_and_remove_account() { Fortitude::Force )); - let final_weight = 50; + // Actual call weight + actual extension weight. + let final_weight = 50 + 20; let final_fee_in_native = fee_in_native - final_weight - tip; let token_refund = AssetConversion::quote_price_exact_tokens_for_tokens( NativeOrWithId::Native, @@ -851,12 +838,12 @@ fn transfer_add_and_remove_account() { // make sure the refund amount is enough to create the account. assert!(token_refund >= min_balance); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), - &info_from_weight(WEIGHT_100), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, + &info, &post_info_from_weight(WEIGHT_50), len, - &Ok(()) + &Ok(()), )); // fee paid with no refund. @@ -867,3 +854,40 @@ fn transfer_add_and_remove_account() { assert_eq!(Assets::balance(asset_id, caller), 0); }); } + +#[test] +fn no_fee_and_no_weight_for_other_origins() { + ExtBuilder::default().build().execute_with(|| { + let ext = ChargeAssetTxPayment::::from(0, None); + + let mut info = CALL.get_dispatch_info(); + info.extension_weight = ext.weight(CALL); + + // Ensure we test the refund. + assert!(info.extension_weight != Weight::zero()); + + let len = CALL.encoded_size(); + + let origin = frame_system::RawOrigin::Root.into(); + let (pre, origin) = ext.validate_and_prepare(origin, CALL, &info, len).unwrap(); + + assert!(origin.as_system_ref().unwrap().is_root()); + + let pd_res = Ok(()); + let mut post_info = frame_support::dispatch::PostDispatchInfo { + actual_weight: Some(info.total_weight()), + pays_fee: Default::default(), + }; + + as TransactionExtension>::post_dispatch( + pre, + &info, + &mut post_info, + len, + &pd_res, + ) + .unwrap(); + + assert_eq!(post_info.actual_weight, Some(info.call_weight)); + }) +} diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs new file mode 100644 index 00000000000..f95e49f8073 --- /dev/null +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs @@ -0,0 +1,150 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_asset_conversion_tx_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, 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` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// ./target/production/substrate-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_asset_conversion_tx_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_asset_conversion_tx_payment`. +pub trait WeightInfo { + fn charge_asset_tx_payment_zero() -> Weight; + fn charge_asset_tx_payment_native() -> Weight; + fn charge_asset_tx_payment_asset() -> Weight; +} + +/// Weights for `pallet_asset_conversion_tx_payment` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 628_000 picoseconds. + Weight::from_parts(694_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 34_410_000 picoseconds. + Weight::from_parts(35_263_000, 1733) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `888` + // Estimated: `6208` + // Minimum execution time: 112_432_000 picoseconds. + Weight::from_parts(113_992_000, 6208) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 628_000 picoseconds. + Weight::from_parts(694_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 34_410_000 picoseconds. + Weight::from_parts(35_263_000, 1733) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `888` + // Estimated: `6208` + // Minimum execution time: 112_432_000 picoseconds. + Weight::from_parts(113_992_000, 6208) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } +} diff --git a/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml b/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml index 8d39dea8c62..89fe5bfe7a4 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml @@ -64,6 +64,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ diff --git a/substrate/frame/transaction-payment/asset-tx-payment/README.md b/substrate/frame/transaction-payment/asset-tx-payment/README.md index fc860347d85..933ce13b0ee 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/README.md +++ b/substrate/frame/transaction-payment/asset-tx-payment/README.md @@ -16,6 +16,6 @@ asset. ### Integration This pallet wraps FRAME's transaction payment pallet and functions as a replacement. This means you should include both pallets in your `construct_runtime` macro, but only include this -pallet's [`SignedExtension`] ([`ChargeAssetTxPayment`]). +pallet's [`TransactionExtension`] ([`ChargeAssetTxPayment`]). License: Apache-2.0 diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/benchmarking.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/benchmarking.rs new file mode 100644 index 00000000000..25902bf452b --- /dev/null +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/benchmarking.rs @@ -0,0 +1,131 @@ +// 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. + +//! Benchmarks for Asset Tx Payment Pallet's transaction extension + +extern crate alloc; + +use super::*; +use crate::Pallet; +use frame_benchmarking::v2::*; +use frame_support::{ + dispatch::{DispatchInfo, PostDispatchInfo}, + pallet_prelude::*, +}; +use frame_system::RawOrigin; +use sp_runtime::traits::{ + AsSystemOriginSigner, AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, +}; + +#[benchmarks(where + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + T::RuntimeCall: Dispatchable, + AssetBalanceOf: Send + Sync, + BalanceOf: Send + Sync + From + IsType>, + ChargeAssetIdOf: Send + Sync, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + Credit: IsType>, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn charge_asset_tx_payment_zero() { + let caller: T::AccountId = account("caller", 0, 0); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(0u32.into(), None); + let inner = frame_system::Call::remark { remark: alloc::vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + call_weight: Weight::zero(), + extension_weight: Weight::zero(), + class: DispatchClass::Normal, + pays_fee: Pays::No, + }; + let post_info = PostDispatchInfo { actual_weight: None, pays_fee: Pays::No }; + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok(post_info)) + .unwrap() + .is_ok()); + } + } + + #[benchmark] + fn charge_asset_tx_payment_native() { + let caller: T::AccountId = account("caller", 0, 0); + let (fun_asset_id, _) = ::BenchmarkHelper::create_asset_id_parameter(1); + ::BenchmarkHelper::setup_balances_and_pool(fun_asset_id, caller.clone()); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(10u32.into(), None); + let inner = frame_system::Call::remark { remark: alloc::vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + call_weight: Weight::from_parts(10, 0), + extension_weight: Weight::zero(), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(10, 0)), + pays_fee: Pays::Yes, + }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller).into(), &call, &info, 0, |_| Ok(post_info)) + .unwrap() + .is_ok()); + } + } + + #[benchmark] + fn charge_asset_tx_payment_asset() { + let caller: T::AccountId = account("caller", 0, 0); + let (fun_asset_id, asset_id) = ::BenchmarkHelper::create_asset_id_parameter(1); + ::BenchmarkHelper::setup_balances_and_pool( + fun_asset_id.clone(), + caller.clone(), + ); + let tip = 10u32.into(); + let ext: ChargeAssetTxPayment = ChargeAssetTxPayment::from(tip, Some(asset_id)); + let inner = frame_system::Call::remark { remark: alloc::vec![] }; + let call = T::RuntimeCall::from(inner); + let info = DispatchInfo { + call_weight: Weight::from_parts(10, 0), + extension_weight: Weight::zero(), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(10, 0)), + pays_fee: Pays::Yes, + }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller.clone()).into(), &call, &info, 0, |_| Ok( + post_info + )) + .unwrap() + .is_ok()); + } + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime); +} diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs index 97f1116993f..25aa272ba01 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -31,13 +31,14 @@ //! This pallet wraps FRAME's transaction payment pallet and functions as a replacement. This means //! you should include both pallets in your `construct_runtime` macro, but only include this -//! pallet's [`SignedExtension`] ([`ChargeAssetTxPayment`]). +//! pallet's [`TransactionExtension`] ([`ChargeAssetTxPayment`]). #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode}; use frame_support::{ dispatch::{DispatchInfo, DispatchResult, PostDispatchInfo}, + pallet_prelude::Weight, traits::{ tokens::{ fungibles::{Balanced, Credit, Inspect}, @@ -50,10 +51,11 @@ use frame_support::{ use pallet_transaction_payment::OnChargeTransaction; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, - transaction_validity::{ - InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, + traits::{ + AsSystemOriginSigner, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, RefundWeight, + TransactionExtension, Zero, }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, }; #[cfg(test)] @@ -61,8 +63,14 @@ mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + mod payment; +pub mod weights; + pub use payment::*; +pub use weights::WeightInfo; /// Type aliases used for interaction with `OnChargeTransaction`. pub(crate) type OnChargeTransactionOf = @@ -118,11 +126,30 @@ pub mod pallet { type Fungibles: Balanced; /// The actual transaction charging logic that charges the fees. type OnChargeAssetTransaction: OnChargeAssetTransaction; + /// The weight information of this pallet. + type WeightInfo: WeightInfo; + /// Benchmark helper + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelperTrait< + Self::AccountId, + <::Fungibles as Inspect>::AssetId, + <::OnChargeAssetTransaction as OnChargeAssetTransaction>::AssetId, + >; } #[pallet::pallet] pub struct Pallet(_); + #[cfg(feature = "runtime-benchmarks")] + /// Helper trait to benchmark the `ChargeAssetTxPayment` transaction extension. + pub trait BenchmarkHelperTrait { + /// Returns the `AssetId` to be used in the liquidity pool by the benchmarking code. + fn create_asset_id_parameter(id: u32) -> (FunAssetIdParameter, AssetIdParameter); + /// Create a liquidity pool for a given asset and sufficiently endow accounts to benchmark + /// the extension. + fn setup_balances_and_pool(asset_id: FunAssetIdParameter, account: AccountId); + } + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -170,9 +197,8 @@ where who: &T::AccountId, call: &T::RuntimeCall, info: &DispatchInfoOf, - len: usize, + fee: BalanceOf, ) -> Result<(BalanceOf, InitialPayment), TransactionValidityError> { - let fee = pallet_transaction_payment::Pallet::::compute_fee(len as u32, info, self.tip); debug_assert!(self.tip <= fee, "tip should be included in the computed fee"); if fee.is_zero() { Ok((fee, InitialPayment::Nothing)) @@ -194,6 +220,35 @@ where .map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() }) } } + + /// Fee withdrawal logic dry-run that dispatches to either `OnChargeAssetTransaction` or + /// `OnChargeTransaction`. + fn can_withdraw_fee( + &self, + who: &T::AccountId, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + fee: BalanceOf, + ) -> Result<(), TransactionValidityError> { + debug_assert!(self.tip <= fee, "tip should be included in the computed fee"); + if fee.is_zero() { + Ok(()) + } else if let Some(asset_id) = self.asset_id { + T::OnChargeAssetTransaction::can_withdraw_fee( + who, + call, + info, + asset_id, + fee.into(), + self.tip.into(), + ) + } else { + as OnChargeTransaction>::can_withdraw_fee( + who, call, info, fee, self.tip, + ) + .map_err(|_| -> TransactionValidityError { InvalidTransaction::Payment.into() }) + } + } } impl core::fmt::Debug for ChargeAssetTxPayment { @@ -207,106 +262,184 @@ impl core::fmt::Debug for ChargeAssetTxPayment { } } -impl SignedExtension for ChargeAssetTxPayment +/// The info passed between the validate and prepare steps for the `ChargeAssetTxPayment` extension. +pub enum Val { + Charge { + tip: BalanceOf, + // who paid the fee + who: T::AccountId, + // transaction fee + fee: BalanceOf, + }, + NoCharge, +} + +/// The info passed between the prepare and post-dispatch steps for the `ChargeAssetTxPayment` +/// extension. +pub enum Pre { + Charge { + tip: BalanceOf, + // who paid the fee + who: T::AccountId, + // imbalance resulting from withdrawing the fee + initial_payment: InitialPayment, + // asset_id for the transaction payment + asset_id: Option>, + // weight used by the extension + weight: Weight, + }, + NoCharge { + // weight initially estimated by the extension, to be refunded + refund: Weight, + }, +} + +impl TransactionExtension for ChargeAssetTxPayment where T::RuntimeCall: Dispatchable, AssetBalanceOf: Send + Sync, BalanceOf: Send + Sync + From + IsType>, ChargeAssetIdOf: Send + Sync, Credit: IsType>, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, { const IDENTIFIER: &'static str = "ChargeAssetTxPayment"; - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); - type Pre = ( - // tip - BalanceOf, - // who paid the fee - Self::AccountId, - // imbalance resulting from withdrawing the fee - InitialPayment, - // asset_id for the transaction payment - Option>, - ); + type Implicit = (); + type Val = Val; + type Pre = Pre; - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) + fn weight(&self, _: &T::RuntimeCall) -> Weight { + if self.asset_id.is_some() { + ::WeightInfo::charge_asset_tx_payment_asset() + } else { + ::WeightInfo::charge_asset_tx_payment_native() + } } fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> TransactionValidity { + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { use pallet_transaction_payment::ChargeTransactionPayment; - let (fee, _) = self.withdraw_fee(who, call, info, len)?; + let Some(who) = origin.as_system_origin_signer() else { + return Ok((ValidTransaction::default(), Val::NoCharge, origin)) + }; + // Non-mutating call of `compute_fee` to calculate the fee used in the transaction priority. + let fee = pallet_transaction_payment::Pallet::::compute_fee(len as u32, info, self.tip); + self.can_withdraw_fee(&who, call, info, fee)?; let priority = ChargeTransactionPayment::::get_priority(info, len, self.tip, fee); - Ok(ValidTransaction { priority, ..Default::default() }) + let val = Val::Charge { tip: self.tip, who: who.clone(), fee }; + let validity = ValidTransaction { priority, ..Default::default() }; + Ok((validity, val, origin)) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + val: Self::Val, + _origin: &::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + _len: usize, ) -> Result { - let (_fee, initial_payment) = self.withdraw_fee(who, call, info, len)?; - Ok((self.tip, who.clone(), initial_payment, self.asset_id)) + match val { + Val::Charge { tip, who, fee } => { + // Mutating call of `withdraw_fee` to actually charge for the transaction. + let (_fee, initial_payment) = self.withdraw_fee(&who, call, info, fee)?; + Ok(Pre::Charge { + tip, + who, + initial_payment, + asset_id: self.asset_id, + weight: self.weight(call), + }) + }, + Val::NoCharge => Ok(Pre::NoCharge { refund: self.weight(call) }), + } } - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, len: usize, result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - if let Some((tip, who, initial_payment, asset_id)) = pre { - match initial_payment { - InitialPayment::Native(already_withdrawn) => { - pallet_transaction_payment::ChargeTransactionPayment::::post_dispatch( - Some((tip, who, already_withdrawn)), + ) -> Result { + let (tip, who, initial_payment, asset_id, extension_weight) = match pre { + Pre::Charge { tip, who, initial_payment, asset_id, weight } => + (tip, who, initial_payment, asset_id, weight), + Pre::NoCharge { refund } => { + // No-op: Refund everything + return Ok(refund) + }, + }; + + match initial_payment { + InitialPayment::Native(already_withdrawn) => { + // Take into account the weight used by this extension before calculating the + // refund. + let actual_ext_weight = ::WeightInfo::charge_asset_tx_payment_native(); + let unspent_weight = extension_weight.saturating_sub(actual_ext_weight); + let mut actual_post_info = *post_info; + actual_post_info.refund(unspent_weight); + pallet_transaction_payment::ChargeTransactionPayment::::post_dispatch_details( + pallet_transaction_payment::Pre::Charge { + tip, + who, + imbalance: already_withdrawn, + }, + info, + &actual_post_info, + len, + result, + )?; + Ok(unspent_weight) + }, + InitialPayment::Asset(already_withdrawn) => { + let actual_ext_weight = ::WeightInfo::charge_asset_tx_payment_asset(); + let unspent_weight = extension_weight.saturating_sub(actual_ext_weight); + let mut actual_post_info = *post_info; + actual_post_info.refund(unspent_weight); + let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( + len as u32, + info, + &actual_post_info, + tip, + ); + + let (converted_fee, converted_tip) = + T::OnChargeAssetTransaction::correct_and_deposit_fee( + &who, info, - post_info, - len, - result, + &actual_post_info, + actual_fee.into(), + tip.into(), + already_withdrawn.into(), )?; - }, - InitialPayment::Asset(already_withdrawn) => { - let actual_fee = pallet_transaction_payment::Pallet::::compute_actual_fee( - len as u32, info, post_info, tip, - ); - - let (converted_fee, converted_tip) = - T::OnChargeAssetTransaction::correct_and_deposit_fee( - &who, - info, - post_info, - actual_fee.into(), - tip.into(), - already_withdrawn.into(), - )?; - Pallet::::deposit_event(Event::::AssetTxFeePaid { - who, - actual_fee: converted_fee, - tip: converted_tip, - asset_id, - }); - }, - InitialPayment::Nothing => { - // `actual_fee` should be zero here for any signed extrinsic. It would be - // non-zero here in case of unsigned extrinsics as they don't pay fees but - // `compute_actual_fee` is not aware of them. In both cases it's fine to just - // move ahead without adjusting the fee, though, so we do nothing. - debug_assert!(tip.is_zero(), "tip should be zero if initial fee was zero."); - }, - } + Pallet::::deposit_event(Event::::AssetTxFeePaid { + who, + actual_fee: converted_fee, + tip: converted_tip, + asset_id, + }); + Ok(unspent_weight) + }, + InitialPayment::Nothing => { + // `actual_fee` should be zero here for any signed extrinsic. It would be + // non-zero here in case of unsigned extrinsics as they don't pay fees but + // `compute_actual_fee` is not aware of them. In both cases it's fine to just + // move ahead without adjusting the fee, though, so we do nothing. + debug_assert!(tip.is_zero(), "tip should be zero if initial fee was zero."); + Ok(extension_weight + .saturating_sub(::WeightInfo::charge_asset_tx_payment_zero())) + }, } - - Ok(()) } } diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs index e84df1e4eb9..fce029bb4bf 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -105,14 +105,22 @@ impl WeightToFeeT for TransactionByteFee { } } +pub struct MockTxPaymentWeights; + +impl pallet_transaction_payment::WeightInfo for MockTxPaymentWeights { + fn charge_transaction_payment() -> Weight { + Weight::from_parts(10, 0) + } +} + #[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = FungibleAdapter; type WeightToFee = WeightToFee; type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = MockTxPaymentWeights; } type AssetId = u32; @@ -168,6 +176,23 @@ impl HandleCredit for CreditToBlockAuthor { } } +/// Weights used in testing. +pub struct MockWeights; + +impl WeightInfo for MockWeights { + fn charge_asset_tx_payment_zero() -> Weight { + Weight::from_parts(0, 0) + } + + fn charge_asset_tx_payment_native() -> Weight { + Weight::from_parts(15, 0) + } + + fn charge_asset_tx_payment_asset() -> Weight { + Weight::from_parts(20, 0) + } +} + impl Config for Runtime { type RuntimeEvent = RuntimeEvent; type Fungibles = Assets; @@ -175,4 +200,56 @@ impl Config for Runtime { pallet_assets::BalanceToAssetBalance, CreditToBlockAuthor, >; + type WeightInfo = MockWeights; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = Helper; +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_test_ext() -> sp_io::TestExternalities { + let base_weight = 5; + let balance_factor = 100; + crate::tests::ExtBuilder::default() + .balance_factor(balance_factor) + .base_weight(Weight::from_parts(base_weight, 0)) + .build() +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct Helper; + +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelperTrait for Helper { + fn create_asset_id_parameter(id: u32) -> (u32, u32) { + (id.into(), id.into()) + } + + fn setup_balances_and_pool(asset_id: u32, account: u64) { + use frame_support::{assert_ok, traits::fungibles::Mutate}; + use sp_runtime::traits::StaticLookup; + let min_balance = 1; + assert_ok!(Assets::force_create( + RuntimeOrigin::root(), + asset_id.into(), + 42, /* owner */ + true, /* is_sufficient */ + min_balance + )); + + // mint into the caller account + let caller = 2; + let beneficiary = ::Lookup::unlookup(caller); + let balance = 1000; + assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); + assert_eq!(Assets::balance(asset_id, caller), balance); + + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&account, u32::MAX.into()); + + let beneficiary = ::Lookup::unlookup(account); + let balance = 1000; + + assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); + assert_eq!(Assets::balance(asset_id, account), balance); + } } diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs index 2486474bad4..2074b1476f4 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs @@ -56,6 +56,18 @@ pub trait OnChargeAssetTransaction { tip: Self::Balance, ) -> Result; + /// Ensure payment of the transaction fees can be withdrawn. + /// + /// Note: The `fee` already includes the `tip`. + fn can_withdraw_fee( + who: &T::AccountId, + call: &T::RuntimeCall, + dispatch_info: &DispatchInfoOf, + asset_id: Self::AssetId, + fee: Self::Balance, + tip: Self::Balance, + ) -> Result<(), TransactionValidityError>; + /// After the transaction was executed the actual fee can be calculated. /// This function should refund any overpaid fees and optionally deposit /// the corrected amount. @@ -140,6 +152,32 @@ where .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment)) } + /// Ensure payment of the transaction fees can be withdrawn. + /// + /// Note: The `fee` already includes the `tip`. + fn can_withdraw_fee( + who: &T::AccountId, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + asset_id: Self::AssetId, + fee: Self::Balance, + _tip: Self::Balance, + ) -> Result<(), TransactionValidityError> { + // We don't know the precision of the underlying asset. Because the converted fee could be + // less than one (e.g. 0.5) but gets rounded down by integer division we introduce a minimum + // fee. + let min_converted_fee = if fee.is_zero() { Zero::zero() } else { One::one() }; + let converted_fee = CON::to_asset_balance(fee, asset_id) + .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))? + .max(min_converted_fee); + let can_withdraw = + >::can_withdraw(asset_id, who, converted_fee); + if can_withdraw != WithdrawConsequence::Success { + return Err(InvalidTransaction::Payment.into()) + } + Ok(()) + } + /// Hand the fee and the tip over to the `[HandleCredit]` implementation. /// Since the predicted fee might have been too high, parts of the fee may be refunded. /// diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/tests.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/tests.rs index 098ecf11dd9..cd694c3e81a 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/tests.rs @@ -17,15 +17,18 @@ use super::*; use frame_support::{ assert_ok, - dispatch::{DispatchInfo, PostDispatchInfo}, + dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, - traits::fungibles::Mutate, + traits::{fungibles::Mutate, OriginTrait}, weights::Weight, }; use frame_system as system; use mock::{ExtrinsicBaseWeight, *}; use pallet_balances::Call as BalancesCall; -use sp_runtime::{traits::StaticLookup, BuildStorage}; +use sp_runtime::{ + traits::{DispatchTransaction, StaticLookup}, + BuildStorage, +}; const CALL: &::RuntimeCall = &RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); @@ -88,7 +91,7 @@ impl ExtBuilder { /// create a transaction info struct from weight. Handy to avoid building the whole struct. pub fn info_from_weight(w: Weight) -> DispatchInfo { // pays_fee: Pays::Yes -- class: DispatchClass::Normal - DispatchInfo { weight: w, ..Default::default() } + DispatchInfo { call_weight: w, ..Default::default() } } fn post_info_from_weight(w: Weight) -> PostDispatchInfo { @@ -116,35 +119,49 @@ fn transaction_payment_in_native_possible() { .build() .execute_with(|| { let len = 10; - let pre = ChargeAssetTxPayment::::from(0, None) - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(5, 0)), len) - .unwrap(); + let mut info = info_from_weight(Weight::from_parts(5, 0)); + let ext = ChargeAssetTxPayment::::from(0, None); + info.extension_weight = ext.weight(CALL); + let (pre, _) = ext.validate_and_prepare(Some(1).into(), CALL, &info, len).unwrap(); let initial_balance = 10 * balance_factor; - assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); + assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 15 - 10); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(5, 0)), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, + &info, &default_post_info(), len, - &Ok(()) + &Ok(()), )); - assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 10); + assert_eq!(Balances::free_balance(1), initial_balance - 5 - 5 - 15 - 10); - let pre = ChargeAssetTxPayment::::from(5 /* tipped */, None) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) - .unwrap(); + let mut info = info_from_weight(Weight::from_parts(100, 0)); + let ext = ChargeAssetTxPayment::::from(5 /* tipped */, None); + info.extension_weight = ext.weight(CALL); + let (pre, _) = ext.validate_and_prepare(Some(2).into(), CALL, &info, len).unwrap(); let initial_balance_for_2 = 20 * balance_factor; - assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 100 - 5); + assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 100 - 15 - 5); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(100, 0)), - &post_info_from_weight(Weight::from_parts(50, 0)), + let call_actual_weight = Weight::from_parts(50, 0); + // The extension weight refund should be taken into account in `post_dispatch`. + let post_info = post_info_from_weight(call_actual_weight.saturating_add( + ChargeAssetTxPayment::::from(5 /* tipped */, None).weight(CALL), + )); + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, + &info, + &post_info, len, - &Ok(()) + &Ok(()), )); - assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 50 - 5); + assert_eq!( + post_info.actual_weight, + Some( + call_actual_weight + .saturating_add(MockWeights::charge_asset_tx_payment_native()) + ) + ); + assert_eq!(Balances::free_balance(2), initial_balance_for_2 - 5 - 10 - 50 - 15 - 5); }); } @@ -181,8 +198,13 @@ fn transaction_payment_in_asset_possible() { // we convert the from weight to fee based on the ratio between asset min balance and // existential deposit let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); // assert that native balance is not used assert_eq!(Balances::free_balance(caller), 10 * balance_factor); @@ -196,12 +218,12 @@ fn transaction_payment_in_asset_possible() { amount: fee, })); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance - fee); // check that the block author gets rewarded @@ -246,8 +268,13 @@ fn transaction_payment_without_fee() { // we convert the from weight to fee based on the ratio between asset min balance and // existential deposit let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); // assert that native balance is not used assert_eq!(Balances::free_balance(caller), 10 * balance_factor); @@ -255,12 +282,12 @@ fn transaction_payment_without_fee() { assert_eq!(Assets::balance(asset_id, caller), balance - fee); assert_eq!(Assets::balance(asset_id, BLOCK_AUTHOR), 0); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_weight(Weight::from_parts(weight, 0)), &post_info_from_pays(Pays::No), len, - &Ok(()) + &Ok(()), )); // caller should be refunded assert_eq!(Assets::balance(asset_id, caller), balance); @@ -298,14 +325,16 @@ fn asset_transaction_payment_with_tip_and_refund() { assert_eq!(Assets::balance(asset_id, caller), balance); let weight = 100; let tip = 5; + let ext = ChargeAssetTxPayment::::from(tip, Some(asset_id)); + let ext_weight = ext.weight(CALL); let len = 10; // we convert the from weight to fee based on the ratio between asset min balance and // existential deposit - let fee_with_tip = - (base_weight + weight + len as u64 + tip) * min_balance / ExistentialDeposit::get(); - let pre = ChargeAssetTxPayment::::from(tip, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) - .unwrap(); + let fee_with_tip = (base_weight + weight + ext_weight.ref_time() + len as u64 + tip) * + min_balance / ExistentialDeposit::get(); + let mut info = info_from_weight(Weight::from_parts(weight, 0)); + info.extension_weight = ext_weight; + let (pre, _) = ext.validate_and_prepare(Some(caller).into(), CALL, &info, len).unwrap(); assert_eq!(Assets::balance(asset_id, caller), balance - fee_with_tip); System::assert_has_event(RuntimeEvent::Assets(pallet_assets::Event::Withdrawn { @@ -315,15 +344,22 @@ fn asset_transaction_payment_with_tip_and_refund() { })); let final_weight = 50; - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(weight, 0)), - &post_info_from_weight(Weight::from_parts(final_weight, 0)), + let mut post_info = post_info_from_weight(Weight::from_parts(final_weight, 0)); + post_info + .actual_weight + .as_mut() + .map(|w| w.saturating_accrue(MockWeights::charge_asset_tx_payment_asset())); + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, + &info, + &post_info, len, - &Ok(()) + &Ok(()), )); - let final_fee = - fee_with_tip - (weight - final_weight) * min_balance / ExistentialDeposit::get(); + let final_fee = fee_with_tip - + (weight - final_weight + ext_weight.ref_time() - + MockWeights::charge_asset_tx_payment_asset().ref_time()) * + min_balance / ExistentialDeposit::get(); assert_eq!(Assets::balance(asset_id, caller), balance - (final_fee)); assert_eq!(Assets::balance(asset_id, BLOCK_AUTHOR), final_fee); @@ -367,19 +403,24 @@ fn payment_from_account_with_only_assets() { // we convert the from weight to fee based on the ratio between asset min balance and // existential deposit let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); assert_eq!(Balances::free_balance(caller), 0); // check that fee was charged in the given asset assert_eq!(Assets::balance(asset_id, caller), balance - fee); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance - fee); assert_eq!(Balances::free_balance(caller), 0); @@ -400,7 +441,12 @@ fn payment_only_with_existing_sufficient_asset() { let len = 10; // pre_dispatch fails for non-existent asset assert!(ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len + ) .is_err()); // create the non-sufficient asset @@ -414,7 +460,12 @@ fn payment_only_with_existing_sufficient_asset() { )); // pre_dispatch fails for non-sufficient asset assert!(ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len + ) .is_err()); }); } @@ -452,33 +503,38 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { // naive fee calculation would round down to zero assert_eq!(fee, 0); { - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` still implies no fees assert_eq!(Assets::balance(asset_id, caller), balance); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_pays(Pays::No), &post_info_from_pays(Pays::No), len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance); } - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_weight(Weight::from_parts(weight, 0)), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare( + Some(caller).into(), + CALL, + &info_from_weight(Weight::from_parts(weight, 0)), + len, + ) .unwrap(); // check that at least one coin was charged in the given asset assert_eq!(Assets::balance(asset_id, caller), balance - 1); - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_weight(Weight::from_parts(weight, 0)), &default_post_info(), len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance - 1); }); @@ -516,12 +572,14 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { let fee = (base_weight + weight + len as u64) * min_balance / ExistentialDeposit::get(); // calculated fee is greater than 0 assert!(fee > 0); - let pre = ChargeAssetTxPayment::::from(0, Some(asset_id)) - .pre_dispatch(&caller, CALL, &info_from_pays(Pays::No), len) + let (pre, _) = ChargeAssetTxPayment::::from(0, Some(asset_id)) + .validate_and_prepare(Some(caller).into(), CALL, &info_from_pays(Pays::No), len) .unwrap(); // `Pays::No` implies no pre-dispatch fees assert_eq!(Assets::balance(asset_id, caller), balance); - let (_tip, _who, initial_payment, _asset_id) = ⪯ + let Pre::Charge { initial_payment, .. } = &pre else { + panic!("Expected Charge"); + }; let not_paying = match initial_payment { &InitialPayment::Nothing => true, _ => false, @@ -530,62 +588,50 @@ fn post_dispatch_fee_is_zero_if_pre_dispatch_fee_is_zero() { // `Pays::Yes` on post-dispatch does not mean we pay (we never charge more than the // initial fee) - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - Some(pre), + assert_ok!(ChargeAssetTxPayment::::post_dispatch_details( + pre, &info_from_pays(Pays::No), &post_info_from_pays(Pays::Yes), len, - &Ok(()) + &Ok(()), )); assert_eq!(Assets::balance(asset_id, caller), balance); }); } #[test] -fn post_dispatch_fee_is_zero_if_unsigned_pre_dispatch_fee_is_zero() { - let base_weight = 1; - ExtBuilder::default() - .balance_factor(100) - .base_weight(Weight::from_parts(base_weight, 0)) - .build() - .execute_with(|| { - // create the asset - let asset_id = 1; - let min_balance = 100; - assert_ok!(Assets::force_create( - RuntimeOrigin::root(), - asset_id.into(), - 42, /* owner */ - true, /* is_sufficient */ - min_balance - )); +fn no_fee_and_no_weight_for_other_origins() { + ExtBuilder::default().build().execute_with(|| { + let ext = ChargeAssetTxPayment::::from(0, None); - // mint into the caller account - let caller = 333; - let beneficiary = ::Lookup::unlookup(caller); - let balance = 100; - assert_ok!(Assets::mint_into(asset_id.into(), &beneficiary, balance)); - assert_eq!(Assets::balance(asset_id, caller), balance); - let weight = 1; - let len = 1; - ChargeAssetTxPayment::::pre_dispatch_unsigned( - CALL, - &info_from_weight(Weight::from_parts(weight, 0)), - len, - ) - .unwrap(); + let mut info = CALL.get_dispatch_info(); + info.extension_weight = ext.weight(CALL); - assert_eq!(Assets::balance(asset_id, caller), balance); + // Ensure we test the refund. + assert!(info.extension_weight != Weight::zero()); - // `Pays::Yes` on post-dispatch does not mean we pay (we never charge more than the - // initial fee) - assert_ok!(ChargeAssetTxPayment::::post_dispatch( - None, - &info_from_weight(Weight::from_parts(weight, 0)), - &post_info_from_pays(Pays::Yes), - len, - &Ok(()) - )); - assert_eq!(Assets::balance(asset_id, caller), balance); - }); + let len = CALL.encoded_size(); + + let origin = frame_system::RawOrigin::Root.into(); + let (pre, origin) = ext.validate_and_prepare(origin, CALL, &info, len).unwrap(); + + assert!(origin.as_system_ref().unwrap().is_root()); + + let pd_res = Ok(()); + let mut post_info = frame_support::dispatch::PostDispatchInfo { + actual_weight: Some(info.total_weight()), + pays_fee: Default::default(), + }; + + as TransactionExtension>::post_dispatch( + pre, + &info, + &mut post_info, + len, + &pd_res, + ) + .unwrap(); + + assert_eq!(post_info.actual_weight, Some(info.call_weight)); + }) } diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/weights.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/weights.rs new file mode 100644 index 00000000000..1af1c94177d --- /dev/null +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/weights.rs @@ -0,0 +1,146 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_asset_tx_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, 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` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// ./target/production/substrate-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_asset_tx_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/transaction-payment/asset-tx-payment/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_asset_tx_payment`. +pub trait WeightInfo { + fn charge_asset_tx_payment_zero() -> Weight; + fn charge_asset_tx_payment_native() -> Weight; + fn charge_asset_tx_payment_asset() -> Weight; +} + +/// Weights for `pallet_asset_tx_payment` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 542_000 picoseconds. + Weight::from_parts(597_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 33_162_000 picoseconds. + Weight::from_parts(34_716_000, 1733) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `747` + // Estimated: `3675` + // Minimum execution time: 44_230_000 picoseconds. + Weight::from_parts(45_297_000, 3675) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + fn charge_asset_tx_payment_zero() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 542_000 picoseconds. + Weight::from_parts(597_000, 0) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_native() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 33_162_000 picoseconds. + Weight::from_parts(34_716_000, 1733) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + } + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:1) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_asset_tx_payment_asset() -> Weight { + // Proof Size summary in bytes: + // Measured: `747` + // Estimated: `3675` + // Minimum execution time: 44_230_000 picoseconds. + Weight::from_parts(45_297_000, 3675) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } +} diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs index 3ab38743baf..d6ac648cefd 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/lib.rs @@ -21,7 +21,7 @@ //! //! ## Overview //! -//! It does this by wrapping an existing [`SignedExtension`] implementation (e.g. +//! It does this by wrapping an existing [`TransactionExtension`] implementation (e.g. //! [`pallet-transaction-payment`]) and checking if the dispatchable is feeless before applying the //! wrapped extension. If the dispatchable is indeed feeless, the extension is skipped and a custom //! event is emitted instead. Otherwise, the extension is applied as usual. @@ -31,7 +31,7 @@ //! //! This pallet wraps an existing transaction payment pallet. This means you should both pallets //! in your [`construct_runtime`](frame_support::construct_runtime) macro and -//! include this pallet's [`SignedExtension`] ([`SkipCheckIfFeeless`]) that would accept the +//! include this pallet's [`TransactionExtension`] ([`SkipCheckIfFeeless`]) that would accept the //! existing one as an argument. #![cfg_attr(not(feature = "std"), no_std)] @@ -40,11 +40,14 @@ use codec::{Decode, Encode}; use frame_support::{ dispatch::{CheckIfFeeless, DispatchResult}, traits::{IsType, OriginTrait}, + weights::Weight, }; use scale_info::{StaticTypeInfo, TypeInfo}; use sp_runtime::{ - traits::{DispatchInfoOf, PostDispatchInfoOf, SignedExtension}, - transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction}, + traits::{ + DispatchInfoOf, DispatchOriginOf, PostDispatchInfoOf, TransactionExtension, ValidateResult, + }, + transaction_validity::TransactionValidityError, }; #[cfg(test)] @@ -71,11 +74,11 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// A transaction fee was skipped. - FeeSkipped { who: T::AccountId }, + FeeSkipped { origin: ::PalletsOrigin }, } } -/// A [`SignedExtension`] that skips the wrapped extension if the dispatchable is feeless. +/// A [`TransactionExtension`] that skips the wrapped extension if the dispatchable is feeless. #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct SkipCheckIfFeeless(pub S, core::marker::PhantomData); @@ -104,67 +107,83 @@ impl From for SkipCheckIfFeeless { } } -impl> SignedExtension - for SkipCheckIfFeeless +pub enum Intermediate { + /// The wrapped extension should be applied. + Apply(T), + /// The wrapped extension should be skipped. + Skip(O), +} +use Intermediate::*; + +impl> + TransactionExtension for SkipCheckIfFeeless where - S::Call: CheckIfFeeless>, + T::RuntimeCall: CheckIfFeeless>, { - type AccountId = T::AccountId; - type Call = S::Call; - type AdditionalSigned = S::AdditionalSigned; - type Pre = (Self::AccountId, Option<::Pre>); // From the outside this extension should be "invisible", because it just extends the wrapped // extension with an extra check in `pre_dispatch` and `post_dispatch`. Thus, we should forward // the identifier of the wrapped extension to let wallets see this extension as it would only be // the wrapped extension itself. const IDENTIFIER: &'static str = S::IDENTIFIER; + type Implicit = S::Implicit; + + fn implicit(&self) -> Result { + self.0.implicit() + } + type Val = + Intermediate as OriginTrait>::PalletsOrigin>; + type Pre = + Intermediate as OriginTrait>::PalletsOrigin>; - fn additional_signed(&self) -> Result { - self.0.additional_signed() + fn weight(&self, call: &T::RuntimeCall) -> frame_support::weights::Weight { + self.0.weight(call) } fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, + origin: DispatchOriginOf, + call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> TransactionValidity { - if call.is_feeless(&::RuntimeOrigin::signed(who.clone())) { - Ok(ValidTransaction::default()) + self_implicit: S::Implicit, + inherited_implication: &impl Encode, + ) -> ValidateResult { + if call.is_feeless(&origin) { + Ok((Default::default(), Skip(origin.caller().clone()), origin)) } else { - self.0.validate(who, call, info, len) + let (x, y, z) = + self.0.validate(origin, call, info, len, self_implicit, inherited_implication)?; + Ok((x, Apply(y), z)) } } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, + val: Self::Val, + origin: &DispatchOriginOf, + call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, ) -> Result { - if call.is_feeless(&::RuntimeOrigin::signed(who.clone())) { - Ok((who.clone(), None)) - } else { - Ok((who.clone(), Some(self.0.pre_dispatch(who, call, info, len)?))) + match val { + Apply(val) => self.0.prepare(val, origin, call, info, len).map(Apply), + Skip(origin) => Ok(Skip(origin)), } } - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, len: usize, result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - if let Some(pre) = pre { - if let Some(pre) = pre.1 { - S::post_dispatch(Some(pre), info, post_info, len, result)?; - } else { - Pallet::::deposit_event(Event::::FeeSkipped { who: pre.0 }); - } + ) -> Result { + match pre { + Apply(pre) => S::post_dispatch_details(pre, info, post_info, len, result), + Skip(origin) => { + Pallet::::deposit_event(Event::::FeeSkipped { origin }); + Ok(Weight::zero()) + }, } - Ok(()) } } diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs index d6d600f24e7..83f7b7dfe2b 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/mock.rs @@ -18,9 +18,12 @@ use crate as pallet_skip_feeless_payment; use frame_support::{derive_impl, parameter_types}; use frame_system as system; +use sp_runtime::{ + traits::{DispatchOriginOf, TransactionExtension}, + transaction_validity::ValidTransaction, +}; type Block = frame_system::mocking::MockBlock; -type AccountId = u64; #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { @@ -32,42 +35,45 @@ impl Config for Runtime { } parameter_types! { - pub static PreDispatchCount: u32 = 0; + pub static PrepareCount: u32 = 0; pub static ValidateCount: u32 = 0; } #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo)] pub struct DummyExtension; -impl SignedExtension for DummyExtension { - type AccountId = AccountId; - type Call = RuntimeCall; - type AdditionalSigned = (); - type Pre = (); +impl TransactionExtension for DummyExtension { const IDENTIFIER: &'static str = "DummyExtension"; - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) + type Implicit = (); + type Val = (); + type Pre = (); + + fn weight(&self, _: &RuntimeCall) -> Weight { + Weight::zero() } fn validate( &self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, + origin: DispatchOriginOf, + _call: &RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { ValidateCount::mutate(|c| *c += 1); - Ok(Default::default()) + Ok((ValidTransaction::default(), (), origin)) } - fn pre_dispatch( + fn prepare( self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, + _val: Self::Val, + _origin: &DispatchOriginOf, + _call: &RuntimeCall, + _info: &DispatchInfoOf, _len: usize, ) -> Result { - PreDispatchCount::mutate(|c| *c += 1); + PrepareCount::mutate(|c| *c += 1); Ok(()) } } diff --git a/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs b/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs index adee52d6b3c..666844c883b 100644 --- a/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/skip-feeless-payment/src/tests.rs @@ -15,23 +15,24 @@ use super::*; use crate::mock::{ - pallet_dummy::Call, DummyExtension, PreDispatchCount, Runtime, RuntimeCall, ValidateCount, + pallet_dummy::Call, DummyExtension, PrepareCount, Runtime, RuntimeCall, ValidateCount, }; use frame_support::dispatch::DispatchInfo; +use sp_runtime::traits::DispatchTransaction; #[test] fn skip_feeless_payment_works() { let call = RuntimeCall::DummyPallet(Call::::aux { data: 1 }); SkipCheckIfFeeless::::from(DummyExtension) - .pre_dispatch(&0, &call, &DispatchInfo::default(), 0) + .validate_and_prepare(Some(0).into(), &call, &DispatchInfo::default(), 0) .unwrap(); - assert_eq!(PreDispatchCount::get(), 1); + assert_eq!(PrepareCount::get(), 1); let call = RuntimeCall::DummyPallet(Call::::aux { data: 0 }); SkipCheckIfFeeless::::from(DummyExtension) - .pre_dispatch(&0, &call, &DispatchInfo::default(), 0) + .validate_and_prepare(Some(0).into(), &call, &DispatchInfo::default(), 0) .unwrap(); - assert_eq!(PreDispatchCount::get(), 1); + assert_eq!(PrepareCount::get(), 1); } #[test] @@ -40,13 +41,42 @@ fn validate_works() { let call = RuntimeCall::DummyPallet(Call::::aux { data: 1 }); SkipCheckIfFeeless::::from(DummyExtension) - .validate(&0, &call, &DispatchInfo::default(), 0) + .validate_only(Some(0).into(), &call, &DispatchInfo::default(), 0) .unwrap(); assert_eq!(ValidateCount::get(), 1); + assert_eq!(PrepareCount::get(), 0); let call = RuntimeCall::DummyPallet(Call::::aux { data: 0 }); SkipCheckIfFeeless::::from(DummyExtension) - .validate(&0, &call, &DispatchInfo::default(), 0) + .validate_only(Some(0).into(), &call, &DispatchInfo::default(), 0) .unwrap(); assert_eq!(ValidateCount::get(), 1); + assert_eq!(PrepareCount::get(), 0); +} + +#[test] +fn validate_prepare_works() { + assert_eq!(ValidateCount::get(), 0); + + let call = RuntimeCall::DummyPallet(Call::::aux { data: 1 }); + SkipCheckIfFeeless::::from(DummyExtension) + .validate_and_prepare(Some(0).into(), &call, &DispatchInfo::default(), 0) + .unwrap(); + assert_eq!(ValidateCount::get(), 1); + assert_eq!(PrepareCount::get(), 1); + + let call = RuntimeCall::DummyPallet(Call::::aux { data: 0 }); + SkipCheckIfFeeless::::from(DummyExtension) + .validate_and_prepare(Some(0).into(), &call, &DispatchInfo::default(), 0) + .unwrap(); + assert_eq!(ValidateCount::get(), 1); + assert_eq!(PrepareCount::get(), 1); + + // Changes from previous prepare calls persist. + let call = RuntimeCall::DummyPallet(Call::::aux { data: 1 }); + SkipCheckIfFeeless::::from(DummyExtension) + .validate_and_prepare(Some(0).into(), &call, &DispatchInfo::default(), 0) + .unwrap(); + assert_eq!(ValidateCount::get(), 2); + assert_eq!(PrepareCount::get(), 2); } diff --git a/substrate/frame/transaction-payment/src/benchmarking.rs b/substrate/frame/transaction-payment/src/benchmarking.rs new file mode 100644 index 00000000000..c5f87fb8c12 --- /dev/null +++ b/substrate/frame/transaction-payment/src/benchmarking.rs @@ -0,0 +1,86 @@ +// 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. + +//! Benchmarks for Transaction Payment Pallet's transaction extension + +extern crate alloc; + +use super::*; +use crate::Pallet; +use frame_benchmarking::v2::*; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::{EventRecord, RawOrigin}; +use sp_runtime::traits::{AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable}; + +fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + // compare to the last event record + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + +#[benchmarks(where + T: Config, + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, + T::RuntimeCall: Dispatchable, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn charge_transaction_payment() { + let caller: T::AccountId = account("caller", 0, 0); + >::endow_account( + &caller, + >::minimum_balance() * 1000u32.into(), + ); + let tip = >::minimum_balance(); + let ext: ChargeTransactionPayment = ChargeTransactionPayment::from(tip); + let inner = frame_system::Call::remark { remark: alloc::vec![] }; + let call = T::RuntimeCall::from(inner); + let extension_weight = ext.weight(&call); + let info = DispatchInfo { + call_weight: Weight::from_parts(100, 0), + extension_weight, + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let mut post_info = PostDispatchInfo { + actual_weight: Some(Weight::from_parts(10, 0)), + pays_fee: Pays::Yes, + }; + + #[block] + { + assert!(ext + .test_run(RawOrigin::Signed(caller.clone()).into(), &call, &info, 10, |_| Ok( + post_info + )) + .unwrap() + .is_ok()); + } + + post_info.actual_weight.as_mut().map(|w| w.saturating_accrue(extension_weight)); + let actual_fee = Pallet::::compute_actual_fee(10, &info, &post_info, tip); + assert_last_event::( + Event::::TransactionFeePaid { who: caller, actual_fee, tip }.into(), + ); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime); +} diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs index c17ab393b5d..711189be8d0 100644 --- a/substrate/frame/transaction-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/src/lib.rs @@ -56,28 +56,32 @@ use frame_support::{ }, traits::{Defensive, EstimateCallFee, Get}, weights::{Weight, WeightToFee}, + RuntimeDebugNoBound, }; pub use pallet::*; pub use payment::*; use sp_runtime::{ traits::{ Convert, DispatchInfoOf, Dispatchable, One, PostDispatchInfoOf, SaturatedConversion, - Saturating, SignedExtension, Zero, - }, - transaction_validity::{ - TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransaction, + Saturating, TransactionExtension, Zero, }, + transaction_validity::{TransactionPriority, TransactionValidityError, ValidTransaction}, FixedPointNumber, FixedU128, Perbill, Perquintill, RuntimeDebug, }; pub use types::{FeeDetails, InclusionFee, RuntimeDispatchInfo}; +pub use weights::WeightInfo; #[cfg(test)] mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + mod payment; mod types; +pub mod weights; /// Fee multiplier. pub type Multiplier = FixedU128; @@ -334,6 +338,7 @@ pub mod pallet { type RuntimeEvent = (); type FeeMultiplierUpdate = (); type OperationalFeeMultiplier = (); + type WeightInfo = (); } } @@ -386,6 +391,9 @@ pub mod pallet { /// transactions. #[pallet::constant] type OperationalFeeMultiplier: Get; + + /// The weight information of this pallet. + type WeightInfo: WeightInfo; } #[pallet::type_value] @@ -496,7 +504,7 @@ impl Pallet { /// /// All dispatchables must be annotated with weight and will have some fee info. This function /// always returns. - pub fn query_info( + pub fn query_info( unchecked_extrinsic: Extrinsic, len: u32, ) -> RuntimeDispatchInfo> @@ -510,20 +518,20 @@ impl Pallet { // a very very little potential gain in the future. let dispatch_info = ::get_dispatch_info(&unchecked_extrinsic); - let partial_fee = if unchecked_extrinsic.is_signed().unwrap_or(false) { - Self::compute_fee(len, &dispatch_info, 0u32.into()) - } else { - // Unsigned extrinsics have no partial fee. + let partial_fee = if unchecked_extrinsic.is_bare() { + // Bare extrinsics have no partial fee. 0u32.into() + } else { + Self::compute_fee(len, &dispatch_info, 0u32.into()) }; - let DispatchInfo { weight, class, .. } = dispatch_info; + let DispatchInfo { class, .. } = dispatch_info; - RuntimeDispatchInfo { weight, class, partial_fee } + RuntimeDispatchInfo { weight: dispatch_info.total_weight(), class, partial_fee } } /// Query the detailed fee of a given `call`. - pub fn query_fee_details( + pub fn query_fee_details( unchecked_extrinsic: Extrinsic, len: u32, ) -> FeeDetails> @@ -534,11 +542,11 @@ impl Pallet { let tip = 0u32.into(); - if unchecked_extrinsic.is_signed().unwrap_or(false) { - Self::compute_fee_details(len, &dispatch_info, tip) - } else { - // Unsigned extrinsics have no inclusion fee. + if unchecked_extrinsic.is_bare() { + // Bare extrinsics have no inclusion fee. FeeDetails { inclusion_fee: None, tip } + } else { + Self::compute_fee_details(len, &dispatch_info, tip) } } @@ -548,10 +556,10 @@ impl Pallet { T::RuntimeCall: Dispatchable + GetDispatchInfo, { let dispatch_info = ::get_dispatch_info(&call); - let DispatchInfo { weight, class, .. } = dispatch_info; + let DispatchInfo { class, .. } = dispatch_info; RuntimeDispatchInfo { - weight, + weight: dispatch_info.total_weight(), class, partial_fee: Self::compute_fee(len, &dispatch_info, 0u32.into()), } @@ -589,7 +597,7 @@ impl Pallet { where T::RuntimeCall: Dispatchable, { - Self::compute_fee_raw(len, info.weight, tip, info.pays_fee, info.class) + Self::compute_fee_raw(len, info.total_weight(), tip, info.pays_fee, info.class) } /// Compute the actual post dispatch fee for a particular transaction. @@ -722,7 +730,7 @@ where who: &T::AccountId, call: &T::RuntimeCall, info: &DispatchInfoOf, - len: usize, + fee: BalanceOf, ) -> Result< ( BalanceOf, @@ -731,7 +739,6 @@ where TransactionValidityError, > { let tip = self.0; - let fee = Pallet::::compute_fee(len as u32, info, tip); <::OnChargeTransaction as OnChargeTransaction>::withdraw_fee( who, call, info, fee, tip, @@ -739,6 +746,22 @@ where .map(|i| (fee, i)) } + fn can_withdraw_fee( + &self, + who: &T::AccountId, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + len: usize, + ) -> Result, TransactionValidityError> { + let tip = self.0; + let fee = Pallet::::compute_fee(len as u32, info, tip); + + <::OnChargeTransaction as OnChargeTransaction>::can_withdraw_fee( + who, call, info, fee, tip, + )?; + Ok(fee) + } + /// Get an appropriate priority for a transaction with the given `DispatchInfo`, encoded length /// and user-included tip. /// @@ -764,7 +787,8 @@ where let max_block_length = *T::BlockLength::get().max.get(info.class) as u64; // bounded_weight is used as a divisor later so we keep it non-zero. - let bounded_weight = info.weight.max(Weight::from_parts(1, 1)).min(max_block_weight); + let bounded_weight = + info.total_weight().max(Weight::from_parts(1, 1)).min(max_block_weight); let bounded_length = (len as u64).clamp(1, max_block_length); // returns the scarce resource, i.e. the one that is limiting the number of transactions. @@ -825,68 +849,130 @@ impl core::fmt::Debug for ChargeTransactionPayment { } } -impl SignedExtension for ChargeTransactionPayment +/// The info passed between the validate and prepare steps for the `ChargeAssetTxPayment` extension. +#[derive(RuntimeDebugNoBound)] +pub enum Val { + Charge { + tip: BalanceOf, + // who paid the fee + who: T::AccountId, + // transaction fee + fee: BalanceOf, + }, + NoCharge, +} + +/// The info passed between the prepare and post-dispatch steps for the `ChargeAssetTxPayment` +/// extension. +pub enum Pre { + Charge { + tip: BalanceOf, + // who paid the fee + who: T::AccountId, + // imbalance resulting from withdrawing the fee + imbalance: <::OnChargeTransaction as OnChargeTransaction>::LiquidityInfo, + }, + NoCharge { + // weight initially estimated by the extension, to be refunded + refund: Weight, + }, +} + +impl core::fmt::Debug for Pre { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + Pre::Charge { tip, who, imbalance: _ } => { + write!(f, "Charge {{ tip: {:?}, who: {:?}, imbalance: }}", tip, who) + }, + Pre::NoCharge { refund } => write!(f, "NoCharge {{ refund: {:?} }}", refund), + } + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.write_str("") + } +} + +impl TransactionExtension for ChargeTransactionPayment where - BalanceOf: Send + Sync + From, T::RuntimeCall: Dispatchable, { const IDENTIFIER: &'static str = "ChargeTransactionPayment"; - type AccountId = T::AccountId; - type Call = T::RuntimeCall; - type AdditionalSigned = (); - type Pre = ( - // tip - BalanceOf, - // who paid the fee - this is an option to allow for a Default impl. - Self::AccountId, - // imbalance resulting from withdrawing the fee - <::OnChargeTransaction as OnChargeTransaction>::LiquidityInfo, - ); - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) + type Implicit = (); + type Val = Val; + type Pre = Pre; + + fn weight(&self, _: &T::RuntimeCall) -> Weight { + T::WeightInfo::charge_transaction_payment() } fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, len: usize, - ) -> TransactionValidity { - let (final_fee, _) = self.withdraw_fee(who, call, info, len)?; + _: (), + _implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + let Ok(who) = frame_system::ensure_signed(origin.clone()) else { + return Ok((ValidTransaction::default(), Val::NoCharge, origin)); + }; + let final_fee = self.can_withdraw_fee(&who, call, info, len)?; let tip = self.0; - Ok(ValidTransaction { - priority: Self::get_priority(info, len, tip, final_fee), - ..Default::default() - }) + Ok(( + ValidTransaction { + priority: Self::get_priority(info, len, tip, final_fee), + ..Default::default() + }, + Val::Charge { tip: self.0, who, fee: final_fee }, + origin, + )) } - fn pre_dispatch( + fn prepare( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + val: Self::Val, + _origin: &::RuntimeOrigin, + call: &T::RuntimeCall, + info: &DispatchInfoOf, + _len: usize, ) -> Result { - let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?; - Ok((self.0, who.clone(), imbalance)) + match val { + Val::Charge { tip, who, fee } => { + // Mutating call to `withdraw_fee` to actually charge for the transaction. + let (_final_fee, imbalance) = self.withdraw_fee(&who, call, info, fee)?; + Ok(Pre::Charge { tip, who, imbalance }) + }, + Val::NoCharge => Ok(Pre::NoCharge { refund: self.weight(call) }), + } } - fn post_dispatch( - maybe_pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, len: usize, _result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - if let Some((tip, who, imbalance)) = maybe_pre { - let actual_fee = Pallet::::compute_actual_fee(len as u32, info, post_info, tip); - T::OnChargeTransaction::correct_and_deposit_fee( - &who, info, post_info, actual_fee, tip, imbalance, - )?; - Pallet::::deposit_event(Event::::TransactionFeePaid { who, actual_fee, tip }); - } - Ok(()) + ) -> Result { + let (tip, who, imbalance) = match pre { + Pre::Charge { tip, who, imbalance } => (tip, who, imbalance), + Pre::NoCharge { refund } => { + // No-op: Refund everything + return Ok(refund) + }, + }; + let actual_fee = Pallet::::compute_actual_fee(len as u32, info, &post_info, tip); + T::OnChargeTransaction::correct_and_deposit_fee( + &who, info, &post_info, actual_fee, tip, imbalance, + )?; + Pallet::::deposit_event(Event::::TransactionFeePaid { who, actual_fee, tip }); + Ok(Weight::zero()) } } diff --git a/substrate/frame/transaction-payment/src/mock.rs b/substrate/frame/transaction-payment/src/mock.rs index 8767024ee23..3995c41e8b1 100644 --- a/substrate/frame/transaction-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/src/mock.rs @@ -119,6 +119,15 @@ impl OnUnbalanced::AccountId, } } +/// Weights used in testing. +pub struct MockWeights; + +impl WeightInfo for MockWeights { + fn charge_transaction_payment() -> Weight { + Weight::from_parts(10, 0) + } +} + impl Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = FungibleAdapter; @@ -126,4 +135,14 @@ impl Config for Runtime { type WeightToFee = WeightToFee; type LengthToFee = TransactionByteFee; type FeeMultiplierUpdate = (); + type WeightInfo = MockWeights; +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_test_ext() -> sp_io::TestExternalities { + crate::tests::ExtBuilder::default() + .base_weight(Weight::from_parts(100, 0)) + .byte_fee(10) + .balance_factor(0) + .build() } diff --git a/substrate/frame/transaction-payment/src/payment.rs b/substrate/frame/transaction-payment/src/payment.rs index 0fe61678290..4b39cd3fe53 100644 --- a/substrate/frame/transaction-payment/src/payment.rs +++ b/substrate/frame/transaction-payment/src/payment.rs @@ -20,14 +20,14 @@ use crate::Config; use core::marker::PhantomData; use sp_runtime::{ - traits::{DispatchInfoOf, PostDispatchInfoOf, Saturating, Zero}, + traits::{CheckedSub, DispatchInfoOf, PostDispatchInfoOf, Saturating, Zero}, transaction_validity::InvalidTransaction, }; use frame_support::{ traits::{ fungible::{Balanced, Credit, Debt, Inspect}, - tokens::Precision, + tokens::{Precision, WithdrawConsequence}, Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReasons, }, unsigned::TransactionValidityError, @@ -55,6 +55,17 @@ pub trait OnChargeTransaction { tip: Self::Balance, ) -> Result; + /// Check if the predicted fee from the transaction origin can be withdrawn. + /// + /// Note: The `fee` already includes the `tip`. + fn can_withdraw_fee( + who: &T::AccountId, + call: &T::RuntimeCall, + dispatch_info: &DispatchInfoOf, + fee: Self::Balance, + tip: Self::Balance, + ) -> Result<(), TransactionValidityError>; + /// After the transaction was executed the actual fee can be calculated. /// This function should refund any overpaid fees and optionally deposit /// the corrected amount. @@ -68,6 +79,12 @@ pub trait OnChargeTransaction { tip: Self::Balance, already_withdrawn: Self::LiquidityInfo, ) -> Result<(), TransactionValidityError>; + + #[cfg(feature = "runtime-benchmarks")] + fn endow_account(who: &T::AccountId, amount: Self::Balance); + + #[cfg(feature = "runtime-benchmarks")] + fn minimum_balance() -> Self::Balance; } /// Implements transaction payment for a pallet implementing the [`frame_support::traits::fungible`] @@ -110,6 +127,23 @@ where } } + fn can_withdraw_fee( + who: &T::AccountId, + _call: &T::RuntimeCall, + _dispatch_info: &DispatchInfoOf, + fee: Self::Balance, + _tip: Self::Balance, + ) -> Result<(), TransactionValidityError> { + if fee.is_zero() { + return Ok(()) + } + + match F::can_withdraw(who, fee) { + WithdrawConsequence::Success => Ok(()), + _ => Err(InvalidTransaction::Payment.into()), + } + } + fn correct_and_deposit_fee( who: &::AccountId, _dispatch_info: &DispatchInfoOf<::RuntimeCall>, @@ -141,6 +175,16 @@ where Ok(()) } + + #[cfg(feature = "runtime-benchmarks")] + fn endow_account(who: &T::AccountId, amount: Self::Balance) { + let _ = F::deposit(who, amount, Precision::BestEffort); + } + + #[cfg(feature = "runtime-benchmarks")] + fn minimum_balance() -> Self::Balance { + F::minimum_balance() + } } /// Implements the transaction payment for a pallet implementing the [`Currency`] @@ -202,6 +246,33 @@ where } } + /// Check if the predicted fee from the transaction origin can be withdrawn. + /// + /// Note: The `fee` already includes the `tip`. + fn can_withdraw_fee( + who: &T::AccountId, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + fee: Self::Balance, + tip: Self::Balance, + ) -> Result<(), TransactionValidityError> { + if fee.is_zero() { + return Ok(()) + } + + let withdraw_reason = if tip.is_zero() { + WithdrawReasons::TRANSACTION_PAYMENT + } else { + WithdrawReasons::TRANSACTION_PAYMENT | WithdrawReasons::TIP + }; + + let new_balance = + C::free_balance(who).checked_sub(&fee).ok_or(InvalidTransaction::Payment)?; + C::ensure_can_withdraw(who, fee, withdraw_reason, new_balance) + .map(|_| ()) + .map_err(|_| InvalidTransaction::Payment.into()) + } + /// Hand the fee and the tip over to the `[OnUnbalanced]` implementation. /// Since the predicted fee might have been too high, parts of the fee may /// be refunded. @@ -234,4 +305,14 @@ where } Ok(()) } + + #[cfg(feature = "runtime-benchmarks")] + fn endow_account(who: &T::AccountId, amount: Self::Balance) { + let _ = C::deposit_creating(who, amount); + } + + #[cfg(feature = "runtime-benchmarks")] + fn minimum_balance() -> Self::Balance { + C::minimum_balance() + } } diff --git a/substrate/frame/transaction-payment/src/tests.rs b/substrate/frame/transaction-payment/src/tests.rs index bac89967d6a..e8f5ab99529 100644 --- a/substrate/frame/transaction-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/src/tests.rs @@ -21,13 +21,16 @@ use crate as pallet_transaction_payment; use codec::Encode; use sp_runtime::{ - testing::TestXt, traits::One, transaction_validity::InvalidTransaction, BuildStorage, + generic::UncheckedExtrinsic, + traits::{DispatchTransaction, One}, + transaction_validity::InvalidTransaction, + BuildStorage, }; use frame_support::{ - assert_noop, assert_ok, + assert_ok, dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, - traits::Currency, + traits::{Currency, OriginTrait}, weights::Weight, }; use frame_system as system; @@ -113,7 +116,7 @@ impl ExtBuilder { /// create a transaction info struct from weight. Handy to avoid building the whole struct. pub fn info_from_weight(w: Weight) -> DispatchInfo { // pays_fee: Pays::Yes -- class: DispatchClass::Normal - DispatchInfo { weight: w, ..Default::default() } + DispatchInfo { call_weight: w, ..Default::default() } } fn post_info_from_weight(w: Weight) -> PostDispatchInfo { @@ -128,88 +131,82 @@ fn default_post_info() -> PostDispatchInfo { PostDispatchInfo { actual_weight: None, pays_fee: Default::default() } } +type Ext = ChargeTransactionPayment; + #[test] -fn signed_extension_transaction_payment_work() { +fn transaction_extension_transaction_payment_work() { ExtBuilder::default() .balance_factor(10) .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(5, 0)), len) - .unwrap(); - assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(5, 0)), - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); - assert_eq!(FeeUnbalancedAmount::get(), 5 + 5 + 10); + let mut info = info_from_weight(Weight::from_parts(5, 0)); + let ext = Ext::from(0); + let ext_weight = ext.weight(CALL); + info.extension_weight = ext_weight; + ext.test_run(Some(1).into(), CALL, &info, 10, |_| { + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10 - 10); + Ok(default_post_info()) + }) + .unwrap() + .unwrap(); + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10 - 10); + assert_eq!(FeeUnbalancedAmount::get(), 5 + 5 + 10 + 10); assert_eq!(TipUnbalancedAmount::get(), 0); FeeUnbalancedAmount::mutate(|a| *a = 0); - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) + let mut info = info_from_weight(Weight::from_parts(100, 0)); + info.extension_weight = ext_weight; + Ext::from(5 /* tipped */) + .test_run(Some(2).into(), CALL, &info, 10, |_| { + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 10 - 5); + Ok(post_info_from_weight(Weight::from_parts(50, 0))) + }) + .unwrap() .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(100, 0)), - &post_info_from_weight(Weight::from_parts(50, 0)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 5); - assert_eq!(FeeUnbalancedAmount::get(), 5 + 10 + 50); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 10 - 5); + assert_eq!(FeeUnbalancedAmount::get(), 5 + 10 + 50 + 10); assert_eq!(TipUnbalancedAmount::get(), 5); }); } #[test] -fn signed_extension_transaction_payment_multiplied_refund_works() { +fn transaction_extension_transaction_payment_multiplied_refund_works() { ExtBuilder::default() .balance_factor(10) .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { - let len = 10; NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) - .unwrap(); - // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(100, 0)), - &post_info_from_weight(Weight::from_parts(50, 0)), - len, - &Ok(()) - )); - // 75 (3/2 of the returned 50 units of weight) is refunded - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5); + let len = 10; + let origin = Some(2).into(); + let mut info = info_from_weight(Weight::from_parts(100, 0)); + let ext = Ext::from(5 /* tipped */); + let ext_weight = ext.weight(CALL); + info.extension_weight = ext_weight; + ext.test_run(origin, CALL, &info, len, |_| { + // 5 base fee, 10 byte fee, 3/2 * (100 call weight fee + 10 ext weight fee), 5 + // tip + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 165 - 5); + Ok(post_info_from_weight(Weight::from_parts(50, 0))) + }) + .unwrap() + .unwrap(); + + // 75 (3/2 of the returned 50 units of call weight, 0 returned of ext weight) is + // refunded + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - (165 - 75) - 5); }); } #[test] -fn signed_extension_transaction_payment_is_bounded() { +fn transaction_extension_transaction_payment_is_bounded() { ExtBuilder::default().balance_factor(1000).byte_fee(0).build().execute_with(|| { // maximum weight possible - assert_ok!(ChargeTransactionPayment::::from(0).pre_dispatch( - &1, - CALL, - &info_from_weight(Weight::MAX), - 10 - )); + let info = info_from_weight(Weight::MAX); + assert_ok!(Ext::from(0).validate_and_prepare(Some(1).into(), CALL, &info, 10)); // fee will be proportional to what is the actual maximum weight in the runtime. assert_eq!( Balances::free_balance(&1), @@ -220,7 +217,7 @@ fn signed_extension_transaction_payment_is_bounded() { } #[test] -fn signed_extension_allows_free_transactions() { +fn transaction_extension_allows_free_transactions() { ExtBuilder::default() .base_weight(Weight::from_parts(100, 0)) .balance_factor(0) @@ -232,38 +229,30 @@ fn signed_extension_allows_free_transactions() { let len = 100; // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. - let operational_transaction = DispatchInfo { - weight: Weight::from_parts(0, 0), + let op_tx = DispatchInfo { + call_weight: Weight::from_parts(0, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::No, }; - assert_ok!(ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &operational_transaction, - len - )); + assert_ok!(Ext::from(0).validate_only(Some(1).into(), CALL, &op_tx, len)); // like a InsecureFreeNormal - let free_transaction = DispatchInfo { - weight: Weight::from_parts(0, 0), + let free_tx = DispatchInfo { + call_weight: Weight::from_parts(0, 0), + extension_weight: Weight::zero(), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; - assert_noop!( - ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &free_transaction, - len - ), + assert_eq!( + Ext::from(0).validate_only(Some(1).into(), CALL, &free_tx, len).unwrap_err(), TransactionValidityError::Invalid(InvalidTransaction::Payment), ); }); } #[test] -fn signed_ext_length_fee_is_also_updated_per_congestion() { +fn transaction_ext_length_fee_is_also_updated_per_congestion() { ExtBuilder::default() .base_weight(Weight::from_parts(5, 0)) .balance_factor(10) @@ -272,18 +261,15 @@ fn signed_ext_length_fee_is_also_updated_per_congestion() { // all fees should be x1.5 NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); let len = 10; - - assert_ok!( - ChargeTransactionPayment::::from(10) // tipped - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(3, 0)), len) - ); + let info = info_from_weight(Weight::from_parts(3, 0)); + assert_ok!(Ext::from(10).validate_and_prepare(Some(1).into(), CALL, &info, len)); assert_eq!( Balances::free_balance(1), 100 // original - - 10 // tip - - 5 // base - - 10 // len - - (3 * 3 / 2) // adjusted weight + - 10 // tip + - 5 // base + - 10 // len + - (3 * 3 / 2) // adjusted weight ); }) } @@ -293,62 +279,62 @@ fn query_info_and_fee_details_works() { let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 2, value: 69 }); let origin = 111111; let extra = (); - let xt = TestXt::new(call.clone(), Some((origin, extra))); + let xt = UncheckedExtrinsic::new_signed(call.clone(), origin, (), extra); let info = xt.get_dispatch_info(); let ext = xt.encode(); let len = ext.len() as u32; - let unsigned_xt = TestXt::<_, ()>::new(call, None); + let unsigned_xt = UncheckedExtrinsic::::new_bare(call); let unsigned_xt_info = unsigned_xt.get_dispatch_info(); ExtBuilder::default() - .base_weight(Weight::from_parts(5, 0)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_info(xt.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_info(unsigned_xt.clone(), len), - RuntimeDispatchInfo { - weight: unsigned_xt_info.weight, - class: unsigned_xt_info.class, - partial_fee: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(xt, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, - len_fee: len as u64, - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 - }), - tip: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(unsigned_xt, len), - FeeDetails { inclusion_fee: None, tip: 0 }, - ); - }); + .base_weight(Weight::from_parts(5, 0)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_info(xt.clone(), len), + RuntimeDispatchInfo { + weight: info.total_weight(), + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.total_weight().min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_info(unsigned_xt.clone(), len), + RuntimeDispatchInfo { + weight: unsigned_xt_info.call_weight, + class: unsigned_xt_info.class, + partial_fee: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(xt, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, + len_fee: len as u64, + adjusted_weight_fee: info + .total_weight() + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 + }), + tip: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(unsigned_xt, len), + FeeDetails { inclusion_fee: None, tip: 0 }, + ); + }); } #[test] @@ -359,39 +345,39 @@ fn query_call_info_and_fee_details_works() { let len = encoded_call.len() as u32; ExtBuilder::default() - .base_weight(Weight::from_parts(5, 0)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_call_info(call.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_call_fee_details(call, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, /* base * weight_fee */ - len_fee: len as u64, /* len * 1 */ - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multiplier */ - }), - tip: 0, - }, - ); - }); + .base_weight(Weight::from_parts(5, 0)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_call_info(call.clone(), len), + RuntimeDispatchInfo { + weight: info.total_weight(), + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.total_weight().min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_call_fee_details(call, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, /* base * weight_fee */ + len_fee: len as u64, /* len * 1 */ + adjusted_weight_fee: info + .total_weight() + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ + }), + tip: 0, + }, + ); + }); } #[test] @@ -407,14 +393,16 @@ fn compute_fee_works_without_multiplier() { // Tip only, no fees works let dispatch_info = DispatchInfo { - weight: Weight::from_parts(0, 0), + call_weight: Weight::from_parts(0, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::No, }; assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 10), 10); // No tip, only base fee works let dispatch_info = DispatchInfo { - weight: Weight::from_parts(0, 0), + call_weight: Weight::from_parts(0, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -425,7 +413,8 @@ fn compute_fee_works_without_multiplier() { assert_eq!(Pallet::::compute_fee(42, &dispatch_info, 0), 520); // Weight fee + base fee works let dispatch_info = DispatchInfo { - weight: Weight::from_parts(1000, 0), + call_weight: Weight::from_parts(1000, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -445,7 +434,8 @@ fn compute_fee_works_with_multiplier() { NextFeeMultiplier::::put(Multiplier::saturating_from_rational(3, 2)); // Base fee is unaffected by multiplier let dispatch_info = DispatchInfo { - weight: Weight::from_parts(0, 0), + call_weight: Weight::from_parts(0, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -453,7 +443,8 @@ fn compute_fee_works_with_multiplier() { // Everything works together :) let dispatch_info = DispatchInfo { - weight: Weight::from_parts(123, 0), + call_weight: Weight::from_parts(123, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -478,7 +469,8 @@ fn compute_fee_works_with_negative_multiplier() { // Base fee is unaffected by multiplier. let dispatch_info = DispatchInfo { - weight: Weight::from_parts(0, 0), + call_weight: Weight::from_parts(0, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -486,7 +478,8 @@ fn compute_fee_works_with_negative_multiplier() { // Everything works together. let dispatch_info = DispatchInfo { - weight: Weight::from_parts(123, 0), + call_weight: Weight::from_parts(123, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -508,7 +501,8 @@ fn compute_fee_does_not_overflow() { .execute_with(|| { // Overflow is handled let dispatch_info = DispatchInfo { - weight: Weight::MAX, + call_weight: Weight::MAX, + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; @@ -528,27 +522,23 @@ fn refund_does_not_recreate_account() { .execute_with(|| { // So events are emitted System::set_block_number(10); - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) + let info = info_from_weight(Weight::from_parts(100, 0)); + Ext::from(5 /* tipped */) + .test_run(Some(2).into(), CALL, &info, 10, |origin| { + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + // kill the account between pre and post dispatch + assert_ok!(Balances::transfer_allow_death( + origin, + 3, + Balances::free_balance(2) + )); + assert_eq!(Balances::free_balance(2), 0); + + Ok(post_info_from_weight(Weight::from_parts(50, 0))) + }) + .unwrap() .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - // kill the account between pre and post dispatch - assert_ok!(Balances::transfer_allow_death( - Some(2).into(), - 3, - Balances::free_balance(2) - )); - assert_eq!(Balances::free_balance(2), 0); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(100, 0)), - &post_info_from_weight(Weight::from_parts(50, 0)), - len, - &Ok(()) - )); assert_eq!(Balances::free_balance(2), 0); // Transfer Event System::assert_has_event(RuntimeEvent::Balances(pallet_balances::Event::Transfer { @@ -570,20 +560,15 @@ fn actual_weight_higher_than_max_refunds_nothing() { .base_weight(Weight::from_parts(5, 0)) .build() .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_parts(100, 0)), len) + let info = info_from_weight(Weight::from_parts(100, 0)); + Ext::from(5 /* tipped */) + .test_run(Some(2).into(), CALL, &info, 10, |_| { + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + Ok(post_info_from_weight(Weight::from_parts(101, 0))) + }) + .unwrap() .unwrap(); assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_parts(100, 0)), - &post_info_from_weight(Weight::from_parts(101, 0)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); }); } @@ -596,25 +581,21 @@ fn zero_transfer_on_free_transaction() { .execute_with(|| { // So events are emitted System::set_block_number(10); - let len = 10; - let dispatch_info = DispatchInfo { - weight: Weight::from_parts(100, 0), + let info = DispatchInfo { + call_weight: Weight::from_parts(100, 0), + extension_weight: Weight::zero(), pays_fee: Pays::No, class: DispatchClass::Normal, }; let user = 69; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&user, CALL, &dispatch_info, len) + Ext::from(0) + .test_run(Some(user).into(), CALL, &info, 10, |_| { + assert_eq!(Balances::total_balance(&user), 0); + Ok(default_post_info()) + }) + .unwrap() .unwrap(); assert_eq!(Balances::total_balance(&user), 0); - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &dispatch_info, - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::total_balance(&user), 0); // TransactionFeePaid Event System::assert_has_event(RuntimeEvent::TransactionPayment( pallet_transaction_payment::Event::TransactionFeePaid { @@ -633,33 +614,33 @@ fn refund_consistent_with_actual_weight() { .base_weight(Weight::from_parts(7, 0)) .build() .execute_with(|| { - let info = info_from_weight(Weight::from_parts(100, 0)); - let post_info = post_info_from_weight(Weight::from_parts(33, 0)); + let mut info = info_from_weight(Weight::from_parts(100, 0)); + let tip = 5; + let ext = Ext::from(tip); + let ext_weight = ext.weight(CALL); + info.extension_weight = ext_weight; + let mut post_info = post_info_from_weight(Weight::from_parts(33, 0)); let prev_balance = Balances::free_balance(2); let len = 10; - let tip = 5; NextFeeMultiplier::::put(Multiplier::saturating_from_rational(5, 4)); - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) + let actual_post_info = ext + .test_run(Some(2).into(), CALL, &info, len, |_| Ok(post_info)) + .unwrap() .unwrap(); - - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); + post_info + .actual_weight + .as_mut() + .map(|w| w.saturating_accrue(Ext::from(tip).weight(CALL))); + assert_eq!(post_info, actual_post_info); let refund_based_fee = prev_balance - Balances::free_balance(2); let actual_fee = - Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); + Pallet::::compute_actual_fee(len as u32, &info, &actual_post_info, tip); - // 33 weight, 10 length, 7 base, 5 tip - assert_eq!(actual_fee, 7 + 10 + (33 * 5 / 4) + 5); + // 33 call weight, 10 ext weight, 10 length, 7 base, 5 tip + assert_eq!(actual_fee, 7 + 10 + ((33 + 10) * 5 / 4) + 5); assert_eq!(refund_based_fee, actual_fee); }); } @@ -671,41 +652,35 @@ fn should_alter_operational_priority() { ExtBuilder::default().balance_factor(100).build().execute_with(|| { let normal = DispatchInfo { - weight: Weight::from_parts(100, 0), + call_weight: Weight::from_parts(100, 0), + extension_weight: Weight::zero(), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; + let ext = Ext::from(tip); + let priority = ext.validate_only(Some(2).into(), CALL, &normal, len).unwrap().0.priority; assert_eq!(priority, 60); - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - + let ext = Ext::from(2 * tip); + let priority = ext.validate_only(Some(2).into(), CALL, &normal, len).unwrap().0.priority; assert_eq!(priority, 110); }); ExtBuilder::default().balance_factor(100).build().execute_with(|| { let op = DispatchInfo { - weight: Weight::from_parts(100, 0), + call_weight: Weight::from_parts(100, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; + + let ext = Ext::from(tip); + let priority = ext.validate_only(Some(2).into(), CALL, &op, len).unwrap().0.priority; assert_eq!(priority, 5810); - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; + let ext = Ext::from(2 * tip); + let priority = ext.validate_only(Some(2).into(), CALL, &op, len).unwrap().0.priority; assert_eq!(priority, 6110); }); } @@ -717,28 +692,25 @@ fn no_tip_has_some_priority() { ExtBuilder::default().balance_factor(100).build().execute_with(|| { let normal = DispatchInfo { - weight: Weight::from_parts(100, 0), + call_weight: Weight::from_parts(100, 0), + extension_weight: Weight::zero(), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - + let ext = Ext::from(tip); + let priority = ext.validate_only(Some(2).into(), CALL, &normal, len).unwrap().0.priority; assert_eq!(priority, 10); }); ExtBuilder::default().balance_factor(100).build().execute_with(|| { let op = DispatchInfo { - weight: Weight::from_parts(100, 0), + call_weight: Weight::from_parts(100, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; + let ext = Ext::from(tip); + let priority = ext.validate_only(Some(2).into(), CALL, &op, len).unwrap().0.priority; assert_eq!(priority, 5510); }); } @@ -746,34 +718,32 @@ fn no_tip_has_some_priority() { #[test] fn higher_tip_have_higher_priority() { let get_priorities = |tip: u64| { - let mut priority1 = 0; - let mut priority2 = 0; + let mut pri1 = 0; + let mut pri2 = 0; let len = 10; ExtBuilder::default().balance_factor(100).build().execute_with(|| { let normal = DispatchInfo { - weight: Weight::from_parts(100, 0), + call_weight: Weight::from_parts(100, 0), + extension_weight: Weight::zero(), class: DispatchClass::Normal, pays_fee: Pays::Yes, }; - priority1 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; + let ext = Ext::from(tip); + pri1 = ext.validate_only(Some(2).into(), CALL, &normal, len).unwrap().0.priority; }); ExtBuilder::default().balance_factor(100).build().execute_with(|| { let op = DispatchInfo { - weight: Weight::from_parts(100, 0), + call_weight: Weight::from_parts(100, 0), + extension_weight: Weight::zero(), class: DispatchClass::Operational, pays_fee: Pays::Yes, }; - priority2 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; + let ext = Ext::from(tip); + pri2 = ext.validate_only(Some(2).into(), CALL, &op, len).unwrap().0.priority; }); - (priority1, priority2) + (pri1, pri2) }; let mut prev_priorities = get_priorities(0); @@ -801,19 +771,11 @@ fn post_info_can_change_pays_fee() { NextFeeMultiplier::::put(Multiplier::saturating_from_rational(5, 4)); - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) + let post_info = ChargeTransactionPayment::::from(tip) + .test_run(Some(2).into(), CALL, &info, len, |_| Ok(post_info)) + .unwrap() .unwrap(); - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); - let refund_based_fee = prev_balance - Balances::free_balance(2); let actual_fee = Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); @@ -843,3 +805,40 @@ fn genesis_default_works() { assert_eq!(NextFeeMultiplier::::get(), Multiplier::saturating_from_integer(1)); }); } + +#[test] +fn no_fee_and_no_weight_for_other_origins() { + ExtBuilder::default().build().execute_with(|| { + let ext = Ext::from(0); + + let mut info = CALL.get_dispatch_info(); + info.extension_weight = ext.weight(CALL); + + // Ensure we test the refund. + assert!(info.extension_weight != Weight::zero()); + + let len = CALL.encoded_size(); + + let origin = frame_system::RawOrigin::Root.into(); + let (pre, origin) = ext.validate_and_prepare(origin, CALL, &info, len).unwrap(); + + assert!(origin.as_system_ref().unwrap().is_root()); + + let pd_res = Ok(()); + let mut post_info = frame_support::dispatch::PostDispatchInfo { + actual_weight: Some(info.total_weight()), + pays_fee: Default::default(), + }; + + >::post_dispatch( + pre, + &info, + &mut post_info, + len, + &pd_res, + ) + .unwrap(); + + assert_eq!(post_info.actual_weight, Some(info.call_weight)); + }) +} diff --git a/substrate/frame/transaction-payment/src/types.rs b/substrate/frame/transaction-payment/src/types.rs index 67c7311d0ca..d6b4a655744 100644 --- a/substrate/frame/transaction-payment/src/types.rs +++ b/substrate/frame/transaction-payment/src/types.rs @@ -111,7 +111,7 @@ pub struct RuntimeDispatchInfo /// The inclusion fee of this dispatch. /// /// This does not include a tip or anything else that - /// depends on the signature (i.e. depends on a `SignedExtension`). + /// depends on the signature (i.e. depends on a `TransactionExtension`). #[cfg_attr(feature = "std", serde(with = "serde_balance"))] pub partial_fee: Balance, } diff --git a/substrate/frame/transaction-payment/src/weights.rs b/substrate/frame/transaction-payment/src/weights.rs new file mode 100644 index 00000000000..bcffb2eb331 --- /dev/null +++ b/substrate/frame/transaction-payment/src/weights.rs @@ -0,0 +1,92 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_transaction_payment` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-01, 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` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// ./target/production/substrate-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_transaction_payment +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./substrate/frame/transaction-payment/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_transaction_payment`. +pub trait WeightInfo { + fn charge_transaction_payment() -> Weight; +} + +/// Weights for `pallet_transaction_payment` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 40_506_000 picoseconds. + Weight::from_parts(41_647_000, 1733) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) + /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Authorship::Author` (r:1 w:0) + /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) + /// Storage: `System::Digest` (r:1 w:0) + /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn charge_transaction_payment() -> Weight { + // Proof Size summary in bytes: + // Measured: `248` + // Estimated: `1733` + // Minimum execution time: 40_506_000 picoseconds. + Weight::from_parts(41_647_000, 1733) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + } +} diff --git a/substrate/frame/utility/src/lib.rs b/substrate/frame/utility/src/lib.rs index a4f66298f3f..26c38d1f045 100644 --- a/substrate/frame/utility/src/lib.rs +++ b/substrate/frame/utility/src/lib.rs @@ -249,7 +249,7 @@ pub mod pallet { T::WeightInfo::as_derivative() // AccountData for inner call origin accountdata. .saturating_add(T::DbWeight::get().reads_writes(1, 1)) - .saturating_add(dispatch_info.weight), + .saturating_add(dispatch_info.call_weight), dispatch_info.class, ) })] @@ -354,7 +354,7 @@ pub mod pallet { let dispatch_info = call.get_dispatch_info(); ( T::WeightInfo::dispatch_as() - .saturating_add(dispatch_info.weight), + .saturating_add(dispatch_info.call_weight), dispatch_info.class, ) })] @@ -466,7 +466,7 @@ pub mod pallet { (Weight::zero(), DispatchClass::Operational), |(total_weight, dispatch_class): (Weight, DispatchClass), di| { ( - total_weight.saturating_add(di.weight), + total_weight.saturating_add(di.call_weight), // If not all are `Operational`, we want to use `DispatchClass::Normal`. if di.class == DispatchClass::Normal { di.class } else { dispatch_class }, ) diff --git a/substrate/frame/utility/src/tests.rs b/substrate/frame/utility/src/tests.rs index 9755efaea41..274a90d77cf 100644 --- a/substrate/frame/utility/src/tests.rs +++ b/substrate/frame/utility/src/tests.rs @@ -296,7 +296,7 @@ fn as_derivative_handles_weight_refund() { let info = call.get_dispatch_info(); let result = call.dispatch(RuntimeOrigin::signed(1)); assert_ok!(result); - assert_eq!(extract_actual_weight(&result, &info), info.weight); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight); // Refund weight when ok let inner_call = call_foobar(false, start_weight, Some(end_weight)); @@ -308,7 +308,7 @@ fn as_derivative_handles_weight_refund() { let result = call.dispatch(RuntimeOrigin::signed(1)); assert_ok!(result); // Diff is refunded - assert_eq!(extract_actual_weight(&result, &info), info.weight - diff); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight - diff); // Full weight when err let inner_call = call_foobar(true, start_weight, None); @@ -323,7 +323,7 @@ fn as_derivative_handles_weight_refund() { DispatchErrorWithPostInfo { post_info: PostDispatchInfo { // No weight is refunded - actual_weight: Some(info.weight), + actual_weight: Some(info.call_weight), pays_fee: Pays::Yes, }, error: DispatchError::Other("The cake is a lie."), @@ -343,7 +343,7 @@ fn as_derivative_handles_weight_refund() { DispatchErrorWithPostInfo { post_info: PostDispatchInfo { // Diff is refunded - actual_weight: Some(info.weight - diff), + actual_weight: Some(info.call_weight - diff), pays_fee: Pays::Yes, }, error: DispatchError::Other("The cake is a lie."), @@ -456,14 +456,14 @@ fn batch_weight_calculation_doesnt_overflow() { let big_call = RuntimeCall::RootTesting(RootTestingCall::fill_block { ratio: Perbill::from_percent(50), }); - assert_eq!(big_call.get_dispatch_info().weight, Weight::MAX / 2); + assert_eq!(big_call.get_dispatch_info().call_weight, Weight::MAX / 2); // 3 * 50% saturates to 100% let batch_call = RuntimeCall::Utility(crate::Call::batch { calls: vec![big_call.clone(), big_call.clone(), big_call.clone()], }); - assert_eq!(batch_call.get_dispatch_info().weight, Weight::MAX); + assert_eq!(batch_call.get_dispatch_info().call_weight, Weight::MAX); }); } @@ -482,7 +482,7 @@ fn batch_handles_weight_refund() { let info = call.get_dispatch_info(); let result = call.dispatch(RuntimeOrigin::signed(1)); assert_ok!(result); - assert_eq!(extract_actual_weight(&result, &info), info.weight); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight); // Refund weight when ok let inner_call = call_foobar(false, start_weight, Some(end_weight)); @@ -492,7 +492,7 @@ fn batch_handles_weight_refund() { let result = call.dispatch(RuntimeOrigin::signed(1)); assert_ok!(result); // Diff is refunded - assert_eq!(extract_actual_weight(&result, &info), info.weight - diff * batch_len); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight - diff * batch_len); // Full weight when err let good_call = call_foobar(false, start_weight, None); @@ -506,7 +506,7 @@ fn batch_handles_weight_refund() { utility::Event::BatchInterrupted { index: 1, error: DispatchError::Other("") }.into(), ); // No weight is refunded - assert_eq!(extract_actual_weight(&result, &info), info.weight); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight); // Refund weight when err let good_call = call_foobar(false, start_weight, Some(end_weight)); @@ -520,7 +520,7 @@ fn batch_handles_weight_refund() { System::assert_last_event( utility::Event::BatchInterrupted { index: 1, error: DispatchError::Other("") }.into(), ); - assert_eq!(extract_actual_weight(&result, &info), info.weight - diff * batch_len); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight - diff * batch_len); // Partial batch completion let good_call = call_foobar(false, start_weight, Some(end_weight)); @@ -571,7 +571,7 @@ fn batch_all_revert() { DispatchErrorWithPostInfo { post_info: PostDispatchInfo { actual_weight: Some( - ::WeightInfo::batch_all(2) + info.weight * 2 + ::WeightInfo::batch_all(2) + info.call_weight * 2 ), pays_fee: Pays::Yes }, @@ -598,7 +598,7 @@ fn batch_all_handles_weight_refund() { let info = call.get_dispatch_info(); let result = call.dispatch(RuntimeOrigin::signed(1)); assert_ok!(result); - assert_eq!(extract_actual_weight(&result, &info), info.weight); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight); // Refund weight when ok let inner_call = call_foobar(false, start_weight, Some(end_weight)); @@ -608,7 +608,7 @@ fn batch_all_handles_weight_refund() { let result = call.dispatch(RuntimeOrigin::signed(1)); assert_ok!(result); // Diff is refunded - assert_eq!(extract_actual_weight(&result, &info), info.weight - diff * batch_len); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight - diff * batch_len); // Full weight when err let good_call = call_foobar(false, start_weight, None); @@ -619,7 +619,7 @@ fn batch_all_handles_weight_refund() { let result = call.dispatch(RuntimeOrigin::signed(1)); assert_err_ignore_postinfo!(result, "The cake is a lie."); // No weight is refunded - assert_eq!(extract_actual_weight(&result, &info), info.weight); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight); // Refund weight when err let good_call = call_foobar(false, start_weight, Some(end_weight)); @@ -630,7 +630,7 @@ fn batch_all_handles_weight_refund() { let info = call.get_dispatch_info(); let result = call.dispatch(RuntimeOrigin::signed(1)); assert_err_ignore_postinfo!(result, "The cake is a lie."); - assert_eq!(extract_actual_weight(&result, &info), info.weight - diff * batch_len); + assert_eq!(extract_actual_weight(&result, &info), info.call_weight - diff * batch_len); // Partial batch completion let good_call = call_foobar(false, start_weight, Some(end_weight)); @@ -664,7 +664,9 @@ fn batch_all_does_not_nest() { Utility::batch_all(RuntimeOrigin::signed(1), vec![batch_all.clone()]), DispatchErrorWithPostInfo { post_info: PostDispatchInfo { - actual_weight: Some(::WeightInfo::batch_all(1) + info.weight), + actual_weight: Some( + ::WeightInfo::batch_all(1) + info.call_weight + ), pays_fee: Pays::Yes }, error: frame_system::Error::::CallFiltered.into(), @@ -789,7 +791,7 @@ fn batch_all_doesnt_work_with_inherents() { batch_all.dispatch(RuntimeOrigin::signed(1)), DispatchErrorWithPostInfo { post_info: PostDispatchInfo { - actual_weight: Some(info.weight), + actual_weight: Some(info.call_weight), pays_fee: Pays::Yes }, error: frame_system::Error::::CallFiltered.into(), @@ -805,7 +807,7 @@ fn batch_works_with_council_origin() { calls: vec![RuntimeCall::Democracy(mock_democracy::Call::external_propose_majority {})], }); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash = BlakeTwo256::hash_of(&proposal); assert_ok!(Council::propose( @@ -842,7 +844,7 @@ fn force_batch_works_with_council_origin() { calls: vec![RuntimeCall::Democracy(mock_democracy::Call::external_propose_majority {})], }); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); - let proposal_weight = proposal.get_dispatch_info().weight; + let proposal_weight = proposal.get_dispatch_info().call_weight; let hash = BlakeTwo256::hash_of(&proposal); assert_ok!(Council::propose( @@ -892,7 +894,7 @@ fn with_weight_works() { })); // Weight before is max. assert_eq!( - upgrade_code_call.get_dispatch_info().weight, + upgrade_code_call.get_dispatch_info().call_weight, ::SystemWeightInfo::set_code() ); assert_eq!( @@ -905,7 +907,7 @@ fn with_weight_works() { weight: Weight::from_parts(123, 456), }; // Weight after is set by Root. - assert_eq!(with_weight_call.get_dispatch_info().weight, Weight::from_parts(123, 456)); + assert_eq!(with_weight_call.get_dispatch_info().call_weight, Weight::from_parts(123, 456)); assert_eq!( with_weight_call.get_dispatch_info().class, frame_support::dispatch::DispatchClass::Operational diff --git a/substrate/frame/verify-signature/Cargo.toml b/substrate/frame/verify-signature/Cargo.toml new file mode 100644 index 00000000000..3c5fd5e6515 --- /dev/null +++ b/substrate/frame/verify-signature/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "pallet-verify-signature" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "FRAME verify signature pallet" +readme = "README.md" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true } +scale-info = { features = ["derive"], workspace = true } +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-weights = { features = ["serde"], workspace = true } + +[dev-dependencies] +pallet-balances = { workspace = true, default-features = true } +pallet-root-testing = { workspace = true, default-features = true } +pallet-collective = { workspace = true, default-features = true } +pallet-timestamp = { workspace = true, default-features = true } +sp-core = { workspace = true, default-features = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-collective/std", + "pallet-root-testing/std", + "pallet-timestamp/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-weights/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-collective/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "pallet-collective/try-runtime", + "pallet-root-testing/try-runtime", + "pallet-timestamp/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/verify-signature/README.md b/substrate/frame/verify-signature/README.md new file mode 100644 index 00000000000..7748315c61c --- /dev/null +++ b/substrate/frame/verify-signature/README.md @@ -0,0 +1,19 @@ +# Verify Signature Module +A module that provides a `TransactionExtension` that validates a signature against a payload and +authorizes the origin. + +## Overview + +This module serves two purposes: +- `VerifySignature`: A `TransactionExtension` that checks the provided signature against a payload + constructed through hashing the inherited implication with `blake2b_256`. If the signature is + valid, then the extension authorizes the origin as signed. The extension can be disabled, or + passthrough, allowing users to use other extensions to authorize different origins other than the + traditionally signed origin. +- Benchmarking: The extension is bound within a pallet to leverage the benchmarking functionality in + FRAME. The `Signature` and `Signer` types are specified in the pallet configuration and a + benchmark helper trait is used to create a signature which is then validated in the benchmark. + +[`Config`]: ./trait.Config.html + +License: Apache-2.0 diff --git a/substrate/frame/verify-signature/src/benchmarking.rs b/substrate/frame/verify-signature/src/benchmarking.rs new file mode 100644 index 00000000000..2b592a4023e --- /dev/null +++ b/substrate/frame/verify-signature/src/benchmarking.rs @@ -0,0 +1,65 @@ +// 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. + +//! Benchmarks for Verify Signature Pallet + +#![cfg(feature = "runtime-benchmarks")] + +extern crate alloc; + +use super::*; + +#[allow(unused)] +use crate::{extension::VerifySignature, Config, Pallet as VerifySignaturePallet}; +use alloc::vec; +use frame_benchmarking::{v2::*, BenchmarkError}; +use frame_support::dispatch::{DispatchInfo, GetDispatchInfo}; +use frame_system::{Call as SystemCall, RawOrigin}; +use sp_io::hashing::blake2_256; +use sp_runtime::traits::{AsTransactionAuthorizedOrigin, Dispatchable, TransactionExtension}; + +pub trait BenchmarkHelper { + fn create_signature(entropy: &[u8], msg: &[u8]) -> (Signature, Signer); +} + +#[benchmarks(where + T: Config + Send + Sync, + T::RuntimeCall: Dispatchable + GetDispatchInfo, + T::RuntimeOrigin: AsTransactionAuthorizedOrigin, +)] +mod benchmarks { + use super::*; + + #[benchmark] + fn verify_signature() -> Result<(), BenchmarkError> { + let entropy = [42u8; 256]; + let call: T::RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let info = call.get_dispatch_info(); + let msg = call.using_encoded(blake2_256).to_vec(); + let (signature, signer) = T::BenchmarkHelper::create_signature(&entropy, &msg[..]); + let ext = VerifySignature::::new_with_signature(signature, signer); + + #[block] + { + assert!(ext.validate(RawOrigin::None.into(), &call, &info, 0, (), &call).is_ok()); + } + + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); +} diff --git a/substrate/frame/verify-signature/src/extension.rs b/substrate/frame/verify-signature/src/extension.rs new file mode 100644 index 00000000000..4490a0a600b --- /dev/null +++ b/substrate/frame/verify-signature/src/extension.rs @@ -0,0 +1,157 @@ +// 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. + +//! Transaction extension which validates a signature against a payload constructed from a call and +//! the rest of the transaction extension pipeline. + +use crate::{Config, WeightInfo}; +use codec::{Decode, Encode}; +use frame_support::traits::OriginTrait; +use scale_info::TypeInfo; +use sp_io::hashing::blake2_256; +use sp_runtime::{ + impl_tx_ext_default, + traits::{ + transaction_extension::TransactionExtension, AsTransactionAuthorizedOrigin, DispatchInfoOf, + Dispatchable, Verify, + }, + transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction}, +}; +use sp_weights::Weight; + +/// Extension that, if enabled, validates a signature type against the payload constructed from the +/// call and the rest of the transaction extension pipeline. This extension provides the +/// functionality that traditionally signed transactions had with the implicit signature checking +/// implemented in [`Checkable`](sp_runtime::traits::Checkable). It is meant to be placed ahead of +/// any other extensions that do authorization work in the [`TransactionExtension`] pipeline. +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub enum VerifySignature +where + T: Config + Send + Sync, +{ + /// The extension will verify the signature and, if successful, authorize a traditionally + /// signed transaction. + Signed { + /// The signature provided by the transaction submitter. + signature: T::Signature, + /// The account that signed the payload. + account: T::AccountId, + }, + /// The extension is disabled and will be passthrough. + Disabled, +} + +impl core::fmt::Debug for VerifySignature +where + T: Config + Send + Sync, +{ + #[cfg(feature = "std")] + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "VerifySignature") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result { + Ok(()) + } +} + +impl VerifySignature +where + T: Config + Send + Sync, +{ + /// Create a new extension instance that will validate the provided signature. + pub fn new_with_signature(signature: T::Signature, account: T::AccountId) -> Self { + Self::Signed { signature, account } + } + + /// Create a new passthrough extension instance. + pub fn new_disabled() -> Self { + Self::Disabled + } +} + +impl TransactionExtension for VerifySignature +where + T: Config + Send + Sync, + ::RuntimeOrigin: AsTransactionAuthorizedOrigin, +{ + const IDENTIFIER: &'static str = "VerifyMultiSignature"; + type Implicit = (); + type Val = (); + type Pre = (); + + fn weight(&self, _call: &T::RuntimeCall) -> Weight { + match &self { + // The benchmarked weight of the payload construction and signature checking. + Self::Signed { .. } => T::WeightInfo::verify_signature(), + // When the extension is passthrough, it consumes no weight. + Self::Disabled => Weight::zero(), + } + } + + fn validate( + &self, + mut origin: ::RuntimeOrigin, + _call: &T::RuntimeCall, + _info: &DispatchInfoOf, + _len: usize, + _: (), + inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + // If the extension is disabled, return early. + let (signature, account) = match &self { + Self::Signed { signature, account } => (signature, account), + Self::Disabled => return Ok((Default::default(), (), origin)), + }; + + // This extension must receive an unauthorized origin as it is meant to headline the + // authorization extension pipeline. Any extensions that precede this one must not authorize + // any origin and serve some other functional purpose. + if origin.is_transaction_authorized() { + return Err(InvalidTransaction::BadSigner.into()); + } + + // Construct the payload that the signature will be validated against. The inherited + // implication contains the encoded bytes of the call and all of the extension data of the + // extensions that follow in the `TransactionExtension` pipeline. + // + // In other words: + // - extensions that precede this extension are ignored in terms of signature validation; + // - extensions that follow this extension are included in the payload to be signed (as if + // they were the entire `SignedExtension` pipeline in the traditional signed transaction + // model). + // + // The encoded bytes of the payload are then hashed using `blake2_256`. + let msg = inherited_implication.using_encoded(blake2_256); + + // The extension was enabled, so the signature must match. + if !signature.verify(&msg[..], account) { + Err(InvalidTransaction::BadProof)? + } + + // Return the signer as the transaction origin. + origin.set_caller_from_signed(account.clone()); + Ok((ValidTransaction::default(), (), origin)) + } + + impl_tx_ext_default!(T::RuntimeCall; prepare); +} diff --git a/substrate/frame/verify-signature/src/lib.rs b/substrate/frame/verify-signature/src/lib.rs new file mode 100644 index 00000000000..96d83dbef9f --- /dev/null +++ b/substrate/frame/verify-signature/src/lib.rs @@ -0,0 +1,68 @@ +// 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. + +//! Transaction extension which validates a signature against a payload constructed from a call and +//! the rest of the transaction extension pipeline. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod extension; +#[cfg(test)] +mod tests; +pub mod weights; + +extern crate alloc; + +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; +use codec::{Decode, Encode}; +pub use extension::VerifySignature; +use frame_support::Parameter; +pub use weights::WeightInfo; + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use sp_runtime::traits::{IdentifyAccount, Verify}; + + #[pallet::pallet] + pub struct Pallet(_); + + /// Configuration trait. + #[pallet::config] + pub trait Config: frame_system::Config { + /// Signature type that the extension of this pallet can verify. + type Signature: Verify + + Parameter + + Encode + + Decode + + Send + + Sync; + /// The account identifier used by this pallet's signature type. + type AccountIdentifier: IdentifyAccount; + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + /// Helper to create a signature to be benchmarked. + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; + } +} diff --git a/substrate/frame/verify-signature/src/tests.rs b/substrate/frame/verify-signature/src/tests.rs new file mode 100644 index 00000000000..3e4c8db12fe --- /dev/null +++ b/substrate/frame/verify-signature/src/tests.rs @@ -0,0 +1,132 @@ +// 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. + +// Tests for Utility Pallet + +#![cfg(test)] + +use super::*; + +use extension::VerifySignature; +use frame_support::{ + derive_impl, + dispatch::GetDispatchInfo, + pallet_prelude::{InvalidTransaction, TransactionValidityError}, + traits::OriginTrait, +}; +use frame_system::Call as SystemCall; +use sp_io::hashing::blake2_256; +use sp_runtime::{ + testing::{TestSignature, UintAuthorityId}, + traits::DispatchTransaction, +}; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + VerifySignaturePallet: crate, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct BenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl crate::BenchmarkHelper for BenchmarkHelper { + fn create_signature(_entropy: &[u8], msg: &[u8]) -> (TestSignature, u64) { + (TestSignature(0, msg.to_vec()), 0) + } +} + +impl crate::Config for Test { + type Signature = TestSignature; + type AccountIdentifier = UintAuthorityId; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = BenchmarkHelper; +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_test_ext() -> sp_io::TestExternalities { + use sp_runtime::BuildStorage; + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +#[test] +fn verification_works() { + let who = 0; + let call: RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let sig = TestSignature(0, call.using_encoded(blake2_256).to_vec()); + let info = call.get_dispatch_info(); + + let (_, _, origin) = VerifySignature::::new_with_signature(sig, who) + .validate_only(None.into(), &call, &info, 0) + .unwrap(); + assert_eq!(origin.as_signer().unwrap(), &who) +} + +#[test] +fn bad_signature() { + let who = 0; + let call: RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let sig = TestSignature(0, b"bogus message".to_vec()); + let info = call.get_dispatch_info(); + + assert_eq!( + VerifySignature::::new_with_signature(sig, who) + .validate_only(None.into(), &call, &info, 0) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::BadProof) + ); +} + +#[test] +fn bad_starting_origin() { + let who = 0; + let call: RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let sig = TestSignature(0, b"bogus message".to_vec()); + let info = call.get_dispatch_info(); + + assert_eq!( + VerifySignature::::new_with_signature(sig, who) + .validate_only(Some(42).into(), &call, &info, 0) + .unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::BadSigner) + ); +} + +#[test] +fn disabled_extension_works() { + let who = 42; + let call: RuntimeCall = SystemCall::remark { remark: vec![] }.into(); + let info = call.get_dispatch_info(); + + let (_, _, origin) = VerifySignature::::new_disabled() + .validate_only(Some(who).into(), &call, &info, 0) + .unwrap(); + assert_eq!(origin.as_signer().unwrap(), &who) +} diff --git a/substrate/frame/verify-signature/src/weights.rs b/substrate/frame/verify-signature/src/weights.rs new file mode 100644 index 00000000000..2c1f0f79542 --- /dev/null +++ b/substrate/frame/verify-signature/src/weights.rs @@ -0,0 +1,75 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_verify_signature` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-09-24, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gleipnir`, CPU: `AMD Ryzen 9 7900X 12-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// ./target/debug/substrate-node +// benchmark +// pallet +// --steps=2 +// --repeat=2 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --pallet=pallet-verify-signature +// --chain=dev +// --output=./substrate/frame/verify-signature/src/weights.rs +// --header=./substrate/HEADER-APACHE2 +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_verify_signature`. +pub trait WeightInfo { + fn verify_signature() -> Weight; +} + +/// Weights for `pallet_verify_signature` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn verify_signature() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 48_953_000 picoseconds. + Weight::from_parts(49_254_000, 0) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + fn verify_signature() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 48_953_000 picoseconds. + Weight::from_parts(49_254_000, 0) + } +} diff --git a/substrate/frame/whitelist/src/benchmarking.rs b/substrate/frame/whitelist/src/benchmarking.rs index cbe6ee4becd..0d7605d9752 100644 --- a/substrate/frame/whitelist/src/benchmarking.rs +++ b/substrate/frame/whitelist/src/benchmarking.rs @@ -75,7 +75,7 @@ mod benchmarks { .map_err(|_| BenchmarkError::Weightless)?; let remark = alloc::vec![1u8; n as usize]; let call: ::RuntimeCall = frame_system::Call::remark { remark }.into(); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; let encoded_call = call.encode(); let call_encoded_len = encoded_call.len() as u32; let call_hash = T::Hashing::hash_of(&call); diff --git a/substrate/frame/whitelist/src/lib.rs b/substrate/frame/whitelist/src/lib.rs index de16c2c2da8..28887e0ca4a 100644 --- a/substrate/frame/whitelist/src/lib.rs +++ b/substrate/frame/whitelist/src/lib.rs @@ -178,7 +178,7 @@ pub mod pallet { .map_err(|_| Error::::UndecodableCall)?; ensure!( - call.get_dispatch_info().weight.all_lte(call_weight_witness), + call.get_dispatch_info().call_weight.all_lte(call_weight_witness), Error::::InvalidCallWeightWitness ); @@ -191,7 +191,7 @@ pub mod pallet { #[pallet::call_index(3)] #[pallet::weight({ - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; let call_len = call.encoded_size() as u32; T::WeightInfo::dispatch_whitelisted_call_with_preimage(call_len) diff --git a/substrate/frame/whitelist/src/tests.rs b/substrate/frame/whitelist/src/tests.rs index 3a60adbcfbe..b53cc93b195 100644 --- a/substrate/frame/whitelist/src/tests.rs +++ b/substrate/frame/whitelist/src/tests.rs @@ -73,7 +73,7 @@ fn test_whitelist_call_and_remove() { fn test_whitelist_call_and_execute() { new_test_ext().execute_with(|| { let call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; let encoded_call = call.encode(); let call_encoded_len = encoded_call.len() as u32; let call_hash = ::Hashing::hash(&encoded_call[..]); @@ -153,7 +153,7 @@ fn test_whitelist_call_and_execute_failing_call() { call_encoded_len: Default::default(), call_weight_witness: Weight::zero(), }); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; let encoded_call = call.encode(); let call_encoded_len = encoded_call.len() as u32; let call_hash = ::Hashing::hash(&encoded_call[..]); @@ -200,7 +200,7 @@ fn test_whitelist_call_and_execute_without_note_preimage() { fn test_whitelist_call_and_execute_decode_consumes_all() { new_test_ext().execute_with(|| { let call = RuntimeCall::System(frame_system::Call::remark_with_event { remark: vec![1] }); - let call_weight = call.get_dispatch_info().weight; + let call_weight = call.get_dispatch_info().call_weight; let mut call = call.encode(); // Appending something does not make the encoded call invalid. // This tests that the decode function consumes all data. diff --git a/substrate/primitives/consensus/pow/Cargo.toml b/substrate/primitives/consensus/pow/Cargo.toml index 8731015f7da..171137a1a04 100644 --- a/substrate/primitives/consensus/pow/Cargo.toml +++ b/substrate/primitives/consensus/pow/Cargo.toml @@ -23,9 +23,4 @@ sp-runtime = { workspace = true } [features] default = ["std"] -std = [ - "codec/std", - "sp-api/std", - "sp-core/std", - "sp-runtime/std", -] +std = ["codec/std", "sp-api/std", "sp-core/std", "sp-runtime/std"] diff --git a/substrate/primitives/consensus/slots/Cargo.toml b/substrate/primitives/consensus/slots/Cargo.toml index 43f8c5514f7..2f993d3167a 100644 --- a/substrate/primitives/consensus/slots/Cargo.toml +++ b/substrate/primitives/consensus/slots/Cargo.toml @@ -23,12 +23,7 @@ sp-timestamp = { workspace = true } [features] default = ["std"] -std = [ - "codec/std", - "scale-info/std", - "serde/std", - "sp-timestamp/std", -] +std = ["codec/std", "scale-info/std", "serde/std", "sp-timestamp/std"] # Serde support without relying on std features. serde = ["dep:serde", "scale-info/serde"] diff --git a/substrate/primitives/inherents/src/lib.rs b/substrate/primitives/inherents/src/lib.rs index 80787669856..0ddc12dde06 100644 --- a/substrate/primitives/inherents/src/lib.rs +++ b/substrate/primitives/inherents/src/lib.rs @@ -98,10 +98,10 @@ //! and production. //! //! ``` -//! # use sp_runtime::testing::ExtrinsicWrapper; +//! # use sp_runtime::testing::{MockCallU64, TestXt}; //! # use sp_inherents::{InherentIdentifier, InherentData}; //! # use futures::FutureExt; -//! # type Block = sp_runtime::testing::Block>; +//! # type Block = sp_runtime::testing::Block>; //! # const INHERENT_IDENTIFIER: InherentIdentifier = *b"testinh0"; //! # struct InherentDataProvider; //! # #[async_trait::async_trait] diff --git a/substrate/primitives/metadata-ir/src/lib.rs b/substrate/primitives/metadata-ir/src/lib.rs index 18b20f2ccaa..4bd13b935af 100644 --- a/substrate/primitives/metadata-ir/src/lib.rs +++ b/substrate/primitives/metadata-ir/src/lib.rs @@ -86,7 +86,7 @@ mod test { call_ty: meta_type::<()>(), signature_ty: meta_type::<()>(), extra_ty: meta_type::<()>(), - signed_extensions: vec![], + extensions: vec![], }, ty: meta_type::<()>(), apis: vec![], diff --git a/substrate/primitives/metadata-ir/src/types.rs b/substrate/primitives/metadata-ir/src/types.rs index da4f5d7f371..199b692fbd8 100644 --- a/substrate/primitives/metadata-ir/src/types.rs +++ b/substrate/primitives/metadata-ir/src/types.rs @@ -178,10 +178,11 @@ pub struct ExtrinsicMetadataIR { pub call_ty: T::Type, /// The type of the extrinsic's signature. pub signature_ty: T::Type, - /// The type of the outermost Extra enum. + /// The type of the outermost Extra/Extensions enum. + // TODO: metadata-v16: remove this, the `implicit` type can be found in `extensions::implicit`. pub extra_ty: T::Type, - /// The signed extensions in the order they appear in the extrinsic. - pub signed_extensions: Vec>, + /// The transaction extensions in the order they appear in the extrinsic. + pub extensions: Vec>, } impl IntoPortable for ExtrinsicMetadataIR { @@ -195,7 +196,7 @@ impl IntoPortable for ExtrinsicMetadataIR { call_ty: registry.register_type(&self.call_ty), signature_ty: registry.register_type(&self.signature_ty), extra_ty: registry.register_type(&self.extra_ty), - signed_extensions: registry.map_into_portable(self.signed_extensions), + extensions: registry.map_into_portable(self.extensions), } } } @@ -225,23 +226,23 @@ impl IntoPortable for PalletAssociatedTypeMetadataIR { /// Metadata of an extrinsic's signed extension. #[derive(Clone, PartialEq, Eq, Encode, Debug)] -pub struct SignedExtensionMetadataIR { +pub struct TransactionExtensionMetadataIR { /// The unique signed extension identifier, which may be different from the type name. pub identifier: T::String, /// The type of the signed extension, with the data to be included in the extrinsic. pub ty: T::Type, - /// The type of the additional signed data, with the data to be included in the signed payload - pub additional_signed: T::Type, + /// The type of the implicit data, with the data to be included in the signed payload. + pub implicit: T::Type, } -impl IntoPortable for SignedExtensionMetadataIR { - type Output = SignedExtensionMetadataIR; +impl IntoPortable for TransactionExtensionMetadataIR { + type Output = TransactionExtensionMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - SignedExtensionMetadataIR { + TransactionExtensionMetadataIR { identifier: self.identifier.into_portable(registry), ty: registry.register_type(&self.ty), - additional_signed: registry.register_type(&self.additional_signed), + implicit: registry.register_type(&self.implicit), } } } diff --git a/substrate/primitives/metadata-ir/src/v14.rs b/substrate/primitives/metadata-ir/src/v14.rs index e1b7a24f765..70e84532add 100644 --- a/substrate/primitives/metadata-ir/src/v14.rs +++ b/substrate/primitives/metadata-ir/src/v14.rs @@ -20,8 +20,8 @@ use super::types::{ ExtrinsicMetadataIR, MetadataIR, PalletCallMetadataIR, PalletConstantMetadataIR, PalletErrorMetadataIR, PalletEventMetadataIR, PalletMetadataIR, PalletStorageMetadataIR, - SignedExtensionMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, - StorageHasherIR, + StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR, + TransactionExtensionMetadataIR, }; use frame_metadata::v14::{ @@ -137,12 +137,12 @@ impl From for PalletErrorMetadata { } } -impl From for SignedExtensionMetadata { - fn from(ir: SignedExtensionMetadataIR) -> Self { +impl From for SignedExtensionMetadata { + fn from(ir: TransactionExtensionMetadataIR) -> Self { SignedExtensionMetadata { identifier: ir.identifier, ty: ir.ty, - additional_signed: ir.additional_signed, + additional_signed: ir.implicit, } } } @@ -152,7 +152,7 @@ impl From for ExtrinsicMetadata { ExtrinsicMetadata { ty: ir.ty, version: ir.version, - signed_extensions: ir.signed_extensions.into_iter().map(Into::into).collect(), + signed_extensions: ir.extensions.into_iter().map(Into::into).collect(), } } } diff --git a/substrate/primitives/metadata-ir/src/v15.rs b/substrate/primitives/metadata-ir/src/v15.rs index a942eb73223..4b3b6106d27 100644 --- a/substrate/primitives/metadata-ir/src/v15.rs +++ b/substrate/primitives/metadata-ir/src/v15.rs @@ -21,7 +21,7 @@ use crate::OuterEnumsIR; use super::types::{ ExtrinsicMetadataIR, MetadataIR, PalletMetadataIR, RuntimeApiMetadataIR, - RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, SignedExtensionMetadataIR, + RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, TransactionExtensionMetadataIR, }; use frame_metadata::v15::{ @@ -87,12 +87,12 @@ impl From for PalletMetadata { } } -impl From for SignedExtensionMetadata { - fn from(ir: SignedExtensionMetadataIR) -> Self { +impl From for SignedExtensionMetadata { + fn from(ir: TransactionExtensionMetadataIR) -> Self { SignedExtensionMetadata { identifier: ir.identifier, ty: ir.ty, - additional_signed: ir.additional_signed, + additional_signed: ir.implicit, } } } @@ -105,7 +105,7 @@ impl From for ExtrinsicMetadata { call_ty: ir.call_ty, signature_ty: ir.signature_ty, extra_ty: ir.extra_ty, - signed_extensions: ir.signed_extensions.into_iter().map(Into::into).collect(), + signed_extensions: ir.extensions.into_iter().map(Into::into).collect(), } } } diff --git a/substrate/primitives/runtime/Cargo.toml b/substrate/primitives/runtime/Cargo.toml index c3413775b1c..8a812c3a577 100644 --- a/substrate/primitives/runtime/Cargo.toml +++ b/substrate/primitives/runtime/Cargo.toml @@ -39,6 +39,7 @@ tracing = { workspace = true, features = ["log"], default-features = false } binary-merkle-tree = { workspace = true } simple-mermaid = { version = "0.1.1", optional = true } +tuplex = { version = "0.1.2", default-features = false } [dev-dependencies] rand = { workspace = true, default-features = true } @@ -75,6 +76,7 @@ std = [ "sp-trie/std", "sp-weights/std", "tracing/std", + "tuplex/std", ] # Serde support without relying on std features. diff --git a/substrate/primitives/runtime/src/generic/block.rs b/substrate/primitives/runtime/src/generic/block.rs index 8ed79c7c8dc..a084a3703f9 100644 --- a/substrate/primitives/runtime/src/generic/block.rs +++ b/substrate/primitives/runtime/src/generic/block.rs @@ -99,7 +99,7 @@ where impl traits::Block for Block where Header: HeaderT + MaybeSerializeDeserialize, - Extrinsic: Member + Codec + traits::Extrinsic, + Extrinsic: Member + Codec + traits::ExtrinsicLike, { type Extrinsic = Extrinsic; type Header = Header; diff --git a/substrate/primitives/runtime/src/generic/checked_extrinsic.rs b/substrate/primitives/runtime/src/generic/checked_extrinsic.rs index 44325920bee..e2ecd5ed6da 100644 --- a/substrate/primitives/runtime/src/generic/checked_extrinsic.rs +++ b/substrate/primitives/runtime/src/generic/checked_extrinsic.rs @@ -18,81 +18,117 @@ //! Generic implementation of an extrinsic that has passed the verification //! stage. +use codec::Encode; +use sp_weights::Weight; + use crate::{ traits::{ - self, DispatchInfoOf, Dispatchable, MaybeDisplay, Member, PostDispatchInfoOf, - SignedExtension, ValidateUnsigned, + self, transaction_extension::TransactionExtension, AsTransactionAuthorizedOrigin, + DispatchInfoOf, DispatchTransaction, Dispatchable, MaybeDisplay, Member, + PostDispatchInfoOf, ValidateUnsigned, }, transaction_validity::{TransactionSource, TransactionValidity}, }; +/// The kind of extrinsic this is, including any fields required of that kind. This is basically +/// the full extrinsic except the `Call`. +#[derive(PartialEq, Eq, Clone, sp_core::RuntimeDebug)] +pub enum ExtrinsicFormat { + /// Extrinsic is bare; it must pass either the bare forms of `TransactionExtension` or + /// `ValidateUnsigned`, both deprecated, or alternatively a `ProvideInherent`. + Bare, + /// Extrinsic has a default `Origin` of `Signed(AccountId)` and must pass all + /// `TransactionExtension`s regular checks and includes all extension data. + Signed(AccountId, Extension), + /// Extrinsic has a default `Origin` of `None` and must pass all `TransactionExtension`s. + /// regular checks and includes all extension data. + General(Extension), +} + /// Definition of something that the external world might want to say; its existence implies that it /// has been checked and is good, particularly with regards to the signature. /// /// This is typically passed into [`traits::Applyable::apply`], which should execute /// [`CheckedExtrinsic::function`], alongside all other bits and bobs. #[derive(PartialEq, Eq, Clone, sp_core::RuntimeDebug)] -pub struct CheckedExtrinsic { +pub struct CheckedExtrinsic { /// Who this purports to be from and the number of extrinsics have come before /// from the same signer, if anyone (note this is not a signature). - pub signed: Option<(AccountId, Extra)>, + pub format: ExtrinsicFormat, /// The function that should be called. pub function: Call, } -impl traits::Applyable - for CheckedExtrinsic +impl traits::Applyable + for CheckedExtrinsic where AccountId: Member + MaybeDisplay, - Call: Member + Dispatchable, - Extra: SignedExtension, - RuntimeOrigin: From>, + Call: Member + Dispatchable + Encode, + Extension: TransactionExtension, + RuntimeOrigin: From> + AsTransactionAuthorizedOrigin, { type Call = Call; - fn validate>( + fn validate>( &self, - // TODO [#5006;ToDr] should source be passed to `SignedExtension`s? - // Perhaps a change for 2.0 to avoid breaking too much APIs? source: TransactionSource, info: &DispatchInfoOf, len: usize, ) -> TransactionValidity { - if let Some((ref id, ref extra)) = self.signed { - Extra::validate(extra, id, &self.function, info, len) - } else { - let valid = Extra::validate_unsigned(&self.function, info, len)?; - let unsigned_validation = U::validate_unsigned(source, &self.function)?; - Ok(valid.combine_with(unsigned_validation)) + match self.format { + ExtrinsicFormat::Bare => { + let inherent_validation = I::validate_unsigned(source, &self.function)?; + #[allow(deprecated)] + let legacy_validation = Extension::bare_validate(&self.function, info, len)?; + Ok(legacy_validation.combine_with(inherent_validation)) + }, + ExtrinsicFormat::Signed(ref signer, ref extension) => { + let origin = Some(signer.clone()).into(); + extension.validate_only(origin, &self.function, info, len).map(|x| x.0) + }, + ExtrinsicFormat::General(ref extension) => + extension.validate_only(None.into(), &self.function, info, len).map(|x| x.0), } } - fn apply>( + fn apply>( self, info: &DispatchInfoOf, len: usize, ) -> crate::ApplyExtrinsicResultWithInfo> { - let (maybe_who, maybe_pre) = if let Some((id, extra)) = self.signed { - let pre = Extra::pre_dispatch(extra, &id, &self.function, info, len)?; - (Some(id), Some(pre)) - } else { - Extra::pre_dispatch_unsigned(&self.function, info, len)?; - U::pre_dispatch(&self.function)?; - (None, None) - }; - let res = self.function.dispatch(RuntimeOrigin::from(maybe_who)); - let post_info = match res { - Ok(info) => info, - Err(err) => err.post_info, - }; - Extra::post_dispatch( - maybe_pre, - info, - &post_info, - len, - &res.map(|_| ()).map_err(|e| e.error), - )?; - Ok(res) + match self.format { + ExtrinsicFormat::Bare => { + I::pre_dispatch(&self.function)?; + // TODO: Separate logic from `TransactionExtension` into a new `InherentExtension` + // interface. + Extension::bare_validate_and_prepare(&self.function, info, len)?; + let res = self.function.dispatch(None.into()); + let mut post_info = res.unwrap_or_else(|err| err.post_info); + let pd_res = res.map(|_| ()).map_err(|e| e.error); + // TODO: Separate logic from `TransactionExtension` into a new `InherentExtension` + // interface. + Extension::bare_post_dispatch(info, &mut post_info, len, &pd_res)?; + Ok(res) + }, + ExtrinsicFormat::Signed(signer, extension) => + extension.dispatch_transaction(Some(signer).into(), self.function, info, len), + ExtrinsicFormat::General(extension) => + extension.dispatch_transaction(None.into(), self.function, info, len), + } + } +} + +impl> + CheckedExtrinsic +{ + /// Returns the weight of the extension of this transaction, if present. If the transaction + /// doesn't use any extension, the weight returned is equal to zero. + pub fn extension_weight(&self) -> Weight { + match &self.format { + ExtrinsicFormat::Bare => Weight::zero(), + ExtrinsicFormat::Signed(_, ext) | ExtrinsicFormat::General(ext) => + ext.weight(&self.function), + } } } diff --git a/substrate/primitives/runtime/src/generic/mod.rs b/substrate/primitives/runtime/src/generic/mod.rs index 3687f7cdb3b..007dee2684b 100644 --- a/substrate/primitives/runtime/src/generic/mod.rs +++ b/substrate/primitives/runtime/src/generic/mod.rs @@ -16,7 +16,7 @@ // limitations under the License. //! Generic implementations of [`crate::traits::Header`], [`crate::traits::Block`] and -//! [`crate::traits::Extrinsic`]. +//! [`crate::traits::ExtrinsicLike`]. mod block; mod checked_extrinsic; @@ -29,9 +29,10 @@ mod unchecked_extrinsic; pub use self::{ block::{Block, BlockId, SignedBlock}, - checked_extrinsic::CheckedExtrinsic, + checked_extrinsic::{CheckedExtrinsic, ExtrinsicFormat}, digest::{Digest, DigestItem, DigestItemRef, OpaqueDigestItemId}, era::{Era, Phase}, header::Header, - unchecked_extrinsic::{SignedPayload, UncheckedExtrinsic}, + unchecked_extrinsic::{Preamble, SignedPayload, UncheckedExtrinsic, EXTRINSIC_FORMAT_VERSION}, }; +pub use unchecked_extrinsic::UncheckedSignaturePayload; diff --git a/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs b/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs index 499b7c5f583..8c44e147f90 100644 --- a/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -18,10 +18,10 @@ //! Generic implementation of an unchecked (pre-verification) extrinsic. use crate::{ - generic::CheckedExtrinsic, + generic::{CheckedExtrinsic, ExtrinsicFormat}, traits::{ - self, Checkable, Extrinsic, ExtrinsicMetadata, IdentifyAccount, MaybeDisplay, Member, - SignaturePayload, SignedExtension, + self, transaction_extension::TransactionExtension, Checkable, Dispatchable, ExtrinsicLike, + ExtrinsicMetadata, IdentifyAccount, MaybeDisplay, Member, SignaturePayload, }, transaction_validity::{InvalidTransaction, TransactionValidityError}, OpaqueExtrinsic, @@ -33,16 +33,170 @@ use codec::{Compact, Decode, Encode, EncodeLike, Error, Input}; use core::fmt; use scale_info::{build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter}; use sp_io::hashing::blake2_256; +use sp_weights::Weight; + +/// Type to represent the version of the [Extension](TransactionExtension) used in this extrinsic. +pub type ExtensionVersion = u8; +/// Type to represent the extrinsic format version which defines an [UncheckedExtrinsic]. +pub type ExtrinsicVersion = u8; /// Current version of the [`UncheckedExtrinsic`] encoded format. /// /// This version needs to be bumped if the encoded representation changes. /// It ensures that if the representation is changed and the format is not known, /// the decoding fails. -const EXTRINSIC_FORMAT_VERSION: u8 = 4; +pub const EXTRINSIC_FORMAT_VERSION: ExtrinsicVersion = 5; +/// Legacy version of the [`UncheckedExtrinsic`] encoded format. +/// +/// This version was used in the signed/unsigned transaction model and is still supported for +/// compatibility reasons. It will be deprecated in favor of v5 extrinsics and an inherent/general +/// transaction model. +pub const LEGACY_EXTRINSIC_FORMAT_VERSION: ExtrinsicVersion = 4; +/// Current version of the [Extension](TransactionExtension) used in this +/// [extrinsic](UncheckedExtrinsic). +/// +/// This version needs to be bumped if there are breaking changes to the extension used in the +/// [UncheckedExtrinsic] implementation. +const EXTENSION_VERSION: ExtensionVersion = 0; /// The `SignaturePayload` of `UncheckedExtrinsic`. -type UncheckedSignaturePayload = (Address, Signature, Extra); +pub type UncheckedSignaturePayload = (Address, Signature, Extension); + +impl SignaturePayload + for UncheckedSignaturePayload +{ + type SignatureAddress = Address; + type Signature = Signature; + type SignatureExtra = Extension; +} + +/// A "header" for extrinsics leading up to the call itself. Determines the type of extrinsic and +/// holds any necessary specialized data. +#[derive(Eq, PartialEq, Clone)] +pub enum Preamble { + /// An extrinsic without a signature or any extension. This means it's either an inherent or + /// an old-school "Unsigned" (we don't use that terminology any more since it's confusable with + /// the general transaction which is without a signature but does have an extension). + /// + /// NOTE: In the future, once we remove `ValidateUnsigned`, this will only serve Inherent + /// extrinsics and thus can be renamed to `Inherent`. + Bare(ExtrinsicVersion), + /// An old-school transaction extrinsic which includes a signature of some hard-coded crypto. + /// Available only on extrinsic version 4. + Signed(Address, Signature, ExtensionVersion, Extension), + /// A new-school transaction extrinsic which does not include a signature by default. The + /// origin authorization, through signatures or other means, is performed by the transaction + /// extension in this extrinsic. Available starting with extrinsic version 5. + General(ExtensionVersion, Extension), +} + +const VERSION_MASK: u8 = 0b0011_1111; +const TYPE_MASK: u8 = 0b1100_0000; +const BARE_EXTRINSIC: u8 = 0b0000_0000; +const SIGNED_EXTRINSIC: u8 = 0b1000_0000; +const GENERAL_EXTRINSIC: u8 = 0b0100_0000; + +impl Decode for Preamble +where + Address: Decode, + Signature: Decode, + Extension: Decode, +{ + fn decode(input: &mut I) -> Result { + let version_and_type = input.read_byte()?; + + let version = version_and_type & VERSION_MASK; + let xt_type = version_and_type & TYPE_MASK; + + let preamble = match (version, xt_type) { + ( + extrinsic_version @ LEGACY_EXTRINSIC_FORMAT_VERSION..=EXTRINSIC_FORMAT_VERSION, + BARE_EXTRINSIC, + ) => Self::Bare(extrinsic_version), + (LEGACY_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC) => { + let address = Address::decode(input)?; + let signature = Signature::decode(input)?; + let ext = Extension::decode(input)?; + Self::Signed(address, signature, 0, ext) + }, + (EXTRINSIC_FORMAT_VERSION, GENERAL_EXTRINSIC) => { + let ext_version = ExtensionVersion::decode(input)?; + let ext = Extension::decode(input)?; + Self::General(ext_version, ext) + }, + (_, _) => return Err("Invalid transaction version".into()), + }; + + Ok(preamble) + } +} + +impl Encode for Preamble +where + Address: Encode, + Signature: Encode, + Extension: Encode, +{ + fn size_hint(&self) -> usize { + match &self { + Preamble::Bare(_) => EXTRINSIC_FORMAT_VERSION.size_hint(), + Preamble::Signed(address, signature, _, ext) => LEGACY_EXTRINSIC_FORMAT_VERSION + .size_hint() + .saturating_add(address.size_hint()) + .saturating_add(signature.size_hint()) + .saturating_add(ext.size_hint()), + Preamble::General(ext_version, ext) => EXTRINSIC_FORMAT_VERSION + .size_hint() + .saturating_add(ext_version.size_hint()) + .saturating_add(ext.size_hint()), + } + } + + fn encode_to(&self, dest: &mut T) { + match &self { + Preamble::Bare(extrinsic_version) => { + (extrinsic_version | BARE_EXTRINSIC).encode_to(dest); + }, + Preamble::Signed(address, signature, _, ext) => { + (LEGACY_EXTRINSIC_FORMAT_VERSION | SIGNED_EXTRINSIC).encode_to(dest); + address.encode_to(dest); + signature.encode_to(dest); + ext.encode_to(dest); + }, + Preamble::General(ext_version, ext) => { + (EXTRINSIC_FORMAT_VERSION | GENERAL_EXTRINSIC).encode_to(dest); + ext_version.encode_to(dest); + ext.encode_to(dest); + }, + } + } +} + +impl Preamble { + /// Returns `Some` if this is a signed extrinsic, together with the relevant inner fields. + pub fn to_signed(self) -> Option<(Address, Signature, Extension)> { + match self { + Self::Signed(a, s, _, e) => Some((a, s, e)), + _ => None, + } + } +} + +impl fmt::Debug for Preamble +where + Address: fmt::Debug, + Extension: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Bare(_) => write!(f, "Bare"), + Self::Signed(address, _, ext_version, tx_ext) => + write!(f, "Signed({:?}, {:?}, {:?})", address, ext_version, tx_ext), + Self::General(ext_version, tx_ext) => + write!(f, "General({:?}, {:?})", ext_version, tx_ext), + } + } +} /// An extrinsic right from the external world. This is unchecked and so can contain a signature. /// @@ -66,41 +220,28 @@ type UncheckedSignaturePayload = (Address, Signature, /// This can be checked using [`Checkable`], yielding a [`CheckedExtrinsic`], which is the /// counterpart of this type after its signature (and other non-negotiable validity checks) have /// passed. -#[derive(PartialEq, Eq, Clone)] -pub struct UncheckedExtrinsic -where - Extra: SignedExtension, -{ - /// The signature, address, number of extrinsics have come before from the same signer and an - /// era describing the longevity of this transaction, if this is a signed extrinsic. - /// - /// `None` if it is unsigned or an inherent. - pub signature: Option>, +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct UncheckedExtrinsic { + /// Information regarding the type of extrinsic this is (inherent or transaction) as well as + /// associated extension (`Extension`) data if it's a transaction and a possible signature. + pub preamble: Preamble, /// The function that should be called. pub function: Call, } -impl SignaturePayload - for UncheckedSignaturePayload -{ - type SignatureAddress = Address; - type Signature = Signature; - type SignatureExtra = Extra; -} - /// Manual [`TypeInfo`] implementation because of custom encoding. The data is a valid encoded /// `Vec`, but requires some logic to extract the signature and payload. /// /// See [`UncheckedExtrinsic::encode`] and [`UncheckedExtrinsic::decode`]. -impl TypeInfo - for UncheckedExtrinsic +impl TypeInfo + for UncheckedExtrinsic where Address: StaticTypeInfo, Call: StaticTypeInfo, Signature: StaticTypeInfo, - Extra: SignedExtension + StaticTypeInfo, + Extension: StaticTypeInfo, { - type Identity = UncheckedExtrinsic; + type Identity = UncheckedExtrinsic; fn type_info() -> Type { Type::builder() @@ -112,7 +253,7 @@ where TypeParameter::new("Address", Some(meta_type::

())), TypeParameter::new("Call", Some(meta_type::())), TypeParameter::new("Signature", Some(meta_type::())), - TypeParameter::new("Extra", Some(meta_type::())), + TypeParameter::new("Extra", Some(meta_type::())), ]) .docs(&["UncheckedExtrinsic raw bytes, requires custom decoding routine"]) // Because of the custom encoding, we can only accurately describe the encoding as an @@ -122,66 +263,104 @@ where } } -impl - UncheckedExtrinsic -{ - /// New instance of a signed extrinsic aka "transaction". - pub fn new_signed(function: Call, signed: Address, signature: Signature, extra: Extra) -> Self { - Self { signature: Some((signed, signature, extra)), function } +impl UncheckedExtrinsic { + /// New instance of a bare (ne unsigned) extrinsic. This could be used for an inherent or an + /// old-school "unsigned transaction" (which are new being deprecated in favour of general + /// transactions). + #[deprecated = "Use new_bare instead"] + pub fn new_unsigned(function: Call) -> Self { + Self::new_bare(function) } - /// New instance of an unsigned extrinsic aka "inherent". - pub fn new_unsigned(function: Call) -> Self { - Self { signature: None, function } + /// Returns `true` if this extrinsic instance is an inherent, `false`` otherwise. + pub fn is_inherent(&self) -> bool { + matches!(self.preamble, Preamble::Bare(_)) } -} -impl - Extrinsic for UncheckedExtrinsic -{ - type Call = Call; + /// Returns `true` if this extrinsic instance is an old-school signed transaction, `false` + /// otherwise. + pub fn is_signed(&self) -> bool { + matches!(self.preamble, Preamble::Signed(..)) + } - type SignaturePayload = UncheckedSignaturePayload; + /// Create an `UncheckedExtrinsic` from a `Preamble` and the actual `Call`. + pub fn from_parts(function: Call, preamble: Preamble) -> Self { + Self { preamble, function } + } - fn is_signed(&self) -> Option { - Some(self.signature.is_some()) + /// New instance of a bare (ne unsigned) extrinsic. + pub fn new_bare(function: Call) -> Self { + Self { preamble: Preamble::Bare(EXTRINSIC_FORMAT_VERSION), function } } - fn new(function: Call, signed_data: Option) -> Option { - Some(if let Some((address, signature, extra)) = signed_data { - Self::new_signed(function, address, signature, extra) - } else { - Self::new_unsigned(function) - }) + /// New instance of a bare (ne unsigned) extrinsic on extrinsic format version 4. + pub fn new_bare_legacy(function: Call) -> Self { + Self { preamble: Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION), function } + } + + /// New instance of an old-school signed transaction on extrinsic format version 4. + pub fn new_signed( + function: Call, + signed: Address, + signature: Signature, + tx_ext: Extension, + ) -> Self { + Self { preamble: Preamble::Signed(signed, signature, 0, tx_ext), function } + } + + /// New instance of an new-school unsigned transaction. + pub fn new_transaction(function: Call, tx_ext: Extension) -> Self { + Self { preamble: Preamble::General(EXTENSION_VERSION, tx_ext), function } + } +} + +impl ExtrinsicLike + for UncheckedExtrinsic +{ + fn is_bare(&self) -> bool { + matches!(self.preamble, Preamble::Bare(_)) + } + + fn is_signed(&self) -> Option { + Some(matches!(self.preamble, Preamble::Signed(..))) } } -impl Checkable - for UncheckedExtrinsic +// TODO: Migrate existing extension pipelines to support current `Signed` transactions as `General` +// transactions by adding an extension to validate signatures, as they are currently validated in +// the `Checkable` implementation for `Signed` transactions. + +impl Checkable + for UncheckedExtrinsic where LookupSource: Member + MaybeDisplay, - Call: Encode + Member, + Call: Encode + Member + Dispatchable, Signature: Member + traits::Verify, ::Signer: IdentifyAccount, - Extra: SignedExtension, + Extension: Encode + TransactionExtension, AccountId: Member + MaybeDisplay, Lookup: traits::Lookup, { - type Checked = CheckedExtrinsic; + type Checked = CheckedExtrinsic; fn check(self, lookup: &Lookup) -> Result { - Ok(match self.signature { - Some((signed, signature, extra)) => { + Ok(match self.preamble { + Preamble::Signed(signed, signature, _, tx_ext) => { let signed = lookup.lookup(signed)?; - let raw_payload = SignedPayload::new(self.function, extra)?; + // The `Implicit` is "implicitly" included in the payload. + let raw_payload = SignedPayload::new(self.function, tx_ext)?; if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) { return Err(InvalidTransaction::BadProof.into()) } - - let (function, extra, _) = raw_payload.deconstruct(); - CheckedExtrinsic { signed: Some((signed, extra)), function } + let (function, tx_ext, _) = raw_payload.deconstruct(); + CheckedExtrinsic { format: ExtrinsicFormat::Signed(signed, tx_ext), function } + }, + Preamble::General(_, tx_ext) => CheckedExtrinsic { + format: ExtrinsicFormat::General(tx_ext), + function: self.function, }, - None => CheckedExtrinsic { signed: None, function: self.function }, + Preamble::Bare(_) => + CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }, }) } @@ -190,91 +369,53 @@ where self, lookup: &Lookup, ) -> Result { - Ok(match self.signature { - Some((signed, _, extra)) => { + Ok(match self.preamble { + Preamble::Signed(signed, _, _, extra) => { let signed = lookup.lookup(signed)?; - let raw_payload = SignedPayload::new(self.function, extra)?; - let (function, extra, _) = raw_payload.deconstruct(); - CheckedExtrinsic { signed: Some((signed, extra)), function } + CheckedExtrinsic { + format: ExtrinsicFormat::Signed(signed, extra), + function: self.function, + } + }, + Preamble::General(_, extra) => CheckedExtrinsic { + format: ExtrinsicFormat::General(extra), + function: self.function, }, - None => CheckedExtrinsic { signed: None, function: self.function }, + Preamble::Bare(_) => + CheckedExtrinsic { format: ExtrinsicFormat::Bare, function: self.function }, }) } } -impl ExtrinsicMetadata - for UncheckedExtrinsic -where - Extra: SignedExtension, +impl> + ExtrinsicMetadata for UncheckedExtrinsic { - const VERSION: u8 = EXTRINSIC_FORMAT_VERSION; - type SignedExtensions = Extra; + // TODO: Expose both version 4 and version 5 in metadata v16. + const VERSION: u8 = LEGACY_EXTRINSIC_FORMAT_VERSION; + type TransactionExtensions = Extension; } -/// A payload that has been signed for an unchecked extrinsics. -/// -/// Note that the payload that we sign to produce unchecked extrinsic signature -/// is going to be different than the `SignaturePayload` - so the thing the extrinsic -/// actually contains. -pub struct SignedPayload((Call, Extra, Extra::AdditionalSigned)); - -impl SignedPayload -where - Call: Encode, - Extra: SignedExtension, +impl> + UncheckedExtrinsic { - /// Create new `SignedPayload`. - /// - /// This function may fail if `additional_signed` of `Extra` is not available. - pub fn new(call: Call, extra: Extra) -> Result { - let additional_signed = extra.additional_signed()?; - let raw_payload = (call, extra, additional_signed); - Ok(Self(raw_payload)) - } - - /// Create new `SignedPayload` from raw components. - pub fn from_raw(call: Call, extra: Extra, additional_signed: Extra::AdditionalSigned) -> Self { - Self((call, extra, additional_signed)) - } - - /// Deconstruct the payload into it's components. - pub fn deconstruct(self) -> (Call, Extra, Extra::AdditionalSigned) { - self.0 - } -} - -impl Encode for SignedPayload -where - Call: Encode, - Extra: SignedExtension, -{ - /// Get an encoded version of this payload. - /// - /// Payloads longer than 256 bytes are going to be `blake2_256`-hashed. - fn using_encoded R>(&self, f: F) -> R { - self.0.using_encoded(|payload| { - if payload.len() > 256 { - f(&blake2_256(payload)[..]) - } else { - f(payload) - } - }) + /// Returns the weight of the extension of this transaction, if present. If the transaction + /// doesn't use any extension, the weight returned is equal to zero. + pub fn extension_weight(&self) -> Weight { + match &self.preamble { + Preamble::Bare(_) => Weight::zero(), + Preamble::Signed(_, _, _, ext) | Preamble::General(_, ext) => + ext.weight(&self.function), + } } } -impl EncodeLike for SignedPayload -where - Call: Encode, - Extra: SignedExtension, -{ -} - -impl Decode for UncheckedExtrinsic +impl Decode + for UncheckedExtrinsic where Address: Decode, Signature: Decode, Call: Decode, - Extra: SignedExtension, + Extension: Decode, { fn decode(input: &mut I) -> Result { // This is a little more complicated than usual since the binary format must be compatible @@ -283,15 +424,7 @@ where let expected_length: Compact = Decode::decode(input)?; let before_length = input.remaining_len()?; - let version = input.read_byte()?; - - let is_signed = version & 0b1000_0000 != 0; - let version = version & 0b0111_1111; - if version != EXTRINSIC_FORMAT_VERSION { - return Err("Invalid transaction version".into()) - } - - let signature = is_signed.then(|| Decode::decode(input)).transpose()?; + let preamble = Decode::decode(input)?; let function = Decode::decode(input)?; if let Some((before_length, after_length)) = @@ -304,31 +437,20 @@ where } } - Ok(Self { signature, function }) + Ok(Self { preamble, function }) } } #[docify::export(unchecked_extrinsic_encode_impl)] -impl Encode for UncheckedExtrinsic +impl Encode + for UncheckedExtrinsic where - Address: Encode, - Signature: Encode, + Preamble: Encode, Call: Encode, - Extra: SignedExtension, + Extension: Encode, { fn encode(&self) -> Vec { - let mut tmp = Vec::with_capacity(core::mem::size_of::()); - - // 1 byte version id. - match self.signature.as_ref() { - Some(s) => { - tmp.push(EXTRINSIC_FORMAT_VERSION | 0b1000_0000); - s.encode_to(&mut tmp); - }, - None => { - tmp.push(EXTRINSIC_FORMAT_VERSION & 0b0111_1111); - }, - } + let mut tmp = self.preamble.encode(); self.function.encode_to(&mut tmp); let compact_len = codec::Compact::(tmp.len() as u32); @@ -343,19 +465,19 @@ where } } -impl EncodeLike - for UncheckedExtrinsic +impl EncodeLike + for UncheckedExtrinsic where Address: Encode, Signature: Encode, - Call: Encode, - Extra: SignedExtension, + Call: Encode + Dispatchable, + Extension: TransactionExtension, { } #[cfg(feature = "serde")] -impl serde::Serialize - for UncheckedExtrinsic +impl serde::Serialize + for UncheckedExtrinsic { fn serialize(&self, seq: S) -> Result where @@ -366,45 +488,86 @@ impl s } #[cfg(feature = "serde")] -impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extra: SignedExtension> - serde::Deserialize<'a> for UncheckedExtrinsic +impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extension: Decode> serde::Deserialize<'a> + for UncheckedExtrinsic { fn deserialize(de: D) -> Result where D: serde::Deserializer<'a>, { let r = sp_core::bytes::deserialize(de)?; - Decode::decode(&mut &r[..]) + Self::decode(&mut &r[..]) .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e))) } } -impl fmt::Debug - for UncheckedExtrinsic +/// A payload that has been signed for an unchecked extrinsics. +/// +/// Note that the payload that we sign to produce unchecked extrinsic signature +/// is going to be different than the `SignaturePayload` - so the thing the extrinsic +/// actually contains. +pub struct SignedPayload>( + (Call, Extension, Extension::Implicit), +); + +impl SignedPayload where - Address: fmt::Debug, - Call: fmt::Debug, - Extra: SignedExtension, + Call: Encode + Dispatchable, + Extension: TransactionExtension, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "UncheckedExtrinsic({:?}, {:?})", - self.signature.as_ref().map(|x| (&x.0, &x.2)), - self.function, - ) + /// Create new `SignedPayload` for extrinsic format version 4. + /// + /// This function may fail if `implicit` of `Extension` is not available. + pub fn new(call: Call, tx_ext: Extension) -> Result { + let implicit = Extension::implicit(&tx_ext)?; + let raw_payload = (call, tx_ext, implicit); + Ok(Self(raw_payload)) + } + + /// Create new `SignedPayload` from raw components. + pub fn from_raw(call: Call, tx_ext: Extension, implicit: Extension::Implicit) -> Self { + Self((call, tx_ext, implicit)) + } + + /// Deconstruct the payload into it's components. + pub fn deconstruct(self) -> (Call, Extension, Extension::Implicit) { + self.0 + } +} + +impl Encode for SignedPayload +where + Call: Encode + Dispatchable, + Extension: TransactionExtension, +{ + /// Get an encoded version of this `blake2_256`-hashed payload. + fn using_encoded R>(&self, f: F) -> R { + self.0.using_encoded(|payload| { + if payload.len() > 256 { + f(&blake2_256(payload)[..]) + } else { + f(payload) + } + }) } } -impl From> - for OpaqueExtrinsic +impl EncodeLike for SignedPayload +where + Call: Encode + Dispatchable, + Extension: TransactionExtension, +{ +} + +impl + From> for OpaqueExtrinsic where Address: Encode, Signature: Encode, Call: Encode, - Extra: SignedExtension, + Extension: Encode, { - fn from(extrinsic: UncheckedExtrinsic) -> Self { + fn from(extrinsic: UncheckedExtrinsic) -> Self { Self::from_bytes(extrinsic.encode().as_slice()).expect( "both OpaqueExtrinsic and UncheckedExtrinsic have encoding that is compatible with \ raw Vec encoding; qed", @@ -412,60 +575,196 @@ where } } +#[cfg(test)] +mod legacy { + use codec::{Compact, Decode, Encode, EncodeLike, Error, Input}; + use scale_info::{ + build::Fields, meta_type, Path, StaticTypeInfo, Type, TypeInfo, TypeParameter, + }; + + pub type UncheckedSignaturePayloadV4 = (Address, Signature, Extra); + + #[derive(PartialEq, Eq, Clone, Debug)] + pub struct UncheckedExtrinsicV4 { + pub signature: Option>, + pub function: Call, + } + + impl TypeInfo + for UncheckedExtrinsicV4 + where + Address: StaticTypeInfo, + Call: StaticTypeInfo, + Signature: StaticTypeInfo, + Extra: StaticTypeInfo, + { + type Identity = UncheckedExtrinsicV4; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("UncheckedExtrinsic", module_path!())) + // Include the type parameter types, even though they are not used directly in any + // of the described fields. These type definitions can be used by downstream + // consumers to help construct the custom decoding from the opaque bytes (see + // below). + .type_params(vec![ + TypeParameter::new("Address", Some(meta_type::
())), + TypeParameter::new("Call", Some(meta_type::())), + TypeParameter::new("Signature", Some(meta_type::())), + TypeParameter::new("Extra", Some(meta_type::())), + ]) + .docs(&["OldUncheckedExtrinsic raw bytes, requires custom decoding routine"]) + // Because of the custom encoding, we can only accurately describe the encoding as + // an opaque `Vec`. Downstream consumers will need to manually implement the + // codec to encode/decode the `signature` and `function` fields. + .composite(Fields::unnamed().field(|f| f.ty::>())) + } + } + + impl UncheckedExtrinsicV4 { + pub fn new_signed( + function: Call, + signed: Address, + signature: Signature, + extra: Extra, + ) -> Self { + Self { signature: Some((signed, signature, extra)), function } + } + + pub fn new_unsigned(function: Call) -> Self { + Self { signature: None, function } + } + } + + impl Decode + for UncheckedExtrinsicV4 + where + Address: Decode, + Signature: Decode, + Call: Decode, + Extra: Decode, + { + fn decode(input: &mut I) -> Result { + // This is a little more complicated than usual since the binary format must be + // compatible with SCALE's generic `Vec` type. Basically this just means accepting + // that there will be a prefix of vector length. + let expected_length: Compact = Decode::decode(input)?; + let before_length = input.remaining_len()?; + + let version = input.read_byte()?; + + let is_signed = version & 0b1000_0000 != 0; + let version = version & 0b0111_1111; + if version != 4u8 { + return Err("Invalid transaction version".into()) + } + + let signature = is_signed.then(|| Decode::decode(input)).transpose()?; + let function = Decode::decode(input)?; + + if let Some((before_length, after_length)) = + input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a))) + { + let length = before_length.saturating_sub(after_length); + + if length != expected_length.0 as usize { + return Err("Invalid length prefix".into()) + } + } + + Ok(Self { signature, function }) + } + } + + #[docify::export(unchecked_extrinsic_encode_impl)] + impl Encode + for UncheckedExtrinsicV4 + where + Address: Encode, + Signature: Encode, + Call: Encode, + Extra: Encode, + { + fn encode(&self) -> Vec { + let mut tmp = Vec::with_capacity(sp_std::mem::size_of::()); + + // 1 byte version id. + match self.signature.as_ref() { + Some(s) => { + tmp.push(4u8 | 0b1000_0000); + s.encode_to(&mut tmp); + }, + None => { + tmp.push(4u8 & 0b0111_1111); + }, + } + self.function.encode_to(&mut tmp); + + let compact_len = codec::Compact::(tmp.len() as u32); + + // Allocate the output buffer with the correct length + let mut output = Vec::with_capacity(compact_len.size_hint() + tmp.len()); + + compact_len.encode_to(&mut output); + output.extend(tmp); + + output + } + } + + impl EncodeLike + for UncheckedExtrinsicV4 + where + Address: Encode, + Signature: Encode, + Call: Encode, + Extra: Encode, + { + } +} + #[cfg(test)] mod tests { - use super::*; + use super::{legacy::UncheckedExtrinsicV4, *}; use crate::{ codec::{Decode, Encode}, + impl_tx_ext_default, testing::TestSignature as TestSig, - traits::{DispatchInfoOf, IdentityLookup, SignedExtension}, + traits::{FakeDispatchable, IdentityLookup, TransactionExtension}, }; use sp_io::hashing::blake2_256; type TestContext = IdentityLookup; type TestAccountId = u64; - type TestCall = Vec; + type TestCall = FakeDispatchable>; const TEST_ACCOUNT: TestAccountId = 0; // NOTE: this is demonstration. One can simply use `()` for testing. #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd, TypeInfo)] - struct TestExtra; - impl SignedExtension for TestExtra { - const IDENTIFIER: &'static str = "TestExtra"; - type AccountId = u64; - type Call = (); - type AdditionalSigned = (); + struct DummyExtension; + impl TransactionExtension for DummyExtension { + const IDENTIFIER: &'static str = "DummyExtension"; + type Implicit = (); + type Val = (); type Pre = (); - - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) - } + impl_tx_ext_default!(TestCall; weight validate prepare); } - type Ex = UncheckedExtrinsic; - type CEx = CheckedExtrinsic; + type Ex = UncheckedExtrinsic; + type CEx = CheckedExtrinsic; #[test] fn unsigned_codec_should_work() { - let ux = Ex::new_unsigned(vec![0u8; 0]); + let call: TestCall = vec![0u8; 0].into(); + let ux = Ex::new_bare(call); let encoded = ux.encode(); assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux)); } #[test] fn invalid_length_prefix_is_detected() { - let ux = Ex::new_unsigned(vec![0u8; 0]); + let ux = Ex::new_bare(vec![0u8; 0].into()); let mut encoded = ux.encode(); let length = Compact::::decode(&mut &encoded[..]).unwrap(); @@ -474,13 +773,20 @@ mod tests { assert_eq!(Ex::decode(&mut &encoded[..]), Err("Invalid length prefix".into())); } + #[test] + fn transaction_codec_should_work() { + let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension); + let encoded = ux.encode(); + assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux)); + } + #[test] fn signed_codec_should_work() { let ux = Ex::new_signed( - vec![0u8; 0], + vec![0u8; 0].into(), TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()), - TestExtra, + TestSig(TEST_ACCOUNT, (vec![0u8; 0], DummyExtension).encode()), + DummyExtension, ); let encoded = ux.encode(); assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux)); @@ -489,13 +795,13 @@ mod tests { #[test] fn large_signed_codec_should_work() { let ux = Ex::new_signed( - vec![0u8; 0], + vec![0u8; 0].into(), TEST_ACCOUNT, TestSig( TEST_ACCOUNT, - (vec![0u8; 257], TestExtra).using_encoded(blake2_256)[..].to_owned(), + (vec![0u8; 257], DummyExtension).using_encoded(blake2_256)[..].to_owned(), ), - TestExtra, + DummyExtension, ); let encoded = ux.encode(); assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux)); @@ -503,44 +809,68 @@ mod tests { #[test] fn unsigned_check_should_work() { - let ux = Ex::new_unsigned(vec![0u8; 0]); - assert!(!ux.is_signed().unwrap_or(false)); - assert!(>::check(ux, &Default::default()).is_ok()); + let ux = Ex::new_bare(vec![0u8; 0].into()); + assert!(ux.is_inherent()); + assert_eq!( + >::check(ux, &Default::default()), + Ok(CEx { format: ExtrinsicFormat::Bare, function: vec![0u8; 0].into() }), + ); } #[test] fn badly_signed_check_should_fail() { let ux = Ex::new_signed( - vec![0u8; 0], + vec![0u8; 0].into(), TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, vec![0u8; 0]), - TestExtra, + TestSig(TEST_ACCOUNT, vec![0u8; 0].into()), + DummyExtension, ); - assert!(ux.is_signed().unwrap_or(false)); + assert!(!ux.is_inherent()); assert_eq!( >::check(ux, &Default::default()), Err(InvalidTransaction::BadProof.into()), ); } + #[test] + fn transaction_check_should_work() { + let ux = Ex::new_transaction(vec![0u8; 0].into(), DummyExtension); + assert!(!ux.is_inherent()); + assert_eq!( + >::check(ux, &Default::default()), + Ok(CEx { + format: ExtrinsicFormat::General(DummyExtension), + function: vec![0u8; 0].into() + }), + ); + } + #[test] fn signed_check_should_work() { + let sig_payload = SignedPayload::from_raw( + FakeDispatchable::from(vec![0u8; 0]), + DummyExtension, + DummyExtension.implicit().unwrap(), + ); let ux = Ex::new_signed( - vec![0u8; 0], + vec![0u8; 0].into(), TEST_ACCOUNT, - TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()), - TestExtra, + TestSig(TEST_ACCOUNT, sig_payload.encode()), + DummyExtension, ); - assert!(ux.is_signed().unwrap_or(false)); + assert!(!ux.is_inherent()); assert_eq!( >::check(ux, &Default::default()), - Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }), + Ok(CEx { + format: ExtrinsicFormat::Signed(TEST_ACCOUNT, DummyExtension), + function: vec![0u8; 0].into() + }), ); } #[test] fn encoding_matches_vec() { - let ex = Ex::new_unsigned(vec![0u8; 0]); + let ex = Ex::new_bare(vec![0u8; 0].into()); let encoded = ex.encode(); let decoded = Ex::decode(&mut encoded.as_slice()).unwrap(); assert_eq!(decoded, ex); @@ -550,7 +880,7 @@ mod tests { #[test] fn conversion_to_opaque() { - let ux = Ex::new_unsigned(vec![0u8; 0]); + let ux = Ex::new_bare(vec![0u8; 0].into()); let encoded = ux.encode(); let opaque: OpaqueExtrinsic = ux.into(); let opaque_encoded = opaque.encode(); @@ -559,10 +889,106 @@ mod tests { #[test] fn large_bad_prefix_should_work() { - let encoded = Compact::::from(u32::MAX).encode(); + let encoded = (Compact::::from(u32::MAX), Preamble::<(), (), ()>::Bare(0)).encode(); + assert!(Ex::decode(&mut &encoded[..]).is_err()); + } + + #[test] + fn legacy_short_signed_encode_decode() { + let call: TestCall = vec![0u8; 4].into(); + let signed = TEST_ACCOUNT; + let extension = DummyExtension; + let implicit = extension.implicit().unwrap(); + let legacy_signature = TestSig(TEST_ACCOUNT, (&call, &extension, &implicit).encode()); + + let old_ux = + UncheckedExtrinsicV4::::new_signed( + call.clone(), + signed, + legacy_signature.clone(), + extension.clone(), + ); + + let encoded_old_ux = old_ux.encode(); + let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap(); + + assert_eq!(decoded_old_ux.function, call); assert_eq!( - Ex::decode(&mut &encoded[..]), - Err(Error::from("Not enough data to fill buffer")) + decoded_old_ux.preamble, + Preamble::Signed(signed, legacy_signature.clone(), 0, extension.clone()) ); + + let new_ux = + Ex::new_signed(call.clone(), signed, legacy_signature.clone(), extension.clone()); + + let new_checked = new_ux.check(&IdentityLookup::::default()).unwrap(); + let old_checked = + decoded_old_ux.check(&IdentityLookup::::default()).unwrap(); + assert_eq!(new_checked, old_checked); + } + + #[test] + fn legacy_long_signed_encode_decode() { + let call: TestCall = vec![0u8; 257].into(); + let signed = TEST_ACCOUNT; + let extension = DummyExtension; + let implicit = extension.implicit().unwrap(); + let signature = TestSig( + TEST_ACCOUNT, + blake2_256(&(&call, DummyExtension, &implicit).encode()[..]).to_vec(), + ); + + let old_ux = + UncheckedExtrinsicV4::::new_signed( + call.clone(), + signed, + signature.clone(), + extension.clone(), + ); + + let encoded_old_ux = old_ux.encode(); + let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap(); + + assert_eq!(decoded_old_ux.function, call); + assert_eq!( + decoded_old_ux.preamble, + Preamble::Signed(signed, signature.clone(), 0, extension.clone()) + ); + + let new_ux = Ex::new_signed(call.clone(), signed, signature.clone(), extension.clone()); + + let new_checked = new_ux.check(&IdentityLookup::::default()).unwrap(); + let old_checked = + decoded_old_ux.check(&IdentityLookup::::default()).unwrap(); + assert_eq!(new_checked, old_checked); + } + + #[test] + fn legacy_unsigned_encode_decode() { + let call: TestCall = vec![0u8; 0].into(); + + let old_ux = + UncheckedExtrinsicV4::::new_unsigned( + call.clone(), + ); + + let encoded_old_ux = old_ux.encode(); + let decoded_old_ux = Ex::decode(&mut &encoded_old_ux[..]).unwrap(); + + assert_eq!(decoded_old_ux.function, call); + assert_eq!(decoded_old_ux.preamble, Preamble::Bare(LEGACY_EXTRINSIC_FORMAT_VERSION)); + + let new_legacy_ux = Ex::new_bare_legacy(call.clone()); + assert_eq!(encoded_old_ux, new_legacy_ux.encode()); + + let new_ux = Ex::new_bare(call.clone()); + let encoded_new_ux = new_ux.encode(); + let decoded_new_ux = Ex::decode(&mut &encoded_new_ux[..]).unwrap(); + assert_eq!(new_ux, decoded_new_ux); + + let new_checked = new_ux.check(&IdentityLookup::::default()).unwrap(); + let old_checked = + decoded_old_ux.check(&IdentityLookup::::default()).unwrap(); + assert_eq!(new_checked, old_checked); } } diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index 260c9a91855..6eed57656a6 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -26,7 +26,7 @@ //! communication between the client and the runtime. This includes: //! //! - A set of traits to declare what any block/header/extrinsic type should provide. -//! - [`traits::Block`], [`traits::Header`], [`traits::Extrinsic`] +//! - [`traits::Block`], [`traits::Header`], [`traits::ExtrinsicLike`] //! - A set of types that implement these traits, whilst still providing a high degree of //! configurability via generics. //! - [`generic::Block`], [`generic::Header`], [`generic::UncheckedExtrinsic`] and @@ -131,6 +131,8 @@ pub use sp_arithmetic::{ FixedPointOperand, FixedU128, FixedU64, InnerOf, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, Rational128, Rounding, UpperOf, }; +/// Re-export this since it's part of the API of this crate. +pub use sp_weights::Weight; pub use either::Either; @@ -955,9 +957,10 @@ impl<'a> ::serde::Deserialize<'a> for OpaqueExtrinsic { } } -impl traits::Extrinsic for OpaqueExtrinsic { - type Call = (); - type SignaturePayload = (); +impl traits::ExtrinsicLike for OpaqueExtrinsic { + fn is_bare(&self) -> bool { + false + } } /// Print something that implements `Printable` from the runtime. diff --git a/substrate/primitives/runtime/src/testing.rs b/substrate/primitives/runtime/src/testing.rs index a4ce4b5fc1a..1fc78cce670 100644 --- a/substrate/primitives/runtime/src/testing.rs +++ b/substrate/primitives/runtime/src/testing.rs @@ -19,23 +19,15 @@ use crate::{ codec::{Codec, Decode, Encode, MaxEncodedLen}, - generic, + generic::{self, UncheckedExtrinsic}, scale_info::TypeInfo, - traits::{ - self, Applyable, BlakeTwo256, Checkable, DispatchInfoOf, Dispatchable, OpaqueKeys, - PostDispatchInfoOf, SignaturePayload, SignedExtension, ValidateUnsigned, - }, - transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, - ApplyExtrinsicResultWithInfo, KeyTypeId, + traits::{self, BlakeTwo256, Dispatchable, OpaqueKeys}, + DispatchResultWithInfo, KeyTypeId, }; -use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize}; use sp_core::crypto::{key_types, ByteArray, CryptoType, Dummy}; pub use sp_core::{sr25519, H256}; -use std::{ - cell::RefCell, - fmt::{self, Debug}, - ops::Deref, -}; +use std::{cell::RefCell, fmt::Debug}; /// A dummy type which can be used instead of regular cryptographic primitives. /// @@ -80,6 +72,11 @@ impl UintAuthorityId { bytes[0..8].copy_from_slice(&self.0.to_le_bytes()); T::from_slice(&bytes).unwrap() } + + /// Set the list of keys returned by the runtime call for all keys of that type. + pub fn set_all_keys>(keys: impl IntoIterator) { + ALL_KEYS.with(|l| *l.borrow_mut() = keys.into_iter().map(Into::into).collect()) + } } impl CryptoType for UintAuthorityId { @@ -104,13 +101,6 @@ thread_local! { static ALL_KEYS: RefCell> = RefCell::new(vec![]); } -impl UintAuthorityId { - /// Set the list of keys returned by the runtime call for all keys of that type. - pub fn set_all_keys>(keys: impl IntoIterator) { - ALL_KEYS.with(|l| *l.borrow_mut() = keys.into_iter().map(Into::into).collect()) - } -} - impl sp_application_crypto::RuntimeAppPublic for UintAuthorityId { const ID: KeyTypeId = key_types::DUMMY; @@ -162,6 +152,18 @@ impl traits::IdentifyAccount for UintAuthorityId { } } +impl traits::Verify for UintAuthorityId { + type Signer = Self; + + fn verify>( + &self, + _msg: L, + signer: &::AccountId, + ) -> bool { + self.0 == *signer + } +} + /// A dummy signature type, to match `UintAuthorityId`. #[derive(Eq, PartialEq, Clone, Debug, Hash, Serialize, Deserialize, Encode, Decode, TypeInfo)] pub struct TestSignature(pub u64, pub Vec); @@ -196,42 +198,6 @@ impl Header { } } -/// An opaque extrinsic wrapper type. -#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)] -pub struct ExtrinsicWrapper(Xt); - -impl traits::Extrinsic for ExtrinsicWrapper { - type Call = (); - type SignaturePayload = (); - - fn is_signed(&self) -> Option { - None - } -} - -impl serde::Serialize for ExtrinsicWrapper { - fn serialize(&self, seq: S) -> Result - where - S: ::serde::Serializer, - { - self.using_encoded(|bytes| seq.serialize_bytes(bytes)) - } -} - -impl From for ExtrinsicWrapper { - fn from(xt: Xt) -> Self { - ExtrinsicWrapper(xt) - } -} - -impl Deref for ExtrinsicWrapper { - type Target = Xt; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - /// Testing block #[derive(PartialEq, Eq, Clone, Serialize, Debug, Encode, Decode, TypeInfo)] pub struct Block { @@ -246,7 +212,16 @@ impl traits::HeaderProvider for Block { } impl< - Xt: 'static + Codec + Sized + Send + Sync + Serialize + Clone + Eq + Debug + traits::Extrinsic, + Xt: 'static + + Codec + + Sized + + Send + + Sync + + Serialize + + Clone + + Eq + + Debug + + traits::ExtrinsicLike, > traits::Block for Block { type Extrinsic = Xt; @@ -281,139 +256,25 @@ where } } -/// The signature payload of a `TestXt`. -type TxSignaturePayload = (u64, Extra); - -impl SignaturePayload for TxSignaturePayload { - type SignatureAddress = u64; - type Signature = (); - type SignatureExtra = Extra; -} - -/// Test transaction, tuple of (sender, call, signed_extra) -/// with index only used if sender is some. -/// -/// If sender is some then the transaction is signed otherwise it is unsigned. -#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)] -pub struct TestXt { - /// Signature of the extrinsic. - pub signature: Option>, - /// Call of the extrinsic. - pub call: Call, -} - -impl TestXt { - /// Create a new `TextXt`. - pub fn new(call: Call, signature: Option<(u64, Extra)>) -> Self { - Self { call, signature } - } -} - -impl Serialize for TestXt -where - TestXt: Encode, -{ - fn serialize(&self, seq: S) -> Result - where - S: Serializer, - { - self.using_encoded(|bytes| seq.serialize_bytes(bytes)) - } -} - -impl Debug for TestXt { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TestXt({:?}, ...)", self.signature.as_ref().map(|x| &x.0)) - } -} - -impl Checkable for TestXt { - type Checked = Self; - fn check(self, _: &Context) -> Result { - Ok(self) - } - - #[cfg(feature = "try-runtime")] - fn unchecked_into_checked_i_know_what_i_am_doing( - self, - _: &Context, - ) -> Result { - unreachable!() - } -} - -impl traits::Extrinsic - for TestXt -{ - type Call = Call; - type SignaturePayload = TxSignaturePayload; +/// Extrinsic type with `u64` accounts and mocked signatures, used in testing. +pub type TestXt = UncheckedExtrinsic; - fn is_signed(&self) -> Option { - Some(self.signature.is_some()) - } +/// Wrapper over a `u64` that can be used as a `RuntimeCall`. +#[derive(PartialEq, Eq, Debug, Clone, Encode, Decode, TypeInfo)] +pub struct MockCallU64(pub u64); - fn new(c: Call, sig: Option) -> Option { - Some(TestXt { signature: sig, call: c }) +impl Dispatchable for MockCallU64 { + type RuntimeOrigin = u64; + type Config = (); + type Info = (); + type PostInfo = (); + fn dispatch(self, _origin: Self::RuntimeOrigin) -> DispatchResultWithInfo { + Ok(()) } } -impl traits::ExtrinsicMetadata for TestXt -where - Call: Codec + Sync + Send, - Extra: SignedExtension, -{ - type SignedExtensions = Extra; - const VERSION: u8 = 0u8; -} - -impl Applyable for TestXt -where - Call: 'static - + Sized - + Send - + Sync - + Clone - + Eq - + Codec - + Debug - + Dispatchable, - Extra: SignedExtension, - Origin: From>, -{ - type Call = Call; - - /// Checks to see if this is a valid *transaction*. It returns information on it if so. - fn validate>( - &self, - source: TransactionSource, - info: &DispatchInfoOf, - len: usize, - ) -> TransactionValidity { - if let Some((ref id, ref extra)) = self.signature { - Extra::validate(extra, id, &self.call, info, len) - } else { - let valid = Extra::validate_unsigned(&self.call, info, len)?; - let unsigned_validation = U::validate_unsigned(source, &self.call)?; - Ok(valid.combine_with(unsigned_validation)) - } - } - - /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, - /// index and sender. - fn apply>( - self, - info: &DispatchInfoOf, - len: usize, - ) -> ApplyExtrinsicResultWithInfo> { - let maybe_who = if let Some((who, extra)) = self.signature { - Extra::pre_dispatch(extra, &who, &self.call, info, len)?; - Some(who) - } else { - Extra::pre_dispatch_unsigned(&self.call, info, len)?; - U::pre_dispatch(&self.call)?; - None - }; - - Ok(self.call.dispatch(maybe_who.into())) +impl From for MockCallU64 { + fn from(value: u64) -> Self { + Self(value) } } diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits/mod.rs similarity index 91% rename from substrate/primitives/runtime/src/traits.rs rename to substrate/primitives/runtime/src/traits/mod.rs index fc63bc76dec..e6906cdb387 100644 --- a/substrate/primitives/runtime/src/traits.rs +++ b/substrate/primitives/runtime/src/traits/mod.rs @@ -19,7 +19,7 @@ use crate::{ generic::Digest, - scale_info::{MetaType, StaticTypeInfo, TypeInfo}, + scale_info::{StaticTypeInfo, TypeInfo}, transaction_validity::{ TransactionSource, TransactionValidity, TransactionValidityError, UnknownTransaction, ValidTransaction, @@ -52,6 +52,11 @@ use std::fmt::Display; #[cfg(feature = "std")] use std::str::FromStr; +pub mod transaction_extension; +pub use transaction_extension::{ + DispatchTransaction, TransactionExtension, TransactionExtensionMetadata, ValidateResult, +}; + /// A lazy value. pub trait Lazy { /// Get a reference to the underlying value. @@ -226,8 +231,14 @@ pub trait StaticLookup { } /// A lookup implementation returning the input value. -#[derive(Default, Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct IdentityLookup(PhantomData); +impl Default for IdentityLookup { + fn default() -> Self { + Self(PhantomData::::default()) + } +} + impl StaticLookup for IdentityLookup { type Source = T; type Target = T; @@ -1253,7 +1264,7 @@ pub trait Header: // that is then used to define `UncheckedExtrinsic`. // ```ignore // pub type UncheckedExtrinsic = -// generic::UncheckedExtrinsic; +// generic::UncheckedExtrinsic; // ``` // This `UncheckedExtrinsic` is supplied to the `Block`. // ```ignore @@ -1286,7 +1297,7 @@ pub trait Block: + 'static { /// Type for extrinsics. - type Extrinsic: Member + Codec + Extrinsic + MaybeSerialize; + type Extrinsic: Member + Codec + ExtrinsicLike + MaybeSerialize; /// Header type. type Header: Header + MaybeSerializeDeserialize; /// Block hash type. @@ -1310,6 +1321,7 @@ pub trait Block: } /// Something that acts like an `Extrinsic`. +#[deprecated = "Use `ExtrinsicLike` along with the `CreateTransaction` trait family instead"] pub trait Extrinsic: Sized { /// The function call. type Call: TypeInfo; @@ -1327,17 +1339,49 @@ pub trait Extrinsic: Sized { None } - /// Create new instance of the extrinsic. - /// - /// Extrinsics can be split into: - /// 1. Inherents (no signature; created by validators during block production) - /// 2. Unsigned Transactions (no signature; represent "system calls" or other special kinds of - /// calls) 3. Signed Transactions (with signature; a regular transactions with known origin) + /// Returns `true` if this `Extrinsic` is bare. + fn is_bare(&self) -> bool { + !self.is_signed().unwrap_or(true) + } + + /// Create a new old-school extrinsic, either a bare extrinsic if `_signed_data` is `None` or + /// a signed transaction is it is `Some`. fn new(_call: Self::Call, _signed_data: Option) -> Option { None } } +/// Something that acts like an `Extrinsic`. +pub trait ExtrinsicLike: Sized { + /// Is this `Extrinsic` signed? + /// If no information are available about signed/unsigned, `None` should be returned. + #[deprecated = "Use and implement `!is_bare()` instead"] + fn is_signed(&self) -> Option { + None + } + + /// Returns `true` if this `Extrinsic` is bare. + fn is_bare(&self) -> bool { + #[allow(deprecated)] + !self.is_signed().unwrap_or(true) + } +} + +#[allow(deprecated)] +impl ExtrinsicLike for T +where + T: Extrinsic, +{ + fn is_signed(&self) -> Option { + #[allow(deprecated)] + ::is_signed(&self) + } + + fn is_bare(&self) -> bool { + ::is_bare(&self) + } +} + /// Something that acts like a [`SignaturePayload`](Extrinsic::SignaturePayload) of an /// [`Extrinsic`]. pub trait SignaturePayload { @@ -1370,8 +1414,8 @@ pub trait ExtrinsicMetadata { /// By format is meant the encoded representation of the `Extrinsic`. const VERSION: u8; - /// Signed extensions attached to this `Extrinsic`. - type SignedExtensions: SignedExtension; + /// Transaction extensions attached to this `Extrinsic`. + type TransactionExtensions; } /// Extract the hashing type for a block. @@ -1435,6 +1479,27 @@ impl Checkable for T { } } +/// A type that can handle weight refunds. +pub trait RefundWeight { + /// Refund some unspent weight. + fn refund(&mut self, weight: sp_weights::Weight); +} + +/// A type that can handle weight refunds and incorporate extension weights into the call weight +/// after dispatch. +pub trait ExtensionPostDispatchWeightHandler: RefundWeight { + /// Accrue some weight pertaining to the extension. + fn set_extension_weight(&mut self, info: &DispatchInfo); +} + +impl RefundWeight for () { + fn refund(&mut self, _weight: sp_weights::Weight) {} +} + +impl ExtensionPostDispatchWeightHandler<()> for () { + fn set_extension_weight(&mut self, _info: &()) {} +} + /// A lazy call (module function and argument values) that can be executed via its `dispatch` /// method. pub trait Dispatchable { @@ -1450,12 +1515,21 @@ pub trait Dispatchable { type Info; /// Additional information that is returned by `dispatch`. Can be used to supply the caller /// with information about a `Dispatchable` that is only known post dispatch. - type PostInfo: Eq + PartialEq + Clone + Copy + Encode + Decode + Printable; + type PostInfo: Eq + + PartialEq + + Clone + + Copy + + Encode + + Decode + + Printable + + ExtensionPostDispatchWeightHandler; /// Actually dispatch this call and return the result of it. fn dispatch(self, origin: Self::RuntimeOrigin) -> crate::DispatchResultWithInfo; } +/// Shortcut to reference the `RuntimeOrigin` type of a `Dispatchable`. +pub type DispatchOriginOf = ::RuntimeOrigin; /// Shortcut to reference the `Info` type of a `Dispatchable`. pub type DispatchInfoOf = ::Info; /// Shortcut to reference the `PostInfo` type of a `Dispatchable`. @@ -1474,8 +1548,75 @@ impl Dispatchable for () { } } +/// Dispatchable impl containing an arbitrary value which panics if it actually is dispatched. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct FakeDispatchable(pub Inner); +impl From for FakeDispatchable { + fn from(inner: Inner) -> Self { + Self(inner) + } +} +impl FakeDispatchable { + /// Take `self` and return the underlying inner value. + pub fn deconstruct(self) -> Inner { + self.0 + } +} +impl AsRef for FakeDispatchable { + fn as_ref(&self) -> &Inner { + &self.0 + } +} + +impl Dispatchable for FakeDispatchable { + type RuntimeOrigin = (); + type Config = (); + type Info = (); + type PostInfo = (); + fn dispatch( + self, + _origin: Self::RuntimeOrigin, + ) -> crate::DispatchResultWithInfo { + panic!("This implementation should not be used for actual dispatch."); + } +} + +/// Runtime Origin which includes a System Origin variant whose `AccountId` is the parameter. +pub trait AsSystemOriginSigner { + /// Extract a reference of the inner value of the System `Origin::Signed` variant, if self has + /// that variant. + fn as_system_origin_signer(&self) -> Option<&AccountId>; +} + +/// Interface to differentiate between Runtime Origins authorized to include a transaction into the +/// block and dispatch it, and those who aren't. +/// +/// This trait targets transactions, by which we mean extrinsics which are validated through a +/// [`TransactionExtension`]. This excludes bare extrinsics (i.e. inherents), which have their call, +/// not their origin, validated and authorized. +/// +/// Typically, upon validation or application of a transaction, the origin resulting from the +/// transaction extension (see [`TransactionExtension`]) is checked for authorization. The +/// transaction is then rejected or applied. +/// +/// In FRAME, an authorized origin is either an `Origin::Signed` System origin or a custom origin +/// authorized in a [`TransactionExtension`]. +pub trait AsTransactionAuthorizedOrigin { + /// Whether the origin is authorized to include a transaction in a block. + /// + /// In typical FRAME chains, this function returns `false` if the origin is a System + /// `Origin::None` variant, `true` otherwise, meaning only signed or custom origin resulting + /// from the transaction extension pipeline are authorized. + /// + /// NOTE: This function should not be used in the context of bare extrinsics (i.e. inherents), + /// as bare extrinsics do not authorize the origin but rather the call itself, and are not + /// validated through the [`TransactionExtension`] pipeline. + fn is_transaction_authorized(&self) -> bool; +} + /// Means by which a transaction may be extended. This type embodies both the data and the logic /// that should be additionally associated with the transaction. It should be plain old data. +#[deprecated = "Use `TransactionExtension` instead."] pub trait SignedExtension: Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo { @@ -1493,7 +1634,7 @@ pub trait SignedExtension: /// Any additional data that will go into the signed payload. This may be created dynamically /// from the transaction using the `additional_signed` function. - type AdditionalSigned: Encode + TypeInfo; + type AdditionalSigned: Codec + TypeInfo; /// The type that encodes information that can be passed from pre_dispatch to post-dispatch. type Pre; @@ -1532,38 +1673,6 @@ pub trait SignedExtension: len: usize, ) -> Result; - /// Validate an unsigned transaction for the transaction queue. - /// - /// This function can be called frequently by the transaction queue - /// to obtain transaction validity against current state. - /// It should perform all checks that determine a valid unsigned transaction, - /// and quickly eliminate ones that are stale or incorrect. - /// - /// Make sure to perform the same checks in `pre_dispatch_unsigned` function. - fn validate_unsigned( - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> TransactionValidity { - Ok(ValidTransaction::default()) - } - - /// Do any pre-flight stuff for a unsigned transaction. - /// - /// Note this function by default delegates to `validate_unsigned`, so that - /// all checks performed for the transaction queue are also performed during - /// the dispatch phase (applying the extrinsic). - /// - /// If you ever override this function, you need to make sure to always - /// perform the same validation as in `validate_unsigned`. - fn pre_dispatch_unsigned( - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result<(), TransactionValidityError> { - Self::validate_unsigned(call, info, len).map(|_| ()).map_err(Into::into) - } - /// Do any post-flight stuff for an extrinsic. /// /// If the transaction is signed, then `_pre` will contain the output of `pre_dispatch`, @@ -1594,125 +1703,46 @@ pub trait SignedExtension: /// /// As a [`SignedExtension`] can be a tuple of [`SignedExtension`]s we need to return a `Vec` /// that holds the metadata of each one. Each individual `SignedExtension` must return - /// *exactly* one [`SignedExtensionMetadata`]. + /// *exactly* one [`TransactionExtensionMetadata`]. /// /// This method provides a default implementation that returns a vec containing a single - /// [`SignedExtensionMetadata`]. - fn metadata() -> Vec { - alloc::vec![SignedExtensionMetadata { + /// [`TransactionExtensionMetadata`]. + fn metadata() -> Vec { + sp_std::vec![TransactionExtensionMetadata { identifier: Self::IDENTIFIER, ty: scale_info::meta_type::(), - additional_signed: scale_info::meta_type::() + implicit: scale_info::meta_type::() }] } -} - -/// Information about a [`SignedExtension`] for the runtime metadata. -pub struct SignedExtensionMetadata { - /// The unique identifier of the [`SignedExtension`]. - pub identifier: &'static str, - /// The type of the [`SignedExtension`]. - pub ty: MetaType, - /// The type of the [`SignedExtension`] additional signed data for the payload. - pub additional_signed: MetaType, -} - -#[impl_for_tuples(1, 12)] -impl SignedExtension for Tuple { - for_tuples!( where #( Tuple: SignedExtension )* ); - type AccountId = AccountId; - type Call = Call; - const IDENTIFIER: &'static str = "You should call `identifier()`!"; - for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); - for_tuples!( type Pre = ( #( Tuple::Pre ),* ); ); - - fn additional_signed(&self) -> Result { - Ok(for_tuples!( ( #( Tuple.additional_signed()? ),* ) )) - } - - fn validate( - &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> TransactionValidity { - let valid = ValidTransaction::default(); - for_tuples!( #( let valid = valid.combine_with(Tuple.validate(who, call, info, len)?); )* ); - Ok(valid) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - Ok(for_tuples!( ( #( Tuple.pre_dispatch(who, call, info, len)? ),* ) )) - } + /// Validate an unsigned transaction for the transaction queue. + /// + /// This function can be called frequently by the transaction queue + /// to obtain transaction validity against current state. + /// It should perform all checks that determine a valid unsigned transaction, + /// and quickly eliminate ones that are stale or incorrect. fn validate_unsigned( - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, ) -> TransactionValidity { - let valid = ValidTransaction::default(); - for_tuples!( #( let valid = valid.combine_with(Tuple::validate_unsigned(call, info, len)?); )* ); - Ok(valid) + Ok(ValidTransaction::default()) } + /// Do any pre-flight stuff for an unsigned transaction. + /// + /// Note this function by default delegates to `validate_unsigned`, so that + /// all checks performed for the transaction queue are also performed during + /// the dispatch phase (applying the extrinsic). + /// + /// If you ever override this function, you need not perform the same validation as in + /// `validate_unsigned`. fn pre_dispatch_unsigned( call: &Self::Call, info: &DispatchInfoOf, len: usize, ) -> Result<(), TransactionValidityError> { - for_tuples!( #( Tuple::pre_dispatch_unsigned(call, info, len)?; )* ); - Ok(()) - } - - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, - len: usize, - result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - match pre { - Some(x) => { - for_tuples!( #( Tuple::post_dispatch(Some(x.Tuple), info, post_info, len, result)?; )* ); - }, - None => { - for_tuples!( #( Tuple::post_dispatch(None, info, post_info, len, result)?; )* ); - }, - } - Ok(()) - } - - fn metadata() -> Vec { - let mut ids = Vec::new(); - for_tuples!( #( ids.extend(Tuple::metadata()); )* ); - ids - } -} - -impl SignedExtension for () { - type AccountId = u64; - type AdditionalSigned = (); - type Call = (); - type Pre = (); - const IDENTIFIER: &'static str = "UnitSignedExtension"; - fn additional_signed(&self) -> core::result::Result<(), TransactionValidityError> { - Ok(()) - } - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(|_| ()) + Self::validate_unsigned(call, info, len).map(|_| ()).map_err(Into::into) } } @@ -1722,11 +1752,23 @@ impl SignedExtension for () { /// /// Also provides information on to whom this information is attributable and an index that allows /// each piece of attributable information to be disambiguated. +/// +/// IMPORTANT: After validation, in both [validate](Applyable::validate) and +/// [apply](Applyable::apply), all transactions should have *some* authorized origin, except for +/// inherents. This is necessary in order to protect the chain against spam. If no extension in the +/// transaction extension pipeline authorized the transaction with an origin, either a system signed +/// origin or a custom origin, then the transaction must be rejected, as the extensions provided in +/// substrate which protect the chain, such as `CheckNonce`, `ChargeTransactionPayment` etc., rely +/// on the assumption that the system handles system signed transactions, and the pallets handle the +/// custom origin that they authorized. pub trait Applyable: Sized + Send + Sync { /// Type by which we can dispatch. Restricts the `UnsignedValidator` type. type Call: Dispatchable; /// Checks to see if this is a valid *transaction*. It returns information on it if so. + /// + /// IMPORTANT: Ensure that *some* origin has been authorized after validating the transaction. + /// If no origin was authorized, the transaction must be rejected. fn validate>( &self, source: TransactionSource, @@ -1736,6 +1778,9 @@ pub trait Applyable: Sized + Send + Sync { /// Executes all necessary logic needed prior to dispatch and deconstructs into function call, /// index and sender. + /// + /// IMPORTANT: Ensure that *some* origin has been authorized after validating the + /// transaction. If no origin was authorized, the transaction must be rejected. fn apply>( self, info: &DispatchInfoOf, diff --git a/substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs b/substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs new file mode 100644 index 00000000000..a5179748673 --- /dev/null +++ b/substrate/primitives/runtime/src/traits/transaction_extension/as_transaction_extension.rs @@ -0,0 +1,130 @@ +// 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. + +//! The [AsTransactionExtension] adapter struct for adapting [SignedExtension]s to +//! [TransactionExtension]s. + +#![allow(deprecated)] + +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; + +use crate::{ + traits::{AsSystemOriginSigner, SignedExtension, ValidateResult}, + transaction_validity::InvalidTransaction, +}; + +use super::*; + +/// Adapter to use a `SignedExtension` in the place of a `TransactionExtension`. +#[derive(TypeInfo, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] +#[deprecated = "Convert your SignedExtension to a TransactionExtension."] +pub struct AsTransactionExtension(pub SE); + +impl Default for AsTransactionExtension { + fn default() -> Self { + Self(SE::default()) + } +} + +impl From for AsTransactionExtension { + fn from(value: SE) -> Self { + Self(value) + } +} + +impl TransactionExtension for AsTransactionExtension +where + ::RuntimeOrigin: AsSystemOriginSigner + Clone, +{ + const IDENTIFIER: &'static str = SE::IDENTIFIER; + type Implicit = SE::AdditionalSigned; + + fn implicit(&self) -> Result { + self.0.additional_signed() + } + fn metadata() -> Vec { + SE::metadata() + } + fn weight(&self, _call: &SE::Call) -> Weight { + Weight::zero() + } + type Val = (); + type Pre = SE::Pre; + + fn validate( + &self, + origin: ::RuntimeOrigin, + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> ValidateResult { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + let r = self.0.validate(who, call, info, len)?; + Ok((r, (), origin)) + } + + fn prepare( + self, + _: (), + origin: &::RuntimeOrigin, + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result { + let who = origin.as_system_origin_signer().ok_or(InvalidTransaction::BadSigner)?; + self.0.pre_dispatch(who, call, info, len) + } + + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result { + SE::post_dispatch(Some(pre), info, post_info, len, result)?; + Ok(Weight::zero()) + } + + fn bare_validate( + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + ) -> TransactionValidity { + SE::validate_unsigned(call, info, len) + } + + fn bare_validate_and_prepare( + call: &SE::Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(), TransactionValidityError> { + SE::pre_dispatch_unsigned(call, info, len) + } + + fn bare_post_dispatch( + info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + SE::post_dispatch(None, info, post_info, len, result) + } +} diff --git a/substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs b/substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs new file mode 100644 index 00000000000..e2fb556bf9d --- /dev/null +++ b/substrate/primitives/runtime/src/traits/transaction_extension/dispatch_transaction.rs @@ -0,0 +1,154 @@ +// 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. + +//! The [DispatchTransaction] trait. + +use crate::{traits::AsTransactionAuthorizedOrigin, transaction_validity::InvalidTransaction}; + +use super::*; + +/// Single-function utility trait with a blanket impl over [`TransactionExtension`] in order to +/// provide transaction dispatching functionality. We avoid implementing this directly on the trait +/// since we never want it to be overriden by the trait implementation. +pub trait DispatchTransaction { + /// The origin type of the transaction. + type Origin; + /// The info type. + type Info; + /// The resultant type. + type Result; + /// The `Val` of the extension. + type Val; + /// The `Pre` of the extension. + type Pre; + /// Just validate a transaction. + /// + /// The is basically the same as [validate](TransactionExtension::validate), except that there + /// is no need to supply the bond data. + fn validate_only( + &self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + ) -> Result<(ValidTransaction, Self::Val, Self::Origin), TransactionValidityError>; + /// Validate and prepare a transaction, ready for dispatch. + fn validate_and_prepare( + self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + ) -> Result<(Self::Pre, Self::Origin), TransactionValidityError>; + /// Dispatch a transaction with the given base origin and call. + fn dispatch_transaction( + self, + origin: Self::Origin, + call: Call, + info: &Self::Info, + len: usize, + ) -> Self::Result; + /// Do everything which would be done in a [dispatch_transaction](Self::dispatch_transaction), + /// but instead of executing the call, execute `substitute` instead. Since this doesn't actually + /// dispatch the call, it doesn't need to consume it and so `call` can be passed as a reference. + fn test_run( + self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + substitute: impl FnOnce( + Self::Origin, + ) -> crate::DispatchResultWithInfo<::PostInfo>, + ) -> Self::Result; +} + +impl, Call: Dispatchable + Encode> DispatchTransaction for T +where + ::RuntimeOrigin: AsTransactionAuthorizedOrigin, +{ + type Origin = ::RuntimeOrigin; + type Info = DispatchInfoOf; + type Result = crate::ApplyExtrinsicResultWithInfo>; + type Val = T::Val; + type Pre = T::Pre; + + fn validate_only( + &self, + origin: Self::Origin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(ValidTransaction, T::Val, Self::Origin), TransactionValidityError> { + match self.validate(origin, call, info, len, self.implicit()?, call) { + // After validation, some origin must have been authorized. + Ok((_, _, origin)) if !origin.is_transaction_authorized() => + Err(InvalidTransaction::UnknownOrigin.into()), + res => res, + } + } + fn validate_and_prepare( + self, + origin: Self::Origin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(T::Pre, Self::Origin), TransactionValidityError> { + let (_, val, origin) = self.validate_only(origin, call, info, len)?; + let pre = self.prepare(val, &origin, &call, info, len)?; + Ok((pre, origin)) + } + fn dispatch_transaction( + self, + origin: ::RuntimeOrigin, + call: Call, + info: &DispatchInfoOf, + len: usize, + ) -> Self::Result { + let (pre, origin) = self.validate_and_prepare(origin, &call, info, len)?; + let mut res = call.dispatch(origin); + let pd_res = res.map(|_| ()).map_err(|e| e.error); + let post_info = match &mut res { + Ok(info) => info, + Err(err) => &mut err.post_info, + }; + post_info.set_extension_weight(info); + T::post_dispatch(pre, info, post_info, len, &pd_res)?; + Ok(res) + } + fn test_run( + self, + origin: Self::Origin, + call: &Call, + info: &Self::Info, + len: usize, + substitute: impl FnOnce( + Self::Origin, + ) -> crate::DispatchResultWithInfo<::PostInfo>, + ) -> Self::Result { + let (pre, origin) = self.validate_and_prepare(origin, &call, info, len)?; + let mut res = substitute(origin); + let pd_res = res.map(|_| ()).map_err(|e| e.error); + let post_info = match &mut res { + Ok(info) => info, + Err(err) => &mut err.post_info, + }; + post_info.set_extension_weight(info); + T::post_dispatch(pre, info, post_info, len, &pd_res)?; + Ok(res) + } +} diff --git a/substrate/primitives/runtime/src/traits/transaction_extension/mod.rs b/substrate/primitives/runtime/src/traits/transaction_extension/mod.rs new file mode 100644 index 00000000000..58cd0974661 --- /dev/null +++ b/substrate/primitives/runtime/src/traits/transaction_extension/mod.rs @@ -0,0 +1,635 @@ +// 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. + +//! The transaction extension trait. + +use crate::{ + scale_info::{MetaType, StaticTypeInfo}, + transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction}, + DispatchResult, +}; +use codec::{Codec, Decode, Encode}; +use impl_trait_for_tuples::impl_for_tuples; +#[doc(hidden)] +pub use sp_std::marker::PhantomData; +use sp_std::{self, fmt::Debug, prelude::*}; +use sp_weights::Weight; +use tuplex::{PopFront, PushBack}; + +use super::{ + DispatchInfoOf, DispatchOriginOf, Dispatchable, ExtensionPostDispatchWeightHandler, + PostDispatchInfoOf, RefundWeight, +}; + +mod as_transaction_extension; +mod dispatch_transaction; +#[allow(deprecated)] +pub use as_transaction_extension::AsTransactionExtension; +pub use dispatch_transaction::DispatchTransaction; + +/// Shortcut for the result value of the `validate` function. +pub type ValidateResult = + Result<(ValidTransaction, Val, DispatchOriginOf), TransactionValidityError>; + +/// Means by which a transaction may be extended. This type embodies both the data and the logic +/// that should be additionally associated with the transaction. It should be plain old data. +/// +/// The simplest transaction extension would be the Unit type (and empty pipeline) `()`. This +/// executes no additional logic and implies a dispatch of the transaction's call using the +/// inherited origin (either `None` or `Signed`, depending on whether this is a signed or general +/// transaction). +/// +/// Transaction extensions are capable of altering certain associated semantics: +/// +/// - They may define the origin with which the transaction's call should be dispatched. +/// - They may define various parameters used by the transaction queue to determine under what +/// conditions the transaction should be retained and introduced on-chain. +/// - They may define whether this transaction is acceptable for introduction on-chain at all. +/// +/// Each of these semantics are defined by the `validate` function. +/// +/// **NOTE: Transaction extensions cannot under any circumstances alter the call itself.** +/// +/// Transaction extensions are capable of defining logic which is executed additionally to the +/// dispatch of the call: +/// +/// - They may define logic which must be executed prior to the dispatch of the call. +/// - They may also define logic which must be executed after the dispatch of the call. +/// +/// Each of these semantics are defined by the `prepare` and `post_dispatch_details` functions +/// respectively. +/// +/// Finally, transaction extensions may define additional data to help define the implications of +/// the logic they introduce. This additional data may be explicitly defined by the transaction +/// author (in which case it is included as part of the transaction body), or it may be implicitly +/// defined by the transaction extension based around the on-chain state (which the transaction +/// author is assumed to know). This data may be utilized by the above logic to alter how a node's +/// transaction queue treats this transaction. +/// +/// ## Default implementations +/// +/// Of the 6 functions in this trait along with `TransactionExtension`, 2 of them must return a +/// value of an associated type on success, with only `implicit` having a default implementation. +/// This means that default implementations cannot be provided for `validate` and `prepare`. +/// However, a macro is provided [impl_tx_ext_default](crate::impl_tx_ext_default) which is capable +/// of generating default implementations for both of these functions. If you do not wish to +/// introduce additional logic into the transaction pipeline, then it is recommended that you use +/// this macro to implement these functions. Additionally, [weight](TransactionExtension::weight) +/// can return a default value, which would mean the extension is weightless, but it is not +/// implemented by default. Instead, implementers can explicitly choose to implement this default +/// behavior through the same [impl_tx_ext_default](crate::impl_tx_ext_default) macro. +/// +/// If your extension does any post-flight logic, then the functionality must be implemented in +/// [post_dispatch_details](TransactionExtension::post_dispatch_details). This function can return +/// the actual weight used by the extension during an entire dispatch cycle by wrapping said weight +/// value in a `Some`. This is useful in computing fee refunds, similar to how post dispatch +/// information is used to refund fees for calls. Alternatively, a `None` can be returned, which +/// means that the worst case scenario weight, namely the value returned by +/// [weight](TransactionExtension::weight), is the actual weight. This particular piece of logic +/// is embedded in the default implementation of +/// [post_dispatch](TransactionExtension::post_dispatch) so that the weight is assumed to be worst +/// case scenario, but implementers of this trait can correct it with extra effort. Therefore, all +/// users of an extension should use [post_dispatch](TransactionExtension::post_dispatch), with +/// [post_dispatch_details](TransactionExtension::post_dispatch_details) considered an internal +/// function. +/// +/// ## Pipelines, Inherited Implications, and Authorized Origins +/// +/// Requiring a single transaction extension to define all of the above semantics would be +/// cumbersome and would lead to a lot of boilerplate. Instead, transaction extensions are +/// aggregated into pipelines, which are tuples of transaction extensions. Each extension in the +/// pipeline is executed in order, and the output of each extension is aggregated and/or relayed as +/// the input to the next extension in the pipeline. +/// +/// This ordered composition happens with all data types ([Val](TransactionExtension::Val), +/// [Pre](TransactionExtension::Pre) and [Implicit](TransactionExtension::Implicit)) as well as +/// all functions. There are important consequences stemming from how the composition affects the +/// meaning of the `origin` and `implication` parameters as well as the results. Whereas the +/// [prepare](TransactionExtension::prepare) and +/// [post_dispatch](TransactionExtension::post_dispatch) functions are clear in their meaning, the +/// [validate](TransactionExtension::validate) function is fairly sophisticated and warrants further +/// explanation. +/// +/// Firstly, the `origin` parameter. The `origin` passed into the first item in a pipeline is simply +/// that passed into the tuple itself. It represents an authority who has authorized the implication +/// of the transaction, as of the extension it has been passed into *and any further extensions it +/// may pass though, all the way to, and including, the transaction's dispatch call itself. Each +/// following item in the pipeline is passed the origin which the previous item returned. The origin +/// returned from the final item in the pipeline is the origin which is returned by the tuple +/// itself. +/// +/// This means that if a constituent extension returns a different origin to the one it was called +/// with, then (assuming no other extension changes it further) *this new origin will be used for +/// all extensions following it in the pipeline, and will be returned from the pipeline to be used +/// as the origin for the call's dispatch*. The call itself as well as all these extensions +/// following may each imply consequence for this origin. We call this the *inherited implication*. +/// +/// The *inherited implication* is the cumulated on-chain effects born by whatever origin is +/// returned. It is expressed to the [validate](TransactionExtension::validate) function only as the +/// `implication` argument which implements the [Encode] trait. A transaction extension may define +/// its own implications through its own fields and the +/// [implicit](TransactionExtension::implicit) function. This is only utilized by extensions +/// which precede it in a pipeline or, if the transaction is an old-school signed transaction, the +/// underlying transaction verification logic. +/// +/// **The inherited implication passed as the `implication` parameter to +/// [validate](TransactionExtension::validate) does not include the extension's inner data itself +/// nor does it include the result of the extension's `implicit` function.** If you both provide an +/// implication and rely on the implication, then you need to manually aggregate your extensions +/// implication with the aggregated implication passed in. +/// +/// In the post dispatch pipeline, the actual weight of each extension is accrued in the +/// [PostDispatchInfo](PostDispatchInfoOf) of that transaction sequentially with each +/// [post_dispatch](TransactionExtension::post_dispatch) call. This means that an extension handling +/// transaction payment and refunds should be at the end of the pipeline in order to capture the +/// correct amount of weight used during the call. This is because one cannot know the actual weight +/// of an extension after post dispatch without running the post dispatch ahead of time. +pub trait TransactionExtension: + Codec + Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo +{ + /// Unique identifier of this signed extension. + /// + /// This will be exposed in the metadata to identify the signed extension used in an extrinsic. + const IDENTIFIER: &'static str; + + /// Any additional data which was known at the time of transaction construction and can be + /// useful in authenticating the transaction. This is determined dynamically in part from the + /// on-chain environment using the `implicit` function and not directly contained in the + /// transaction itself and therefore is considered "implicit". + type Implicit: Codec + StaticTypeInfo; + + /// Determine any additional data which was known at the time of transaction construction and + /// can be useful in authenticating the transaction. The expected usage of this is to include in + /// any data which is signed and verified as part of transaction validation. Also perform any + /// pre-signature-verification checks and return an error if needed. + fn implicit(&self) -> Result { + use crate::transaction_validity::InvalidTransaction::IndeterminateImplicit; + Ok(Self::Implicit::decode(&mut &[][..]).map_err(|_| IndeterminateImplicit)?) + } + + /// Returns the metadata for this extension. + /// + /// As a [`TransactionExtension`] can be a tuple of [`TransactionExtension`]s we need to return + /// a `Vec` that holds the metadata of each one. Each individual `TransactionExtension` must + /// return *exactly* one [`TransactionExtensionMetadata`]. + /// + /// This method provides a default implementation that returns a vec containing a single + /// [`TransactionExtensionMetadata`]. + fn metadata() -> Vec { + sp_std::vec![TransactionExtensionMetadata { + identifier: Self::IDENTIFIER, + ty: scale_info::meta_type::(), + implicit: scale_info::meta_type::() + }] + } + + /// The type that encodes information that can be passed from `validate` to `prepare`. + type Val; + + /// The type that encodes information that can be passed from `prepare` to `post_dispatch`. + type Pre; + + /// The weight consumed by executing this extension instance fully during transaction dispatch. + fn weight(&self, call: &Call) -> Weight; + + /// Validate a transaction for the transaction queue. + /// + /// This function can be called frequently by the transaction queue to obtain transaction + /// validity against current state. It should perform all checks that determine a valid + /// transaction, that can pay for its execution and quickly eliminate ones that are stale or + /// incorrect. + /// + /// Parameters: + /// - `origin`: The origin of the transaction which this extension inherited; coming from an + /// "old-school" *signed transaction*, this will be a system `RawOrigin::Signed` value. If the + /// transaction is a "new-school" *General Transaction*, then this will be a system + /// `RawOrigin::None` value. If this extension is an item in a composite, then it could be + /// anything which was previously returned as an `origin` value in the result of a `validate` + /// call. + /// - `call`: The `Call` wrapped by this extension. + /// - `info`: Information concerning, and inherent to, the transaction's call. + /// - `len`: The total length of the encoded transaction. + /// - `inherited_implication`: The *implication* which this extension inherits. This is a tuple + /// of the transaction's call and some additional opaque-but-encodable data. Coming directly + /// from a transaction, the latter is [()]. However, if this extension is expressed as part of + /// a composite type, then the latter component is equal to any further implications to which + /// the returned `origin` could potentially apply. See Pipelines, Inherited Implications, and + /// Authorized Origins for more information. + /// + /// Returns a [ValidateResult], which is a [Result] whose success type is a tuple of + /// [ValidTransaction] (defining useful metadata for the transaction queue), the [Self::Val] + /// token of this transaction, which gets passed into [prepare](TransactionExtension::prepare), + /// and the origin of the transaction, which gets passed into + /// [prepare](TransactionExtension::prepare) and is ultimately used for dispatch. + fn validate( + &self, + origin: DispatchOriginOf, + call: &Call, + info: &DispatchInfoOf, + len: usize, + self_implicit: Self::Implicit, + inherited_implication: &impl Encode, + ) -> ValidateResult; + + /// Do any pre-flight stuff for a transaction after validation. + /// + /// This is for actions which do not happen in the transaction queue but only immediately prior + /// to the point of dispatch on-chain. This should not return an error, since errors should + /// already have been identified during the [validate](TransactionExtension::validate) call. If + /// an error is returned, the transaction will be considered invalid but no state changes will + /// happen and therefore work done in [validate](TransactionExtension::validate) will not be + /// paid for. + /// + /// Unlike `validate`, this function may consume `self`. + /// + /// Parameters: + /// - `val`: `Self::Val` returned by the result of the `validate` call. + /// - `origin`: The origin returned by the result of the `validate` call. + /// - `call`: The `Call` wrapped by this extension. + /// - `info`: Information concerning, and inherent to, the transaction's call. + /// - `len`: The total length of the encoded transaction. + /// + /// Returns a [Self::Pre] value on success, which gets passed into + /// [post_dispatch](TransactionExtension::post_dispatch) and after the call is dispatched. + /// + /// IMPORTANT: **Checks made in validation need not be repeated here.** + fn prepare( + self, + val: Self::Val, + origin: &DispatchOriginOf, + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result; + + /// Do any post-flight stuff for an extrinsic. + /// + /// `_pre` contains the output of `prepare`. + /// + /// This gets given the `DispatchResult` `_result` from the extrinsic and can, if desired, + /// introduce a `TransactionValidityError`, causing the block to become invalid for including + /// it. + /// + /// On success, the caller must return the amount of unspent weight left over by this extension + /// after dispatch. By default, this function returns no unspent weight, which means the entire + /// weight computed for the worst case scenario is consumed. + /// + /// WARNING: This function does not automatically keep track of accumulated "actual" weight. + /// Unless this weight is handled at the call site, use + /// [post_dispatch](TransactionExtension::post_dispatch) + /// instead. + /// + /// Parameters: + /// - `pre`: `Self::Pre` returned by the result of the `prepare` call prior to dispatch. + /// - `info`: Information concerning, and inherent to, the transaction's call. + /// - `post_info`: Information concerning the dispatch of the transaction's call. + /// - `len`: The total length of the encoded transaction. + /// - `result`: The result of the dispatch. + /// + /// WARNING: It is dangerous to return an error here. To do so will fundamentally invalidate the + /// transaction and any block that it is included in, causing the block author to not be + /// compensated for their work in validating the transaction or producing the block so far. It + /// can only be used safely when you *know* that the transaction is one that would only be + /// introduced by the current block author. + fn post_dispatch_details( + _pre: Self::Pre, + _info: &DispatchInfoOf, + _post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result { + Ok(Weight::zero()) + } + + /// A wrapper for [`post_dispatch_details`](TransactionExtension::post_dispatch_details) that + /// refunds the unspent weight consumed by this extension into the post dispatch information. + /// + /// If `post_dispatch_details` returns a non-zero unspent weight, which, by definition, must be + /// less than the worst case weight provided by [weight](TransactionExtension::weight), that + /// is the value refunded in `post_info`. + /// + /// If no unspent weight is reported by `post_dispatch_details`, this function assumes the worst + /// case weight and does not refund anything. + /// + /// For more information, look into + /// [post_dispatch_details](TransactionExtension::post_dispatch_details). + fn post_dispatch( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + let unspent_weight = Self::post_dispatch_details(pre, info, &post_info, len, result)?; + post_info.refund(unspent_weight); + + Ok(()) + } + + /// Validation logic for bare extrinsics. + /// + /// NOTE: This function will be migrated to a separate `InherentExtension` interface. + fn bare_validate( + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + Ok(ValidTransaction::default()) + } + + /// All pre-flight logic run before dispatching bare extrinsics. + /// + /// NOTE: This function will be migrated to a separate `InherentExtension` interface. + fn bare_validate_and_prepare( + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } + + /// Post dispatch logic run after dispatching bare extrinsics. + /// + /// NOTE: This function will be migrated to a separate `InherentExtension` interface. + fn bare_post_dispatch( + _info: &DispatchInfoOf, + _post_info: &mut PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } +} + +/// Helper macro to be used in a `impl TransactionExtension` block to add default implementations of +/// `weight`, `validate`, `prepare` or any combinations of the them. +/// +/// The macro is to be used with 2 parameters, separated by ";": +/// - the `Call` type; +/// - the functions for which a default implementation should be generated, separated by " "; +/// available options are `weight`, `validate` and `prepare`. +/// +/// Example usage: +/// ```nocompile +/// impl TransactionExtension for EmptyExtension { +/// type Val = (); +/// type Pre = (); +/// +/// impl_tx_ext_default!(FirstCall; weight validate prepare); +/// } +/// +/// impl TransactionExtension for SimpleExtension { +/// type Val = u32; +/// type Pre = (); +/// +/// fn weight(&self, _: &SecondCall) -> Weight { +/// Weight::zero() +/// } +/// +/// fn validate( +/// &self, +/// _origin: ::RuntimeOrigin, +/// _call: &SecondCall, +/// _info: &DispatchInfoOf, +/// _len: usize, +/// _self_implicit: Self::Implicit, +/// _inherited_implication: &impl Encode, +/// ) -> ValidateResult { +/// Ok((Default::default(), 42u32, origin)) +/// } +/// +/// impl_tx_ext_default!(SecondCall; prepare); +/// } +/// ``` +#[macro_export] +macro_rules! impl_tx_ext_default { + ($call:ty ; , $( $rest:tt )*) => { + impl_tx_ext_default!{$call ; $( $rest )*} + }; + ($call:ty ; validate $( $rest:tt )*) => { + fn validate( + &self, + origin: $crate::traits::DispatchOriginOf<$call>, + _call: &$call, + _info: &$crate::traits::DispatchInfoOf<$call>, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl $crate::codec::Encode, + ) -> $crate::traits::ValidateResult { + Ok((Default::default(), Default::default(), origin)) + } + impl_tx_ext_default!{$call ; $( $rest )*} + }; + ($call:ty ; prepare $( $rest:tt )*) => { + fn prepare( + self, + _val: Self::Val, + _origin: &$crate::traits::DispatchOriginOf<$call>, + _call: &$call, + _info: &$crate::traits::DispatchInfoOf<$call>, + _len: usize, + ) -> Result { + Ok(Default::default()) + } + impl_tx_ext_default!{$call ; $( $rest )*} + }; + ($call:ty ; weight $( $rest:tt )*) => { + fn weight(&self, _call: &$call) -> $crate::Weight { + $crate::Weight::zero() + } + impl_tx_ext_default!{$call ; $( $rest )*} + }; + ($call:ty ;) => {}; +} + +/// Information about a [`TransactionExtension`] for the runtime metadata. +pub struct TransactionExtensionMetadata { + /// The unique identifier of the [`TransactionExtension`]. + pub identifier: &'static str, + /// The type of the [`TransactionExtension`]. + pub ty: MetaType, + /// The type of the [`TransactionExtension`] additional signed data for the payload. + pub implicit: MetaType, +} + +#[impl_for_tuples(1, 12)] +impl TransactionExtension for Tuple { + const IDENTIFIER: &'static str = "Use `metadata()`!"; + for_tuples!( type Implicit = ( #( Tuple::Implicit ),* ); ); + fn implicit(&self) -> Result { + Ok(for_tuples!( ( #( Tuple.implicit()? ),* ) )) + } + fn metadata() -> Vec { + let mut ids = Vec::new(); + for_tuples!( #( ids.extend(Tuple::metadata()); )* ); + ids + } + + for_tuples!( type Val = ( #( Tuple::Val ),* ); ); + for_tuples!( type Pre = ( #( Tuple::Pre ),* ); ); + + fn weight(&self, call: &Call) -> Weight { + let mut weight = Weight::zero(); + for_tuples!( #( weight = weight.saturating_add(Tuple.weight(call)); )* ); + weight + } + + fn validate( + &self, + origin: ::RuntimeOrigin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + self_implicit: Self::Implicit, + inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { + let valid = ValidTransaction::default(); + let val = (); + let following_explicit_implications = for_tuples!( ( #( &self.Tuple ),* ) ); + let following_implicit_implications = self_implicit; + + for_tuples!(#( + // Implication of this pipeline element not relevant for later items, so we pop it. + let (_item, following_explicit_implications) = following_explicit_implications.pop_front(); + let (item_implicit, following_implicit_implications) = following_implicit_implications.pop_front(); + let (item_valid, item_val, origin) = { + let implications = ( + // The first is the implications born of the fact we return the mutated + // origin. + inherited_implication, + // This is the explicitly made implication born of the fact the new origin is + // passed into the next items in this pipeline-tuple. + &following_explicit_implications, + // This is the implicitly made implication born of the fact the new origin is + // passed into the next items in this pipeline-tuple. + &following_implicit_implications, + ); + Tuple.validate(origin, call, info, len, item_implicit, &implications)? + }; + let valid = valid.combine_with(item_valid); + let val = val.push_back(item_val); + )* ); + Ok((valid, val, origin)) + } + + fn prepare( + self, + val: Self::Val, + origin: &::RuntimeOrigin, + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result { + Ok(for_tuples!( ( #( + Tuple::prepare(self.Tuple, val.Tuple, origin, call, info, len)? + ),* ) )) + } + + fn post_dispatch_details( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result { + let mut total_unspent_weight = Weight::zero(); + for_tuples!( #({ + let unspent_weight = Tuple::post_dispatch_details(pre.Tuple, info, post_info, len, result)?; + total_unspent_weight = total_unspent_weight.saturating_add(unspent_weight); + })* ); + Ok(total_unspent_weight) + } + + fn post_dispatch( + pre: Self::Pre, + info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + for_tuples!( #( Tuple::post_dispatch(pre.Tuple, info, post_info, len, result)?; )* ); + Ok(()) + } + + fn bare_validate(call: &Call, info: &DispatchInfoOf, len: usize) -> TransactionValidity { + let valid = ValidTransaction::default(); + for_tuples!(#( + let item_valid = Tuple::bare_validate(call, info, len)?; + let valid = valid.combine_with(item_valid); + )* ); + Ok(valid) + } + + fn bare_validate_and_prepare( + call: &Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result<(), TransactionValidityError> { + for_tuples!( #( Tuple::bare_validate_and_prepare(call, info, len)?; )* ); + Ok(()) + } + + fn bare_post_dispatch( + info: &DispatchInfoOf, + post_info: &mut PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + for_tuples!( #( Tuple::bare_post_dispatch(info, post_info, len, result)?; )* ); + Ok(()) + } +} + +impl TransactionExtension for () { + const IDENTIFIER: &'static str = "UnitTransactionExtension"; + type Implicit = (); + fn implicit(&self) -> sp_std::result::Result { + Ok(()) + } + type Val = (); + type Pre = (); + fn weight(&self, _call: &Call) -> Weight { + Weight::zero() + } + fn validate( + &self, + origin: ::RuntimeOrigin, + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, (), ::RuntimeOrigin), + TransactionValidityError, + > { + Ok((ValidTransaction::default(), (), origin)) + } + fn prepare( + self, + _val: (), + _origin: &::RuntimeOrigin, + _call: &Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result<(), TransactionValidityError> { + Ok(()) + } +} diff --git a/substrate/primitives/runtime/src/transaction_validity.rs b/substrate/primitives/runtime/src/transaction_validity.rs index 2d800e29b8b..a48c8ee7ba8 100644 --- a/substrate/primitives/runtime/src/transaction_validity.rs +++ b/substrate/primitives/runtime/src/transaction_validity.rs @@ -82,6 +82,10 @@ pub enum InvalidTransaction { MandatoryValidation, /// The sending address is disabled or known to be invalid. BadSigner, + /// The implicit data was unable to be calculated. + IndeterminateImplicit, + /// The transaction extension did not authorize any origin. + UnknownOrigin, } impl InvalidTransaction { @@ -113,6 +117,10 @@ impl From for &'static str { "Transaction dispatch is mandatory; transactions must not be validated.", InvalidTransaction::Custom(_) => "InvalidTransaction custom error", InvalidTransaction::BadSigner => "Invalid signing address", + InvalidTransaction::IndeterminateImplicit => + "The implicit data was unable to be calculated", + InvalidTransaction::UnknownOrigin => + "The transaction extension did not authorize any origin", } } } @@ -338,7 +346,7 @@ pub struct ValidTransactionBuilder { impl ValidTransactionBuilder { /// Set the priority of a transaction. /// - /// Note that the final priority for `FRAME` is combined from all `SignedExtension`s. + /// Note that the final priority for `FRAME` is combined from all `TransactionExtension`s. /// Most likely for unsigned transactions you want the priority to be higher /// than for regular transactions. We recommend exposing a base priority for unsigned /// transactions as a runtime module parameter, so that the runtime can tune inter-module diff --git a/substrate/primitives/storage/Cargo.toml b/substrate/primitives/storage/Cargo.toml index 9341d7ac77e..e441ddae52e 100644 --- a/substrate/primitives/storage/Cargo.toml +++ b/substrate/primitives/storage/Cargo.toml @@ -25,12 +25,7 @@ sp-debug-derive = { workspace = true } [features] default = ["std"] -std = [ - "codec/std", - "impl-serde/std", - "serde/std", - "sp-debug-derive/std", -] +std = ["codec/std", "impl-serde/std", "serde/std", "sp-debug-derive/std"] # Serde support without relying on std features. serde = ["dep:serde", "impl-serde"] diff --git a/substrate/primitives/test-primitives/src/lib.rs b/substrate/primitives/test-primitives/src/lib.rs index 1e3b912eaf4..adc96d77369 100644 --- a/substrate/primitives/test-primitives/src/lib.rs +++ b/substrate/primitives/test-primitives/src/lib.rs @@ -28,7 +28,7 @@ use sp_application_crypto::sr25519; use alloc::vec::Vec; pub use sp_core::{hash::H256, RuntimeDebug}; -use sp_runtime::traits::{BlakeTwo256, Extrinsic as ExtrinsicT, Verify}; +use sp_runtime::traits::{BlakeTwo256, ExtrinsicLike, Verify}; /// Extrinsic for test-runtime. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] @@ -47,10 +47,7 @@ impl serde::Serialize for Extrinsic { } } -impl ExtrinsicT for Extrinsic { - type Call = Extrinsic; - type SignaturePayload = (); - +impl ExtrinsicLike for Extrinsic { fn is_signed(&self) -> Option { if let Extrinsic::IncludeData(_) = *self { Some(false) @@ -59,8 +56,12 @@ impl ExtrinsicT for Extrinsic { } } - fn new(call: Self::Call, _signature_payload: Option) -> Option { - Some(call) + fn is_bare(&self) -> bool { + if let Extrinsic::IncludeData(_) = *self { + true + } else { + false + } } } diff --git a/substrate/primitives/weights/Cargo.toml b/substrate/primitives/weights/Cargo.toml index 9b830403dbe..c4e1897dbb8 100644 --- a/substrate/primitives/weights/Cargo.toml +++ b/substrate/primitives/weights/Cargo.toml @@ -47,6 +47,4 @@ serde = [ "sp-arithmetic/serde", ] -json-schema = [ - "dep:schemars", -] +json-schema = ["dep:schemars"] diff --git a/substrate/scripts/run_all_benchmarks.sh b/substrate/scripts/run_all_benchmarks.sh index 6dd7cede319..fe5f89a5b56 100755 --- a/substrate/scripts/run_all_benchmarks.sh +++ b/substrate/scripts/run_all_benchmarks.sh @@ -107,6 +107,19 @@ for PALLET in "${PALLETS[@]}"; do FOLDER="$(echo "${PALLET#*_}" | tr '_' '-')"; WEIGHT_FILE="./frame/${FOLDER}/src/weights.rs" + + # Special handling of custom weight paths. + if [ "$PALLET" == "frame_system_extensions" ] || [ "$PALLET" == "frame-system-extensions" ] + then + WEIGHT_FILE="./frame/system/src/extensions/weights.rs" + elif [ "$PALLET" == "pallet_asset_conversion_tx_payment" ] || [ "$PALLET" == "pallet-asset-conversion-tx-payment" ] + then + WEIGHT_FILE="./frame/transaction-payment/asset-conversion-tx-payment/src/weights.rs" + elif [ "$PALLET" == "pallet_asset_tx_payment" ] || [ "$PALLET" == "pallet-asset-tx-payment" ] + then + WEIGHT_FILE="./frame/transaction-payment/asset-tx-payment/src/weights.rs" + fi + echo "[+] Benchmarking $PALLET with weight file $WEIGHT_FILE"; OUTPUT=$( diff --git a/substrate/test-utils/runtime/src/extrinsic.rs b/substrate/test-utils/runtime/src/extrinsic.rs index 5ae0d8f8f6e..8f94dd10a83 100644 --- a/substrate/test-utils/runtime/src/extrinsic.rs +++ b/substrate/test-utils/runtime/src/extrinsic.rs @@ -26,7 +26,10 @@ use frame_metadata_hash_extension::CheckMetadataHash; use frame_system::{CheckNonce, CheckWeight}; use sp_core::crypto::Pair as TraitPair; use sp_keyring::AccountKeyring; -use sp_runtime::{traits::SignedExtension, transaction_validity::TransactionPriority, Perbill}; +use sp_runtime::{ + generic::Preamble, traits::TransactionExtension, transaction_validity::TransactionPriority, + Perbill, +}; /// Transfer used in test substrate pallet. Extrinsic is created and signed using this data. #[derive(Clone)] @@ -66,11 +69,11 @@ impl TryFrom<&Extrinsic> for TransferData { match uxt { Extrinsic { function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }), - signature: Some((from, _, (CheckNonce(nonce), ..))), + preamble: Preamble::Signed(from, _, _, ((CheckNonce(nonce), ..), ..)), } => Ok(TransferData { from: *from, to: *dest, amount: *value, nonce: *nonce }), Extrinsic { function: RuntimeCall::SubstrateTest(PalletCall::bench_call { transfer }), - signature: None, + preamble: Preamble::Bare(_), } => Ok(transfer.clone()), _ => Err(()), } @@ -203,9 +206,8 @@ impl ExtrinsicBuilder { /// Build `Extrinsic` using embedded parameters pub fn build(self) -> Extrinsic { if let Some(signer) = self.signer { - let extra = ( - CheckNonce::from(self.nonce.unwrap_or(0)), - CheckWeight::new(), + let tx_ext = ( + (CheckNonce::from(self.nonce.unwrap_or(0)), CheckWeight::new()), CheckSubstrateCall {}, self.metadata_hash .map(CheckMetadataHash::new_with_custom_hash) @@ -213,14 +215,14 @@ impl ExtrinsicBuilder { ); let raw_payload = SignedPayload::from_raw( self.function.clone(), - extra.clone(), - extra.additional_signed().unwrap(), + tx_ext.clone(), + tx_ext.implicit().unwrap(), ); let signature = raw_payload.using_encoded(|e| signer.sign(e)); - Extrinsic::new_signed(self.function, signer.public(), signature, extra) + Extrinsic::new_signed(self.function, signer.public(), signature, tx_ext) } else { - Extrinsic::new_unsigned(self.function) + Extrinsic::new_bare(self.function) } } } diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index 74965264c03..a4a0d348a39 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -63,9 +63,11 @@ 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, - traits::{BlakeTwo256, Block as BlockT, DispatchInfoOf, NumberFor, Verify}, - transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, + create_runtime_str, impl_opaque_keys, impl_tx_ext_default, + traits::{BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, NumberFor, Verify}, + transaction_validity::{ + TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, + }, ApplyExtrinsicResult, ExtrinsicInclusionMode, Perbill, }; #[cfg(any(feature = "std", test))] @@ -147,18 +149,18 @@ pub type Signature = sr25519::Signature; #[cfg(feature = "std")] pub type Pair = sp_core::sr25519::Pair; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( - CheckNonce, - CheckWeight, +// TODO: Remove after the Checks are migrated to TxExtension. +/// The extension to the basic transaction logic. +pub type TxExtension = ( + (CheckNonce, CheckWeight), CheckSubstrateCall, frame_metadata_hash_extension::CheckMetadataHash, ); /// The payload being signed in transactions. -pub type SignedPayload = sp_runtime::generic::SignedPayload; +pub type SignedPayload = sp_runtime::generic::SignedPayload; /// Unchecked extrinsic type as expected by this runtime. pub type Extrinsic = - sp_runtime::generic::UncheckedExtrinsic; + sp_runtime::generic::UncheckedExtrinsic; /// An identifier for an account on this system. pub type AccountId = ::Signer; @@ -252,8 +254,17 @@ impl sp_runtime::traits::Printable for CheckSubstrateCall { } } +impl sp_runtime::traits::RefundWeight for CheckSubstrateCall { + fn refund(&mut self, _weight: frame_support::weights::Weight) {} +} +impl sp_runtime::traits::ExtensionPostDispatchWeightHandler + for CheckSubstrateCall +{ + fn set_extension_weight(&mut self, _info: &CheckSubstrateCall) {} +} + impl sp_runtime::traits::Dispatchable for CheckSubstrateCall { - type RuntimeOrigin = CheckSubstrateCall; + type RuntimeOrigin = RuntimeOrigin; type Config = CheckSubstrateCall; type Info = CheckSubstrateCall; type PostInfo = CheckSubstrateCall; @@ -266,42 +277,32 @@ impl sp_runtime::traits::Dispatchable for CheckSubstrateCall { } } -impl sp_runtime::traits::SignedExtension for CheckSubstrateCall { - type AccountId = AccountId; - type Call = RuntimeCall; - type AdditionalSigned = (); - type Pre = (); +impl sp_runtime::traits::TransactionExtension for CheckSubstrateCall { const IDENTIFIER: &'static str = "CheckSubstrateCall"; - - fn additional_signed( - &self, - ) -> core::result::Result { - Ok(()) - } + type Implicit = (); + type Pre = (); + type Val = (); + impl_tx_ext_default!(RuntimeCall; weight prepare); fn validate( &self, - _who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, + origin: ::RuntimeOrigin, + call: &RuntimeCall, + _info: &DispatchInfoOf, _len: usize, - ) -> TransactionValidity { + _self_implicit: Self::Implicit, + _inherited_implication: &impl Encode, + ) -> Result< + (ValidTransaction, Self::Val, ::RuntimeOrigin), + TransactionValidityError, + > { log::trace!(target: LOG_TARGET, "validate"); - match call { + let v = match call { RuntimeCall::SubstrateTest(ref substrate_test_call) => - substrate_test_pallet::validate_runtime_call(substrate_test_call), - _ => Ok(Default::default()), - } - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &sp_runtime::traits::DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(drop) + substrate_test_pallet::validate_runtime_call(substrate_test_call)?, + _ => Default::default(), + }; + Ok((v, (), origin)) } } @@ -670,7 +671,7 @@ impl_runtime_apis! { impl sp_offchain::OffchainWorkerApi for Runtime { fn offchain_worker(header: &::Header) { - let ext = Extrinsic::new_unsigned( + let ext = Extrinsic::new_bare( substrate_test_pallet::pallet::Call::storage_change{ key:b"some_key".encode(), value:Some(header.number.encode()) @@ -1051,7 +1052,7 @@ mod tests { use sp_consensus::BlockOrigin; use sp_core::{storage::well_known_keys::HEAP_PAGES, traits::CallContext}; use sp_runtime::{ - traits::{Hash as _, SignedExtension}, + traits::{DispatchTransaction, Hash as _}, transaction_validity::{InvalidTransaction, ValidTransaction}, }; use substrate_test_runtime_client::{ @@ -1205,26 +1206,28 @@ mod tests { let len = 0_usize; assert_eq!( CheckSubstrateCall {} - .validate( - &x, + .validate_only( + Some(x).into(), &ExtrinsicBuilder::new_call_with_priority(16).build().function, &info, - len + len, ) .unwrap() + .0 .priority, 16 ); assert_eq!( CheckSubstrateCall {} - .validate( - &x, + .validate_only( + Some(x).into(), &ExtrinsicBuilder::new_call_do_not_propagate().build().function, &info, - len + len, ) .unwrap() + .0 .propagate, false ); diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/template.hbs b/substrate/utils/frame/benchmarking-cli/src/pallet/template.hbs index 1e5e294acba..a044049a0d6 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/template.hbs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/template.hbs @@ -22,7 +22,11 @@ use core::marker::PhantomData; /// Weight functions for `{{pallet}}`. pub struct WeightInfo(PhantomData); +{{#if (eq pallet "frame_system_extensions")}} +impl frame_system::ExtensionsWeightInfo for WeightInfo { +{{else}} impl {{pallet}}::WeightInfo for WeightInfo { +{{/if}} {{#each benchmarks as |benchmark|}} {{#each benchmark.comments as |comment|}} /// {{comment}} diff --git a/substrate/utils/frame/remote-externalities/src/lib.rs b/substrate/utils/frame/remote-externalities/src/lib.rs index 955e79008c8..75a2ac2aef4 100644 --- a/substrate/utils/frame/remote-externalities/src/lib.rs +++ b/substrate/utils/frame/remote-externalities/src/lib.rs @@ -1239,8 +1239,9 @@ where #[cfg(test)] mod test_prelude { pub(crate) use super::*; - pub(crate) use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper, H256 as Hash}; - pub(crate) type Block = RawBlock>; + pub(crate) use sp_runtime::testing::{Block as RawBlock, MockCallU64}; + pub(crate) type UncheckedXt = sp_runtime::testing::TestXt; + pub(crate) type Block = RawBlock; pub(crate) fn init_logger() { sp_tracing::try_init_simple(); diff --git a/substrate/utils/frame/rpc/client/src/lib.rs b/substrate/utils/frame/rpc/client/src/lib.rs index 221f260b156..0aecad55305 100644 --- a/substrate/utils/frame/rpc/client/src/lib.rs +++ b/substrate/utils/frame/rpc/client/src/lib.rs @@ -199,11 +199,12 @@ where #[cfg(test)] mod tests { use super::*; - use sp_runtime::testing::{Block as TBlock, ExtrinsicWrapper, Header, H256}; + use sp_runtime::testing::{Block as TBlock, Header, MockCallU64, TestXt, H256}; use std::sync::Arc; use tokio::sync::Mutex; - type Block = TBlock>; + type UncheckedXt = TestXt; + type Block = TBlock; type BlockNumber = u64; type Hash = H256; diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index 7379e33b6b3..464cad4e3da 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -103,8 +103,8 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -/// The signed extensions that are added to the runtime. -type SignedExtra = ( +/// The transaction extensions that are added to the runtime. +type TxExtension = ( // Checks that the sender is not the zero address. frame_system::CheckNonZeroSender, // Checks that the runtime version is correct. @@ -207,7 +207,7 @@ impl pallet_transaction_payment::Config for Runtime { // Implements the types required for the template pallet. impl pallet_minimal_template::Config for Runtime {} -type Block = frame::runtime::types_common::BlockOf; +type Block = frame::runtime::types_common::BlockOf; type Header = HeaderFor; type RuntimeExecutive = diff --git a/templates/parachain/runtime/src/configs/mod.rs b/templates/parachain/runtime/src/configs/mod.rs index 0cf6497fd95..ba4c71c7f21 100644 --- a/templates/parachain/runtime/src/configs/mod.rs +++ b/templates/parachain/runtime/src/configs/mod.rs @@ -177,6 +177,7 @@ impl pallet_transaction_payment::Config for Runtime { type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; + type WeightInfo = (); } impl pallet_sudo::Config for Runtime { diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs index cb30eb0e80d..78dc38ef427 100644 --- a/templates/parachain/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -72,9 +72,9 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. +/// The extension to the basic transaction logic. #[docify::export(template_signed_extra)] -pub type SignedExtra = ( +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -89,7 +89,7 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// All migrations of the runtime, aside from the ones declared in the pallets. /// diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml index a0285e048d1..8a3c7d0ac78 100644 --- a/templates/solochain/node/Cargo.toml +++ b/templates/solochain/node/Cargo.toml @@ -72,6 +72,7 @@ std = ["solochain-template-runtime/std"] runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "sc-service/runtime-benchmarks", "solochain-template-runtime/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/templates/solochain/node/src/benchmarking.rs b/templates/solochain/node/src/benchmarking.rs index 1bd3578af68..0d60230cd19 100644 --- a/templates/solochain/node/src/benchmarking.rs +++ b/templates/solochain/node/src/benchmarking.rs @@ -109,7 +109,7 @@ pub fn create_benchmark_extrinsic( .checked_next_power_of_two() .map(|c| c / 2) .unwrap_or(2) as u64; - let extra: runtime::SignedExtra = ( + let tx_ext: runtime::TxExtension = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), @@ -126,7 +126,7 @@ pub fn create_benchmark_extrinsic( let raw_payload = runtime::SignedPayload::from_raw( call.clone(), - extra.clone(), + tx_ext.clone(), ( (), runtime::VERSION.spec_version, @@ -145,7 +145,7 @@ pub fn create_benchmark_extrinsic( call, sp_runtime::AccountId32::from(sender.public()).into(), runtime::Signature::Sr25519(signature), - extra, + tx_ext, ) } diff --git a/templates/solochain/runtime/Cargo.toml b/templates/solochain/runtime/Cargo.toml index 8a7fa74a597..837849e844b 100644 --- a/templates/solochain/runtime/Cargo.toml +++ b/templates/solochain/runtime/Cargo.toml @@ -81,18 +81,14 @@ substrate-wasm-builder = { optional = true, workspace = true, default-features = default = ["std"] std = [ "codec/std", - "scale-info/std", - + "frame-benchmarking?/std", "frame-executive/std", "frame-metadata-hash-extension/std", "frame-support/std", "frame-system-benchmarking?/std", "frame-system-rpc-runtime-api/std", "frame-system/std", - - "frame-benchmarking?/std", "frame-try-runtime?/std", - "pallet-aura/std", "pallet-balances/std", "pallet-grandpa/std", @@ -101,7 +97,8 @@ std = [ "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-transaction-payment/std", - + "scale-info/std", + "serde_json/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", @@ -109,15 +106,13 @@ std = [ "sp-core/std", "sp-genesis-builder/std", "sp-inherents/std", + "sp-keyring/std", "sp-offchain/std", "sp-runtime/std", "sp-session/std", "sp-storage/std", "sp-transaction-pool/std", "sp-version/std", - - "serde_json/std", - "sp-keyring/std", "substrate-wasm-builder", ] @@ -131,6 +126,7 @@ runtime-benchmarks = [ "pallet-sudo/runtime-benchmarks", "pallet-template/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] diff --git a/templates/solochain/runtime/src/apis.rs b/templates/solochain/runtime/src/apis.rs index 87a09a5f7fe..f21eaa34443 100644 --- a/templates/solochain/runtime/src/apis.rs +++ b/templates/solochain/runtime/src/apis.rs @@ -223,6 +223,7 @@ impl_runtime_apis! { use frame_benchmarking::{baseline, Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use baseline::Pallet as BaselineBench; use super::*; @@ -240,6 +241,7 @@ impl_runtime_apis! { use frame_benchmarking::{baseline, Benchmarking, BenchmarkBatch}; use sp_storage::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; + use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use baseline::Pallet as BaselineBench; use super::*; diff --git a/templates/solochain/runtime/src/benchmarks.rs b/templates/solochain/runtime/src/benchmarks.rs index f39c2bd2959..59012e0b047 100644 --- a/templates/solochain/runtime/src/benchmarks.rs +++ b/templates/solochain/runtime/src/benchmarks.rs @@ -26,6 +26,7 @@ frame_benchmarking::define_benchmarks!( [frame_benchmarking, BaselineBench::] [frame_system, SystemBench::] + [frame_system_extensions, SystemExtensionsBench::] [pallet_balances, Balances] [pallet_timestamp, Timestamp] [pallet_sudo, Sudo] diff --git a/templates/solochain/runtime/src/configs/mod.rs b/templates/solochain/runtime/src/configs/mod.rs index 02d44967513..e34b3cb8215 100644 --- a/templates/solochain/runtime/src/configs/mod.rs +++ b/templates/solochain/runtime/src/configs/mod.rs @@ -148,6 +148,7 @@ impl pallet_transaction_payment::Config for Runtime { type WeightToFee = IdentityFee; type LengthToFee = IdentityFee; type FeeMultiplierUpdate = ConstFeeMultiplier; + type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight; } impl pallet_sudo::Config for Runtime { diff --git a/templates/solochain/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs index bc47f3aeac8..42361a2ff36 100644 --- a/templates/solochain/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -146,8 +146,8 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( +/// The `TransactionExtension`` to the basic transaction logic. +pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, @@ -161,10 +161,10 @@ pub type SignedExtra = ( /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + generic::UncheckedExtrinsic; /// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; +pub type SignedPayload = generic::SignedPayload; /// All migrations of the runtime, aside from the ones declared in the pallets. /// diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index e05bf129bbd..7147a11fb9c 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -148,6 +148,7 @@ std = [ "pallet-tx-pause?/std", "pallet-uniques?/std", "pallet-utility?/std", + "pallet-verify-signature?/std", "pallet-vesting?/std", "pallet-whitelist?/std", "pallet-xcm-benchmarks?/std", @@ -251,6 +252,7 @@ runtime-benchmarks = [ "frame-system?/runtime-benchmarks", "pallet-alliance?/runtime-benchmarks", "pallet-asset-conversion-ops?/runtime-benchmarks", + "pallet-asset-conversion-tx-payment?/runtime-benchmarks", "pallet-asset-conversion?/runtime-benchmarks", "pallet-asset-rate?/runtime-benchmarks", "pallet-asset-tx-payment?/runtime-benchmarks", @@ -321,11 +323,13 @@ runtime-benchmarks = [ "pallet-sudo?/runtime-benchmarks", "pallet-timestamp?/runtime-benchmarks", "pallet-tips?/runtime-benchmarks", + "pallet-transaction-payment?/runtime-benchmarks", "pallet-transaction-storage?/runtime-benchmarks", "pallet-treasury?/runtime-benchmarks", "pallet-tx-pause?/runtime-benchmarks", "pallet-uniques?/runtime-benchmarks", "pallet-utility?/runtime-benchmarks", + "pallet-verify-signature?/runtime-benchmarks", "pallet-vesting?/runtime-benchmarks", "pallet-whitelist?/runtime-benchmarks", "pallet-xcm-benchmarks?/runtime-benchmarks", @@ -460,6 +464,7 @@ try-runtime = [ "pallet-tx-pause?/try-runtime", "pallet-uniques?/try-runtime", "pallet-utility?/try-runtime", + "pallet-verify-signature?/try-runtime", "pallet-vesting?/try-runtime", "pallet-whitelist?/try-runtime", "pallet-xcm-bridge-hub-router?/try-runtime", @@ -536,7 +541,7 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] -runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-fixtures", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] +runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-fixtures", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] runtime = [ "frame-benchmarking", "frame-benchmarking-pallet-pov", @@ -1332,6 +1337,11 @@ path = "../substrate/frame/utility" default-features = false optional = true +[dependencies.pallet-verify-signature] +path = "../substrate/frame/verify-signature" +default-features = false +optional = true + [dependencies.pallet-vesting] path = "../substrate/frame/vesting" default-features = false diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 7778706d515..f3fc949c66e 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -705,6 +705,10 @@ pub use pallet_uniques; #[cfg(feature = "pallet-utility")] pub use pallet_utility; +/// FRAME verify signature pallet. +#[cfg(feature = "pallet-verify-signature")] +pub use pallet_verify_signature; + /// FRAME pallet for manage vesting. #[cfg(feature = "pallet-vesting")] pub use pallet_vesting; -- GitLab From e9238b39eb6dff620ab0088885d013d3e4f44638 Mon Sep 17 00:00:00 2001 From: Andrii Date: Fri, 18 Oct 2024 22:34:49 +0300 Subject: [PATCH 09/50] Removed .scale files (#6124) Those files were introduced by mistake in https://github.com/paritytech/polkadot-sdk/pull/6039 --- .../metadata-files/coretime-rococo-local.scale | Bin 166046 -> 0 bytes .../metadata-files/rococo-local.scale | Bin 389535 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 polkadot/zombienet-sdk-tests/metadata-files/coretime-rococo-local.scale delete mode 100644 polkadot/zombienet-sdk-tests/metadata-files/rococo-local.scale diff --git a/polkadot/zombienet-sdk-tests/metadata-files/coretime-rococo-local.scale b/polkadot/zombienet-sdk-tests/metadata-files/coretime-rococo-local.scale deleted file mode 100644 index 3af6685b91dccb5effc21b5216b850a6127b0650..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166046 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

&dNZaz4ev!FDZD(@YQ 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 -- GitLab From c6b847a3a694b05f0a88c56b3f56393f1bea62b0 Mon Sep 17 00:00:00 2001 From: Andrii Date: Sat, 19 Oct 2024 01:02:42 +0300 Subject: [PATCH 10/50] Added .scale metadata-files to gitignore (#6130) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To prevent [similar issues](https://github.com/paritytech/polkadot-sdk/pull/6124) add .scale files to gitignore --------- Co-authored-by: Branislav Kontur Co-authored-by: Bastian Köcher --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0263626d832..28c28cc8ab0 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ rls*.log runtime/wasm/target/ substrate.code-workspace target/ +*.scale -- GitLab From 5e0843e5a0fb601016dc1ccd46451a96049600e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sat, 19 Oct 2024 01:05:44 +0200 Subject: [PATCH 11/50] sync: Remove checking of the extrinsics root (#5686) With the introduction of `system_version` in https://github.com/paritytech/polkadot-sdk/pull/4257 the extrinsic root may also use the `V1` layout. At this point in the sync code it would require some special handling to find out the `system_version`. So, this pull request is removing it. The extrinsics root is checked when executing the block later, so that at least no invalid block gets imported. --- prdoc/pr_5686.prdoc | 15 +++++++++++++ .../network/sync/src/strategy/chain_sync.rs | 22 +------------------ 2 files changed, 16 insertions(+), 21 deletions(-) create mode 100644 prdoc/pr_5686.prdoc diff --git a/prdoc/pr_5686.prdoc b/prdoc/pr_5686.prdoc new file mode 100644 index 00000000000..3f0da912a34 --- /dev/null +++ b/prdoc/pr_5686.prdoc @@ -0,0 +1,15 @@ +title: "sync: Remove checking of the extrinsics root" + +doc: + - audience: Node Dev + description: | + Remove checking the extrinsics root as part of the sync code. + With the introduction of `system_version` and the possibility to use the `V1` + layout for the trie when calculating the extrinsics root, it would require the + sync code to fetch the runtime version first before knowing which layout to use + when building the extrinsic root. + The extrinsics root is still checked when executing a block on chain. + +crates: + - name: sc-network-sync + bump: patch diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index b0e28d00f64..202033e8e00 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -42,7 +42,6 @@ use crate::{ LOG_TARGET, }; -use codec::Encode; use log::{debug, error, info, trace, warn}; use prometheus_endpoint::{register, Gauge, PrometheusError, Registry, U64}; use sc_client_api::{blockchain::BlockGap, BlockBackend, ProofProvider}; @@ -57,8 +56,7 @@ use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata}; use sp_consensus::{BlockOrigin, BlockStatus}; use sp_runtime::{ traits::{ - Block as BlockT, CheckedSub, Hash, HashingFor, Header as HeaderT, NumberFor, One, - SaturatedConversion, Zero, + Block as BlockT, CheckedSub, Header as HeaderT, NumberFor, One, SaturatedConversion, Zero, }, EncodedJustification, Justifications, }; @@ -2305,24 +2303,6 @@ pub fn validate_blocks( return Err(BadPeer(*peer_id, rep::BAD_BLOCK)); } } - if let (Some(header), Some(body)) = (&b.header, &b.body) { - let expected = *header.extrinsics_root(); - let got = HashingFor::::ordered_trie_root( - body.iter().map(Encode::encode).collect(), - sp_runtime::StateVersion::V0, - ); - if expected != got { - debug!( - target: LOG_TARGET, - "Bad extrinsic root for a block {} received from {}. Expected {:?}, got {:?}", - b.hash, - peer_id, - expected, - got, - ); - return Err(BadPeer(*peer_id, rep::BAD_BLOCK)); - } - } } Ok(blocks.first().and_then(|b| b.header.as_ref()).map(|h| *h.number())) -- GitLab From 21b3a46b11c15ff3915557201657322f42527a2b Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Sun, 20 Oct 2024 17:18:32 +0200 Subject: [PATCH 12/50] Improve CheckMetadataHash: make it constant time and compile error on wrong env variable (#6141) * The compilation now panics if the optional compile-time environment variable `RUNTIME_METADATA_HASH` contains an invalid value. * The weight for the `CheckMetadataHash` transaction extension is more accurate as it is almost compile-time. --- Cargo.lock | 1 + Cargo.toml | 1 + prdoc/pr_6141.prdoc | 11 ++++++++ .../frame/metadata-hash-extension/Cargo.toml | 2 ++ .../frame/metadata-hash-extension/src/lib.rs | 26 ++++++++++++++++--- 5 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 prdoc/pr_6141.prdoc diff --git a/Cargo.lock b/Cargo.lock index 4bb17e66dca..c8f828e85e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6367,6 +6367,7 @@ name = "frame-metadata-hash-extension" version = "0.1.0" dependencies = [ "array-bytes", + "const-hex", "docify", "frame-metadata 16.0.0", "frame-support", diff --git a/Cargo.toml b/Cargo.toml index b357b99be11..77586ae9d8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -683,6 +683,7 @@ color-print = { version = "0.3.4" } colored = { version = "2.0.4" } comfy-table = { version = "7.1.0", default-features = false } console = { version = "0.15.8" } +const-hex = { version = "1.10.0", default-features = false } contracts-rococo-runtime = { path = "cumulus/parachains/runtimes/contracts/contracts-rococo" } coretime-rococo-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo" } coretime-rococo-runtime = { path = "cumulus/parachains/runtimes/coretime/coretime-rococo" } diff --git a/prdoc/pr_6141.prdoc b/prdoc/pr_6141.prdoc new file mode 100644 index 00000000000..d9994ac4f84 --- /dev/null +++ b/prdoc/pr_6141.prdoc @@ -0,0 +1,11 @@ +title: Improve `CheckMetadataHash` transaction extension weight and logic + +doc: + - audience: Runtime Dev + description: | + The compilation now panics if the optional compile-time environment variable `RUNTIME_METADATA_HASH` contains an invalid value. + The weight for the `CheckMetadataHash` transaction extension is more accurate as it is almost compile-time. + +crates: +- name: frame-metadata-hash-extension + bump: minor diff --git a/substrate/frame/metadata-hash-extension/Cargo.toml b/substrate/frame/metadata-hash-extension/Cargo.toml index 10d90bba091..bca2c3ffb19 100644 --- a/substrate/frame/metadata-hash-extension/Cargo.toml +++ b/substrate/frame/metadata-hash-extension/Cargo.toml @@ -17,6 +17,7 @@ frame-support = { workspace = true } frame-system = { workspace = true } log = { workspace = true } docify = { workspace = true } +const-hex = { workspace = true } [dev-dependencies] substrate-wasm-builder = { features = ["metadata-hash"], workspace = true, default-features = true } @@ -31,6 +32,7 @@ sp-tracing = { workspace = true, default-features = true } default = ["std"] std = [ "codec/std", + "const-hex/std", "frame-support/std", "frame-system/std", "log/std", diff --git a/substrate/frame/metadata-hash-extension/src/lib.rs b/substrate/frame/metadata-hash-extension/src/lib.rs index 9bd092c8982..0b45f5a7e51 100644 --- a/substrate/frame/metadata-hash-extension/src/lib.rs +++ b/substrate/frame/metadata-hash-extension/src/lib.rs @@ -39,7 +39,7 @@ extern crate alloc; extern crate self as frame_metadata_hash_extension; use codec::{Decode, Encode}; -use frame_support::DebugNoBound; +use frame_support::{pallet_prelude::Weight, DebugNoBound}; use frame_system::Config; use scale_info::TypeInfo; use sp_runtime::{ @@ -68,12 +68,24 @@ enum MetadataHash { Custom([u8; 32]), } +const RUNTIME_METADATA: Option<[u8; 32]> = if let Some(hex) = option_env!("RUNTIME_METADATA_HASH") { + match const_hex::const_decode_to_array(hex.as_bytes()) { + Ok(hex) => Some(hex), + Err(_) => panic!( + "Invalid RUNTIME_METADATA_HASH environment variable: it must be a 32 \ + bytes value in hexadecimal: e.g. 0x123ABCabd...123ABCabc. Upper case or lower case, \ + 0x prefix is optional." + ), + } +} else { + None +}; + impl MetadataHash { /// Returns the metadata hash. fn hash(&self) -> Option<[u8; 32]> { match self { - Self::FetchFromEnv => - option_env!("RUNTIME_METADATA_HASH").map(array_bytes::hex2array_unchecked), + Self::FetchFromEnv => RUNTIME_METADATA, Self::Custom(hash) => Some(*hash), } } @@ -155,5 +167,11 @@ impl TransactionExtension for CheckMeta type Val = (); type Pre = (); - impl_tx_ext_default!(T::RuntimeCall; weight validate prepare); + fn weight(&self, _: &T::RuntimeCall) -> Weight { + // The weight is the weight of implicit, it consists of a few match operation, it is + // negligible. + Weight::zero() + } + + impl_tx_ext_default!(T::RuntimeCall; validate prepare); } -- GitLab From 73a51fd9d6a365350ffa428337096ffae15ddf4f Mon Sep 17 00:00:00 2001 From: Javier Viola <363911+pepoviola@users.noreply.github.com> Date: Mon, 21 Oct 2024 08:51:23 -0300 Subject: [PATCH 13/50] bump zombienet version `v1.3.116` (#6155) Bump zombienet version, includes fixes for `ci`. (mostly timeouts for k8s). --- .gitlab/pipeline/zombienet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/pipeline/zombienet.yml b/.gitlab/pipeline/zombienet.yml index 1589fccc972..08bfed2e24c 100644 --- a/.gitlab/pipeline/zombienet.yml +++ b/.gitlab/pipeline/zombienet.yml @@ -1,7 +1,7 @@ .zombienet-refs: extends: .build-refs variables: - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.115" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.116" PUSHGATEWAY_URL: "http://zombienet-prometheus-pushgateway.managed-monitoring:9091/metrics/job/zombie-metrics" DEBUG: "zombie,zombie::network-node,zombie::kube::client::logs" ZOMBIE_PROVIDER: "k8s" -- GitLab From d4409e3bad65cde9097e73f9f7bfd602e442375c Mon Sep 17 00:00:00 2001 From: Pavel Suprunyuk <52500720+pavelsupr@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:08:45 +0200 Subject: [PATCH 14/50] Fix a reference name in a reusable workflow invocation (#6118) This is just a small adjustment of the reusable workflow invocation --- .github/workflows/fork-sync-action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fork-sync-action.yml b/.github/workflows/fork-sync-action.yml index 065226764be..50774e91052 100644 --- a/.github/workflows/fork-sync-action.yml +++ b/.github/workflows/fork-sync-action.yml @@ -12,7 +12,7 @@ on: jobs: job_sync_branches: - uses: paritytech-release/sync-workflows/.github/workflows/sync-with-upstream.yml@latest + uses: paritytech-release/sync-workflows/.github/workflows/sync-with-upstream.yml@main with: fork_writer_app_id: ${{ vars.UPSTREAM_CONTENT_SYNC_APP_ID}} fork_owner: ${{ vars.RELEASE_ORG}} -- GitLab From a538ac10ab90c7532c62619770004c91ea1bdc09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:37:27 +0200 Subject: [PATCH 15/50] Bump the known_good_semver group across 1 directory with 3 updates (#6145) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the known_good_semver group with 3 updates in the / directory: [impl-serde](https://github.com/paritytech/parity-common), [serde_json](https://github.com/serde-rs/json) and [syn](https://github.com/dtolnay/syn). Updates `impl-serde` from 0.4.0 to 0.5.0

Commits

Updates `serde_json` from 1.0.128 to 1.0.132
Release notes

Sourced from serde_json's releases.

1.0.132

  • Improve binary size and compile time for JSON array and JSON object deserialization by about 50% (#1205)
  • Improve performance of JSON array and JSON object deserialization by about 8% (#1206)

1.0.131

  • Implement Deserializer and IntoDeserializer for Map<String, Value> and &Map<String, Value> (#1135, thanks @​swlynch99)

1.0.130

  • Support converting and deserializing Number from i128 and u128 (#1141, thanks @​druide)

1.0.129

Commits
  • 86d933c Release 1.0.132
  • f45b422 Merge pull request #1206 from dtolnay/hasnext
  • f2082d2 Clearer order of comparisons
  • 0f54a1a Handle early return sooner on eof in seq or map
  • 2a4cb44 Rearrange 'match peek'
  • 4cb90ce Merge pull request #1205 from dtolnay/hasnext
  • b71ccd2 Reduce duplicative instantiation of logic in SeqAccess and MapAccess
  • a810ba9 Release 1.0.131
  • 0d084c5 Touch up PR 1135
  • b4954a9 Merge pull request #1135 from swlynch99/map-deserializer
  • Additional commits viewable in compare view

Updates `syn` from 2.0.79 to 2.0.82
Release notes

Sourced from syn's releases.

2.0.82

  • Provide Parse impls for PreciseCapture and CapturedParam (#1757, #1758)
  • Support parsing unsafe attributes (#1759)
  • Add Fold and VisitMut methods for Vec<Attribute> (#1762)

2.0.81

  • Add TypeParamBound::PreciseCapture to represent precise capture syntax impl Trait + use<'a, T> (#1752, #1753, #1754)

2.0.80

  • Add Expr::RawAddr (#1743)
  • Reject precise captures and ~const in inappropriate syntax positions (#1747)
  • Reject trait bound containing only precise capture (#1748)
Commits
  • 76092cf Release 2.0.82
  • 937dbcb Merge pull request #1762 from dtolnay/vecattr
  • 386ae9d Add Fold and VisitMut methods for Vec<Attribute>
  • 4c7f82e Merge pull request #1759 from dtolnay/unsafeattr
  • a45af00 Parse unsafe attributes
  • e011ba7 Merge pull request #1758 from dtolnay/precisecapture
  • c25900d Implement Parse for CapturedParam
  • fc22fce Merge pull request #1757 from dtolnay/precisecapture
  • 3a45d69 Implement Parse for PreciseCapture
  • c9bdfac Tweak parsing logic for TypeParamBound
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 198 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 6 +- 2 files changed, 102 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8f828e85e6..864ba5e8047 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.79", + "syn 2.0.82", "syn-solidity", "tiny-keccak", ] @@ -295,7 +295,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -753,7 +753,7 @@ checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", "synstructure 0.13.1", ] @@ -776,7 +776,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1384,7 +1384,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1401,7 +1401,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -1616,7 +1616,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -3040,7 +3040,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4398,7 +4398,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4951,7 +4951,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -4991,7 +4991,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "scratch", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5008,7 +5008,7 @@ checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5056,7 +5056,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "strsim 0.11.1", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5078,7 +5078,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5195,7 +5195,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5206,7 +5206,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5217,7 +5217,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5325,7 +5325,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5386,7 +5386,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "regex", - "syn 2.0.79", + "syn 2.0.82", "termcolor", "toml 0.8.12", "walkdir", @@ -5618,7 +5618,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5638,7 +5638,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5649,7 +5649,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5864,7 +5864,7 @@ dependencies = [ "prettyplease", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -5936,7 +5936,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -6279,7 +6279,7 @@ dependencies = [ "quote 1.0.37", "scale-info", "sp-arithmetic 23.0.0", - "syn 2.0.79", + "syn 2.0.82", "trybuild", ] @@ -6494,7 +6494,7 @@ dependencies = [ "sp-metadata-ir 0.6.0", "sp-runtime 31.0.1", "static_assertions", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -6505,7 +6505,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -6514,7 +6514,7 @@ version = "11.0.0" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -6768,7 +6768,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -8249,7 +8249,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -8981,7 +8981,7 @@ dependencies = [ "proc-macro-warning 0.4.2", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -9389,7 +9389,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -9403,7 +9403,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -9414,7 +9414,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -9425,7 +9425,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -9762,7 +9762,7 @@ dependencies = [ "cfg-if", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -10366,7 +10366,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -10542,7 +10542,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -11314,7 +11314,7 @@ version = "18.0.0" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -12433,7 +12433,7 @@ version = "0.1.0" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -12678,7 +12678,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "sp-runtime 31.0.1", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -13693,7 +13693,7 @@ dependencies = [ "pest_meta", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -13734,7 +13734,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16202,7 +16202,7 @@ dependencies = [ "polkavm-common 0.8.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16214,7 +16214,7 @@ dependencies = [ "polkavm-common 0.9.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16226,7 +16226,7 @@ dependencies = [ "polkavm-common 0.12.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16236,7 +16236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15e85319a0d5129dc9f021c62607e0804f5fb777a05cdda44d750ac0732def66" dependencies = [ "polkavm-derive-impl 0.8.0", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16246,7 +16246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl 0.9.0", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16256,7 +16256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd35472599d35d90e24afe9eb39ae6ee6cb1b924f0c03b277ef8b5f174a63853" dependencies = [ "polkavm-derive-impl 0.12.0", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16473,7 +16473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2 1.0.86", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16578,7 +16578,7 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16589,7 +16589,7 @@ checksum = "9b698b0b09d40e9b7c1a47b132d66a8b54bcd20583d9b6d06e4535e383b4405c" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16670,7 +16670,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16752,7 +16752,7 @@ dependencies = [ "prost 0.12.6", "prost-types 0.12.4", "regex", - "syn 2.0.79", + "syn 2.0.82", "tempfile", ] @@ -16773,7 +16773,7 @@ dependencies = [ "prost 0.13.2", "prost-types 0.13.2", "regex", - "syn 2.0.79", + "syn 2.0.82", "tempfile", ] @@ -16800,7 +16800,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -16813,7 +16813,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -17282,7 +17282,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -17892,7 +17892,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.0", - "syn 2.0.79", + "syn 2.0.82", "unicode-ident", ] @@ -18420,7 +18420,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -19782,7 +19782,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -19965,7 +19965,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "scale-info", - "syn 2.0.79", + "syn 2.0.82", "thiserror", ] @@ -20281,7 +20281,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -20306,9 +20306,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "indexmap 2.2.3", "itoa", @@ -20383,7 +20383,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -21354,7 +21354,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -21369,7 +21369,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -21708,7 +21708,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde 0.4.0", + "impl-serde 0.5.0", "itertools 0.11.0", "k256", "libsecp256k1", @@ -21979,7 +21979,7 @@ version = "0.1.0" dependencies = [ "quote 1.0.37", "sp-crypto-hashing 0.1.0", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -21990,7 +21990,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.79", + "syn 2.0.82", ] [[package]] @@ -22008,7 +22008,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk#82912acb33a9030c0ef3bf5 dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -22017,7 +22017,7 @@ version = "14.0.0" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -22028,7 +22028,7 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -22590,7 +22590,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -22602,7 +22602,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -22616,7 +22616,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -22824,7 +22824,7 @@ dependencies = [ name = "sp-storage" version = "19.0.0" dependencies = [ - "impl-serde 0.4.0", + "impl-serde 0.5.0", "parity-scale-codec", "ref-cast", "serde", @@ -23052,7 +23052,7 @@ dependencies = [ name = "sp-version" version = "29.0.0" dependencies = [ - "impl-serde 0.4.0", + "impl-serde 0.5.0", "parity-scale-codec", "parity-wasm", "scale-info", @@ -23091,7 +23091,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "sp-version 29.0.0", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -23103,7 +23103,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -23569,7 +23569,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "rustversion", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -23582,7 +23582,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "rustversion", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -24071,7 +24071,7 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.79", + "syn 2.0.82", "thiserror", "tokio", ] @@ -24134,7 +24134,7 @@ dependencies = [ "quote 1.0.37", "scale-typegen", "subxt-codegen", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -24287,9 +24287,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", @@ -24305,7 +24305,7 @@ dependencies = [ "paste", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -24334,7 +24334,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -24464,7 +24464,7 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -24639,7 +24639,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -24801,7 +24801,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -25077,7 +25077,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -25119,7 +25119,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -25771,7 +25771,7 @@ dependencies = [ "once_cell", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", "wasm-bindgen-shared", ] @@ -25805,7 +25805,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -26940,7 +26940,7 @@ dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", "staging-xcm", - "syn 2.0.79", + "syn 2.0.82", "trybuild", ] @@ -27111,7 +27111,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] @@ -27131,7 +27131,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.82", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 77586ae9d8e..363155fadf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -807,7 +807,7 @@ hyper-rustls = { version = "0.24.2" } hyper-util = { version = "0.1.5", default-features = false } # TODO: remove hyper v0.14 https://github.com/paritytech/polkadot-sdk/issues/4896 hyperv14 = { package = "hyper", version = "0.14.29", default-features = false } -impl-serde = { version = "0.4.0", default-features = false } +impl-serde = { version = "0.5.0", default-features = false } impl-trait-for-tuples = { version = "0.2.2" } indexmap = { version = "2.0.0" } indicatif = { version = "0.17.7" } @@ -1194,7 +1194,7 @@ separator = { version = "0.4.1" } serde = { version = "1.0.210", default-features = false } serde-big-array = { version = "0.3.2" } serde_derive = { version = "1.0.117" } -serde_json = { version = "1.0.128", default-features = false } +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" } @@ -1305,7 +1305,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.79" } +syn = { version = "2.0.82" } sysinfo = { version = "0.30" } tar = { version = "0.4" } tempfile = { version = "3.8.1" } -- GitLab From 95483a884dac76d077ffe2097c40dc21e548ae4d Mon Sep 17 00:00:00 2001 From: Andrii Date: Mon, 21 Oct 2024 16:54:21 +0300 Subject: [PATCH 16/50] Improved TrustedQueryAPI signatures (#6129) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed returned type of API methods from `Result` to a typed one `type XcmTrustedQueryResult = Result;` Follow-up of [PR-6039](https://github.com/paritytech/polkadot-sdk/pull/6039) --------- Co-authored-by: Bastian Köcher Co-authored-by: Adrian Catangiu --- .../assets/asset-hub-rococo/src/lib.rs | 6 ++-- .../assets/asset-hub-westend/src/lib.rs | 4 +-- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 4 +-- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 4 +-- .../collectives-westend/src/lib.rs | 4 +-- .../contracts/contracts-rococo/src/lib.rs | 4 +-- .../coretime/coretime-rococo/src/lib.rs | 4 +-- .../coretime/coretime-westend/src/lib.rs | 4 +-- .../runtimes/people/people-rococo/src/lib.rs | 4 +-- .../runtimes/people/people-westend/src/lib.rs | 4 +-- .../runtimes/testing/penpal/src/lib.rs | 4 +-- .../xcm/xcm-runtime-apis/src/trusted_query.rs | 7 ++-- prdoc/pr_6129.prdoc | 32 +++++++++++++++++++ 13 files changed, 59 insertions(+), 26 deletions(-) create mode 100644 prdoc/pr_6129.prdoc 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 64fdf488372..2f25fa0ec1d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -1103,8 +1103,6 @@ pub type Executive = frame_executive::Executive< Migrations, >; -type XcmTrustedQueryResult = Result; - #[cfg(feature = "runtime-benchmarks")] pub struct AssetConversionTxHelper; @@ -1877,10 +1875,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> XcmTrustedQueryResult { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> XcmTrustedQueryResult { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } 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 32d12174953..14f24228d0a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1976,10 +1976,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } 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 259b0355916..f63e1f8fcf6 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 @@ -1530,10 +1530,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } 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 85be26d1170..1d7cd5de40e 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 @@ -1346,10 +1346,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 030ed930ed5..8cb2e42cb31 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -1173,10 +1173,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 0111b3d8522..2fc3fe4f314 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -885,10 +885,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 1ce980fa549..f2ccb9c552e 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -1153,10 +1153,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 632f2c657cf..2f944e79fe0 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -1146,10 +1146,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index af8e72fa094..f9499f9d1eb 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -1064,10 +1064,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index d0b0bec2e87..7e3cd1670fe 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -1062,10 +1062,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 2dff159f7ee..136592c5602 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -1165,10 +1165,10 @@ impl_runtime_apis! { } impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_reserve(asset, location) } - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> xcm_runtime_apis::trusted_query::XcmTrustedQueryResult { PolkadotXcm::is_trusted_teleporter(asset, location) } } diff --git a/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs b/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs index a0c4416c831..e75af54ad2f 100644 --- a/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs +++ b/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs @@ -20,6 +20,9 @@ use codec::{Decode, Encode}; use frame_support::pallet_prelude::TypeInfo; use xcm::{VersionedAsset, VersionedLocation}; +/// Result of [`TrustedQueryApi`] functions. +pub type XcmTrustedQueryResult = Result; + sp_api::decl_runtime_apis! { // API for querying trusted reserves and trusted teleporters. pub trait TrustedQueryApi { @@ -28,13 +31,13 @@ sp_api::decl_runtime_apis! { /// # Arguments /// * `asset`: `VersionedAsset`. /// * `location`: `VersionedLocation`. - fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result; + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> XcmTrustedQueryResult; /// Returns if the asset can be teleported to the location. /// /// # Arguments /// * `asset`: `VersionedAsset`. /// * `location`: `VersionedLocation`. - fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result; + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> XcmTrustedQueryResult; } } diff --git a/prdoc/pr_6129.prdoc b/prdoc/pr_6129.prdoc new file mode 100644 index 00000000000..61719c213e8 --- /dev/null +++ b/prdoc/pr_6129.prdoc @@ -0,0 +1,32 @@ +# 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: "Improved TrustedQueryAPI signatures." + +doc: + - audience: Runtime Dev + description: | + Changed returned type of API methods from `Result` to a typed one: + `type XcmTrustedQueryResult = Result` + +crates: + - 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: people-rococo-runtime + bump: patch + - name: people-westend-runtime + bump: patch + - name: penpal-runtime + bump: patch -- GitLab From 9f515e024587648b8315c4a0e7feb81b2f91986e Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Mon, 21 Oct 2024 17:08:00 +0300 Subject: [PATCH 17/50] Fix and re-enable `zombienet-substrate-0002-validators-warp-sync` (#6154) Closes https://github.com/paritytech/polkadot-sdk/issues/5974 Fixed as per https://github.com/paritytech/polkadot-sdk/issues/5974#issuecomment-2426463359 --- .gitlab/pipeline/zombienet/substrate.yml | 6 +++--- .../test-validators-warp-sync.toml | 4 ++-- .../test-validators-warp-sync.zndsl | 8 +++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.gitlab/pipeline/zombienet/substrate.yml b/.gitlab/pipeline/zombienet/substrate.yml index 030a0a3f50a..52118307e6a 100644 --- a/.gitlab/pipeline/zombienet/substrate.yml +++ b/.gitlab/pipeline/zombienet/substrate.yml @@ -70,11 +70,11 @@ zombienet-substrate-0001-basic-warp-sync: --local-dir="${LOCAL_DIR}/0001-basic-warp-sync" --test="test-warp-sync.zndsl" -.zombienet-substrate-0002-validators-warp-sync: +zombienet-substrate-0002-validators-warp-sync: extends: - .zombienet-substrate-warp-sync-common before_script: - - !reference [.zombienet-substrate-warp-sync-common, before_script] + - !reference [ .zombienet-substrate-warp-sync-common, before_script ] - cp --remove-destination ${LOCAL_DIR}/0001-basic-warp-sync/chain-spec.json ${LOCAL_DIR}/0002-validators-warp-sync script: - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh @@ -85,7 +85,7 @@ zombienet-substrate-0003-block-building-warp-sync: extends: - .zombienet-substrate-warp-sync-common before_script: - - !reference [.zombienet-substrate-warp-sync-common, before_script] + - !reference [ .zombienet-substrate-warp-sync-common, before_script ] - cp --remove-destination ${LOCAL_DIR}/0001-basic-warp-sync/chain-spec.json ${LOCAL_DIR}/0003-block-building-warp-sync script: - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh diff --git a/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml b/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml index e388af7c94f..2f0fc7b9fe3 100644 --- a/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml +++ b/substrate/zombienet/0002-validators-warp-sync/test-validators-warp-sync.toml @@ -11,12 +11,12 @@ chain_spec_path = "chain-spec.json" [[relaychain.nodes]] name = "alice" validator = true - args = ["--sync warp"] + args = ["--log=beefy=debug", "--sync warp"] [[relaychain.nodes]] name = "bob" validator = true - args = ["--sync warp"] + args = ["--log=beefy=debug", "--sync warp"] # we need at least 3 nodes for warp sync [[relaychain.nodes]] 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 b68bce508c0..bc587b04477 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 @@ -31,11 +31,9 @@ bob: log line matches "Block history download is complete" within 120 seconds alice: reports block height is at least {{DB_BLOCK_HEIGHT}} within 10 seconds bob: reports block height is at least {{DB_BLOCK_HEIGHT}} within 10 seconds -alice: reports substrate_beefy_best_block is at least {{DB_BLOCK_HEIGHT}} within 180 seconds -bob: reports substrate_beefy_best_block is at least {{DB_BLOCK_HEIGHT}} within 180 seconds - -alice: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 180 seconds -bob: reports substrate_beefy_best_block is greater than {{DB_BLOCK_HEIGHT}} within 180 seconds +# In the worst case scenario, the validators should vote on 1 mandatory block each 6 seconds. And 1 era = 200 blocks. +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 bob: count of log lines containing "verification failed" is 0 within 10 seconds -- GitLab From 69b929f59a54538279807b9493af0d85feb223c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:44:18 +0000 Subject: [PATCH 18/50] Bump thiserror from 1.0.61 to 1.0.64 (#6143) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.61 to 1.0.64.
Release notes

Sourced from thiserror's releases.

1.0.64

  • Exclude derived impls from coverage instrumentation (#322, thanks @​oxalica)

1.0.63

  • Documentation improvements

1.0.62

  • Support referring to nested tuple struct fields inside #[error("…", …)] attribute (#309)
Commits
  • 84484bc Release 1.0.64
  • 023f036 Merge pull request #322 from oxalica/feat/mark-auto-derived
  • ae1f47e Mark #[automatically_derived] for generated impls
  • ab5b5e3 Upload CI Cargo.lock for reproducing failures
  • 00b3c14 Work around new dead code warning in test
  • 915c75e Release 1.0.63
  • 3d5ec25 Merge pull request #312 from dtolnay/backtracedoc
  • de8a1e5 Update documentation of #[from] and #[backtrace] attributes
  • 0bf6e3d Release 1.0.62
  • 4977932 Merge pull request #310 from dtolnay/nestedtuple
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=thiserror&package-manager=cargo&previous-version=1.0.61&new-version=1.0.64)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 864ba5e8047..5edc66847ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24604,9 +24604,9 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] @@ -24633,9 +24633,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2 1.0.86", "quote 1.0.37", diff --git a/Cargo.toml b/Cargo.toml index 363155fadf2..ecab9c30847 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1316,7 +1316,7 @@ test-parachain-halt = { path = "polkadot/parachain/test-parachains/halt" } test-parachain-undying = { path = "polkadot/parachain/test-parachains/undying" } test-runtime-constants = { path = "polkadot/runtime/test-runtime/constants", default-features = false } testnet-parachains-constants = { path = "cumulus/parachains/runtimes/constants", default-features = false } -thiserror = { version = "1.0.48" } +thiserror = { version = "1.0.64" } thousands = { version = "0.2.0" } threadpool = { version = "1.7" } tikv-jemalloc-ctl = { version = "0.5.0" } -- GitLab From 4387d0f8252ce71177f96cfaed97ea05ba08cc72 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:40:39 +0100 Subject: [PATCH 19/50] include more external links and resources (#5758) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher Co-authored-by: Shawn Tabrizi --- README.md | 6 +++--- docs/mermaid/IA.mmd | 4 ++-- docs/sdk/src/external_resources.rs | 14 ++++++++++++++ docs/sdk/src/lib.rs | 7 ++++--- docs/sdk/src/polkadot_sdk/mod.rs | 3 ++- 5 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 docs/sdk/src/external_resources.rs diff --git a/README.md b/README.md index 8016b6b3730..6c0dfbb2e7e 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ forks](https://img.shields.io/github/forks/paritytech/polkadot-sdk) ## ⚡ Quickstart If you want to get an example node running quickly you can execute the following getting started script: + ``` curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/scripts/getting-started.sh | bash ``` @@ -31,9 +32,8 @@ curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/paritytec * [Guides](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/guides/index.html), namely how to build your first FRAME pallet * [Templates](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/polkadot_sdk/templates/index.html) - for starting a new project -* Other resources: - * [Polkadot Wiki -> Build](https://wiki.polkadot.network/docs/build-guide) + for starting a new project. + * [External Resources](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/external_resources/index.html) ## 🚀 Releases diff --git a/docs/mermaid/IA.mmd b/docs/mermaid/IA.mmd index 37417497e1f..0f14e200df9 100644 --- a/docs/mermaid/IA.mmd +++ b/docs/mermaid/IA.mmd @@ -1,13 +1,13 @@ flowchart parity[paritytech.github.io] --> devhub[polkadot_sdk_docs] - polkadot_network[polkadot.network] --> devhub[polkadot_sdk_docs] devhub --> polkadot_sdk devhub --> reference_docs devhub --> guides + devhub --> external_resources polkadot_sdk --> substrate polkadot_sdk --> frame - polkadot_sdk --> cumulus polkadot_sdk --> polkadot[polkadot node] polkadot_sdk --> xcm + polkadot_sdk --> templates diff --git a/docs/sdk/src/external_resources.rs b/docs/sdk/src/external_resources.rs new file mode 100644 index 00000000000..939874d12f1 --- /dev/null +++ b/docs/sdk/src/external_resources.rs @@ -0,0 +1,14 @@ +//! # External Resources +//! +//! A non-exhaustive, un-opinionated list of external resources about Polkadot SDK. +//! +//! Unlike [`crate::guides`], or [`crate::polkadot_sdk::templates`] that contain material directly +//! maintained in the `polkadot-sdk` repository, the list of resources here are maintained by +//! third-parties, and are therefore subject to more variability. Any further resources may be added +//! by opening a pull request to the `polkadot-sdk` repository. +//! +//! - [Polkadot NFT Marketplace Tutorial by Polkadot Fellow Shawn Tabrizi](https://www.shawntabrizi.com/substrate-collectables-workshop/) +//! - [DOT Code School](https://dotcodeschool.com/) +//! - [Polkadot Developers](https://github.com/polkadot-developers/) +//! - [Polkadot Blockchain Academy](https://github.com/Polkadot-Blockchain-Academy) +//! - [Polkadot Wiki: Build](https://wiki.polkadot.network/docs/build-guide) diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index 35f73c290bf..86ca677d7ce 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -5,9 +5,6 @@ //! This crate is a *minimal*, but *always-accurate* source of information for those wishing to //! build on the Polkadot SDK. //! -//! > **Work in Progress**: This crate is under heavy development. Expect content to be moved and -//! > changed. Do not use links to this crate yet. See [`meta_contributing`] for more information. -//! //! ## Getting Started //! //! We suggest the following reading sequence: @@ -35,9 +32,13 @@ /// how one can contribute to it. pub mod meta_contributing; +/// A list of external resources and learning material about Polkadot SDK. +pub mod external_resources; + /// In-depth guides about the most common components of the Polkadot SDK. They are slightly more /// high level and broad than [`reference_docs`]. pub mod guides; + /// An introduction to the Polkadot SDK. Read this module to learn about the structure of the SDK, /// the tools that are provided as a part of it, and to gain a high level understanding of each. pub mod polkadot_sdk; diff --git a/docs/sdk/src/polkadot_sdk/mod.rs b/docs/sdk/src/polkadot_sdk/mod.rs index 32cad72fba7..c089b6729ce 100644 --- a/docs/sdk/src/polkadot_sdk/mod.rs +++ b/docs/sdk/src/polkadot_sdk/mod.rs @@ -106,9 +106,10 @@ //! A list of projects and tools in the blockchain ecosystem that one way or another use parts of //! the Polkadot SDK: //! -//! * [Polygon's spin-off, Avail](https://github.com/availproject/avail) +//! * [Avail](https://github.com/availproject/avail) //! * [Cardano Partner Chains](https://iohk.io/en/blog/posts/2023/11/03/partner-chains-are-coming-to-cardano/) //! * [Starknet's Madara Sequencer](https://github.com/keep-starknet-strange/madara) +//! * [Polymesh](https://polymesh.network/) //! //! [`substrate`]: crate::polkadot_sdk::substrate //! [`frame`]: crate::polkadot_sdk::frame_runtime -- GitLab From dbaa428ccc5ff33f48980f1a6ada7f454d2d9ce3 Mon Sep 17 00:00:00 2001 From: Javier Viola <363911+pepoviola@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:40:54 -0300 Subject: [PATCH 20/50] fix js oom `js-scripts` (#6139) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix `oom` failures (`FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory`), like: https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/7602589 https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/7602594 --------- Co-authored-by: Bastian Köcher --- .gitlab/pipeline/zombienet.yml | 1 + .gitlab/pipeline/zombienet/bridges.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.gitlab/pipeline/zombienet.yml b/.gitlab/pipeline/zombienet.yml index 08bfed2e24c..5aa37f783a0 100644 --- a/.gitlab/pipeline/zombienet.yml +++ b/.gitlab/pipeline/zombienet.yml @@ -9,6 +9,7 @@ RUN_IN_CI: "1" KUBERNETES_CPU_REQUEST: "512m" KUBERNETES_MEMORY_REQUEST: "1Gi" + NODE_OPTIONS: "--max-old-space-size=8192" timeout: 60m include: diff --git a/.gitlab/pipeline/zombienet/bridges.yml b/.gitlab/pipeline/zombienet/bridges.yml index 070bfc8472d..0472601a6a2 100644 --- a/.gitlab/pipeline/zombienet/bridges.yml +++ b/.gitlab/pipeline/zombienet/bridges.yml @@ -47,6 +47,8 @@ - cp -r /tmp/bridges-tests-run-*/bridge_hub_rococo_local_network/*.log ./zombienet-logs/ # copy logs of westend nodes - cp -r /tmp/bridges-tests-run-*/bridge_hub_westend_local_network/*.log ./zombienet-logs/ + tags: + - zombienet-polkadot-integration-test zombienet-bridges-0001-asset-transfer-works: extends: -- GitLab From a3bca4bb65fdbfef99b52b06181779c0f681d3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=B3nal=20Murray?= Date: Mon, 21 Oct 2024 19:19:22 +0200 Subject: [PATCH 21/50] [Coretime chain] Add high assignment count mitigation to testnets (#6022) Fixed in Polkadot and Kusama in https://github.com/polkadot-fellows/runtimes/pull/434 and this PR just adds to testnets. We can handle a maximum of 28 assignments inside one XCM, while it's possible to have 80 (if a region is interlaced 79 times). This can be chunked on the coretime chain side but currently the scheduler on the relay makes assumptions that means we can't send more than one chunk for a given core. This just truncates the additional assignments until we can extend the relay to support this. This means that the first 27 assignments are taken, the final 28th is used to pad with idle to complete the mask (the relay also assumes that every schedule is complete). Any other assignments are dropped. --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- .../src/tests/coretime_interface.rs | 2 +- .../src/tests/coretime_interface.rs | 2 +- .../coretime/coretime-rococo/src/coretime.rs | 30 ++++++++++++++++ .../coretime/coretime-westend/src/coretime.rs | 34 +++++++++++++++++-- prdoc/pr_6022.prdoc | 14 ++++++++ 5 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 prdoc/pr_6022.prdoc diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs index 584bce8f1df..9915b1753ef 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs @@ -46,7 +46,7 @@ fn transact_hardcoded_weights_are_sane() { // Create and populate schedule with the worst case assignment on this core. let mut schedule = Vec::new(); - for i in 0..27 { + for i in 0..80 { schedule.push(ScheduleItem { mask: CoreMask::void().set(i), assignment: CoreAssignment::Task(2000 + i), diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs index f61bc4285a0..00530f80b95 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs @@ -46,7 +46,7 @@ fn transact_hardcoded_weights_are_sane() { // Create and populate schedule with the worst case assignment on this core. let mut schedule = Vec::new(); - for i in 0..27 { + for i in 0..80 { schedule.push(ScheduleItem { mask: CoreMask::void().set(i), assignment: CoreAssignment::Task(2000 + i), diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs index 76ee06a87e8..3910a747e9b 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs @@ -218,6 +218,36 @@ impl CoretimeInterface for CoretimeAllocator { end_hint: Option>, ) { use crate::coretime::CoretimeProviderCalls::AssignCore; + + // 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. + + // This shadowing of `assignment` is temporary and can be removed when the relay can accept + // multiple messages to assign a single core. + let assignment = if assignment.len() > 28 { + let mut total_parts = 0u16; + // Account for missing parts with a new `Idle` assignment at the start as + // `assign_core` on the relay assumes this is sorted. We'll add the rest of the + // assignments and sum the parts in one pass, so this is just initialized to 0. + let mut assignment_truncated = vec![(CoreAssignment::Idle, 0)]; + // Truncate to first 27 non-idle assignments. + assignment_truncated.extend( + assignment + .into_iter() + .filter(|(a, _)| *a != CoreAssignment::Idle) + .take(27) + .inspect(|(_, parts)| total_parts += *parts) + .collect::>(), + ); + + // Set the parts of the `Idle` assignment we injected at the start of the vec above. + assignment_truncated[0].1 = 57_600u16.saturating_sub(total_parts); + assignment_truncated + } else { + assignment + }; + let assign_core_call = RelayRuntimePallets::Coretime(AssignCore(core, begin, assignment, end_hint)); diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs index 865ff68d4c6..86769cb2da1 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs @@ -224,8 +224,6 @@ impl CoretimeInterface for CoretimeAllocator { end_hint: Option>, ) { use crate::coretime::CoretimeProviderCalls::AssignCore; - let assign_core_call = - RelayRuntimePallets::Coretime(AssignCore(core, begin, assignment, end_hint)); // Weight for `assign_core` from westend benchmarks: // `ref_time` = 10177115 + (1 * 25000000) + (2 * 100000000) + (57600 * 13932) = 937660315 @@ -233,6 +231,38 @@ impl CoretimeInterface for CoretimeAllocator { // 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. + + // This shadowing of `assignment` is temporary and can be removed when the relay can accept + // multiple messages to assign a single core. + let assignment = if assignment.len() > 28 { + let mut total_parts = 0u16; + // Account for missing parts with a new `Idle` assignment at the start as + // `assign_core` on the relay assumes this is sorted. We'll add the rest of the + // assignments and sum the parts in one pass, so this is just initialized to 0. + let mut assignment_truncated = vec![(CoreAssignment::Idle, 0)]; + // Truncate to first 27 non-idle assignments. + assignment_truncated.extend( + assignment + .into_iter() + .filter(|(a, _)| *a != CoreAssignment::Idle) + .take(27) + .inspect(|(_, parts)| total_parts += *parts) + .collect::>(), + ); + + // Set the parts of the `Idle` assignment we injected at the start of the vec above. + assignment_truncated[0].1 = 57_600u16.saturating_sub(total_parts); + assignment_truncated + } else { + assignment + }; + + let assign_core_call = + RelayRuntimePallets::Coretime(AssignCore(core, begin, assignment, end_hint)); + let message = Xcm(vec![ Instruction::UnpaidExecution { weight_limit: WeightLimit::Unlimited, diff --git a/prdoc/pr_6022.prdoc b/prdoc/pr_6022.prdoc new file mode 100644 index 00000000000..804d46af661 --- /dev/null +++ b/prdoc/pr_6022.prdoc @@ -0,0 +1,14 @@ +title: '[Coretime chain] Add high assignment count mitigation to testnets' +doc: +- audience: Runtime User + description: | + We can handle a maximum of 28 assignments inside one XCM, while it's possible to have 80 (if a + region is interlaced 79 times). This can be chunked on the coretime chain side but currently the + relay does not support this. This PR truncates the additional assignments on Rococo and Westend + to mitigate this until the relay is fixed. The first 27 assignments are taken, the final 28th is + used to pad with idle to complete the mask. Any other assignments are dropped. +crates: +- name: coretime-rococo-runtime + bump: patch +- name: coretime-westend-runtime + bump: patch -- GitLab From 13d3f58e35c047b3bbcc6c2ef3bffeb5c11222b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 19:35:26 +0200 Subject: [PATCH 22/50] Bump prost-build from 0.12.4 to 0.13.2 (#6144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [prost-build](https://github.com/tokio-rs/prost) from 0.12.4 to 0.13.2.
Changelog

Sourced from prost-build's changelog.

Prost version 0.13.2

PROST! is a Protocol Buffers implementation for the Rust Language. prost generates simple, idiomatic Rust code from proto2 and proto3 files.

Features

  • prost-build: Add protoc executable path to Config (#1126)
  • prost-build: Extract file descriptor loading from compile_protos() (#1067)

Bug Fixes

  • prost-types: Fix date-time parsing (#1096)
  • prost-types: '+' is not a numeric digit (#1104)
  • prost-types: Converting DateTime to Timestamp is fallible (#1095)
  • prost-types: Parse timestamp with long second fraction (#1106)
  • prost-types: Format negative fractional duration (#1110)
  • prost-types: Allow unknown local time offset (#1109)

Styling

  • Remove use of legacy numeric constants (#1089)
  • Move encoding functions into separate modules (#1111)
  • Remove needless borrow (#1122)

Testing

  • Add tests for public interface of DecodeError (#1120)
  • Add parse_date fuzzing target (#1127)
  • Fix build without std (#1134)
  • Change some proptest to kani proofs (#1133)
  • Add parse_duration fuzzing target (#1129)
  • fuzz: Fix building of fuzzing targets (#1107)
  • fuzz: Add fuzz targets to workspace (#1117)

Miscellaneous Tasks

  • Move old protobuf benchmark into prost (#1100)
  • Remove allow clippy::derive_partial_eq_without_eq (#1115)
  • Run cargo test without all-targets (#1118)
  • dependabot: Add github actions (#1121)
  • Update to cargo clippy version 1.80 (#1128)

Build

  • Use proc-macro in Cargo.toml (#1102)
  • Ignore missing features in tests crates (#1101)
  • Use separated build directory for protobuf (#1103)
  • protobuf: Don't install unused test proto (#1116)
  • protobuf: Use crate cmake (#1137)
  • deps: Update devcontainer to Debian Bookworm release (#1114)

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=prost-build&package-manager=cargo&previous-version=0.12.4&new-version=0.13.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bastian Köcher --- Cargo.lock | 52 +++++++++++----------------------------------------- Cargo.toml | 2 +- 2 files changed, 12 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5edc66847ac..e2cce727856 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9265,7 +9265,7 @@ dependencies = [ "parking_lot 0.12.3", "pin-project", "prost 0.12.6", - "prost-build 0.13.2", + "prost-build", "rand", "rcgen", "ring 0.16.20", @@ -16735,27 +16735,6 @@ dependencies = [ "prost-derive 0.13.2", ] -[[package]] -name = "prost-build" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" -dependencies = [ - "bytes", - "heck 0.5.0", - "itertools 0.12.1", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost 0.12.6", - "prost-types 0.12.4", - "regex", - "syn 2.0.82", - "tempfile", -] - [[package]] name = "prost-build" version = "0.13.2" @@ -16763,15 +16742,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", - "heck 0.5.0", - "itertools 0.12.1", + "heck 0.4.1", + "itertools 0.10.5", "log", "multimap", "once_cell", "petgraph", "prettyplease", "prost 0.13.2", - "prost-types 0.13.2", + "prost-types", "regex", "syn 2.0.82", "tempfile", @@ -16797,7 +16776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", @@ -16810,21 +16789,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", ] -[[package]] -name = "prost-types" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" -dependencies = [ - "prost 0.12.6", -] - [[package]] name = "prost-types" version = "0.13.2" @@ -18322,7 +18292,7 @@ dependencies = [ "multihash 0.19.1", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.12.4", + "prost-build", "quickcheck", "rand", "sc-client-api", @@ -19138,7 +19108,7 @@ dependencies = [ "partial_sort", "pin-project", "prost 0.12.6", - "prost-build 0.12.4", + "prost-build", "rand", "sc-block-builder", "sc-client-api", @@ -19183,7 +19153,7 @@ dependencies = [ "futures", "libp2p-identity", "parity-scale-codec", - "prost-build 0.12.4", + "prost-build", "sc-consensus", "sc-network-types", "sp-consensus", @@ -19225,7 +19195,7 @@ dependencies = [ "log", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.12.4", + "prost-build", "sc-client-api", "sc-network", "sc-network-types", @@ -19268,7 +19238,7 @@ dependencies = [ "mockall 0.11.4", "parity-scale-codec", "prost 0.12.6", - "prost-build 0.12.4", + "prost-build", "quickcheck", "sc-block-builder", "sc-client-api", diff --git a/Cargo.toml b/Cargo.toml index ecab9c30847..049de32b54c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1092,7 +1092,7 @@ prometheus = { version = "0.13.0", default-features = false } prometheus-endpoint = { path = "substrate/utils/prometheus", default-features = false, package = "substrate-prometheus-endpoint" } prometheus-parse = { version = "0.2.2" } prost = { version = "0.12.4" } -prost-build = { version = "0.12.4" } +prost-build = { version = "0.13.2" } pyroscope = { version = "0.5.7" } pyroscope_pprofrs = { version = "0.2.7" } quick_cache = { version = "0.3" } -- GitLab From 225536c9f57c9ff4425aa94e275648ce334eb4cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:40:42 +0000 Subject: [PATCH 23/50] Bump the ci_dependencies group across 1 directory with 5 updates (#6035) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the ci_dependencies group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [lycheeverse/lychee-action](https://github.com/lycheeverse/lychee-action) | `1.10.0` | `2.0.1` | | [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) | `2.7.3` | `2.7.5` | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `3.6.1` | `3.7.1` | | [docker/build-push-action](https://github.com/docker/build-push-action) | `6.8.0` | `6.9.0` | | [actions-rust-lang/setup-rust-toolchain](https://github.com/actions-rust-lang/setup-rust-toolchain) | `1.10.0` | `1.10.1` | Updates `lycheeverse/lychee-action` from 1.10.0 to 2.0.1
Release notes

Sourced from lycheeverse/lychee-action's releases.

Version 2.0.1

What's Changed

New Contributors

Full Changelog: https://github.com/lycheeverse/lychee-action/compare/v2...v2.0.1

Version 2.0.0

Breaking Changes

Note: This release improves the action's robustness by changing default behaviors. Changes are only required if you want to opt out of the new failure conditions. Most users won't need to modify their existing configurations.

Fail pipeline on error by default

We've changed the default behavior: pipelines will now fail on broken links automatically. This addresses user feedback that not failing on broken links was unexpected (see [issue #71](lycheeverse/lychee-action#71)).

What you need to do:

  • Update to version 2 of this action to apply this change.
  • Users of the lychee-action@master branch don't need to make any changes, as fail: true has been the default there for a while.
  • If you prefer the old behavior, explicitly set fail to false when updating:
- name: Link Checker
  id: lychee
  uses: lycheeverse/lychee-action@v2
  with:
    fail: false  # Don't fail action on broken links

Fail pipeline if no links were found

Similar to the above change, we now fail the pipeline if no links are found during a run. This helps warn users about potential configuration issues.

What you need to do:

  • If you expect links to be found in your pipeline run, you don't need to do anything.
  • If you expect no links in your pipeline run, you can opt out like this:
- name: Link Checker
  id: lychee
  uses: lycheeverse/lychee-action@v2
  with:
    failIfEmpty: false  # Don't fail action if no links were found

For a more detailed description of the technical aspects behind these changes, please see the full changelog below.

... (truncated)

Commits

Updates `Swatinem/rust-cache` from 2.7.3 to 2.7.5
Release notes

Sourced from Swatinem/rust-cache's releases.

v2.7.5

What's Changed

New Contributors

Full Changelog: https://github.com/Swatinem/rust-cache/compare/v2.7.3...v2.7.5

Commits

Updates `docker/setup-buildx-action` from 3.6.1 to 3.7.1
Release notes

Sourced from docker/setup-buildx-action's releases.

v3.7.1

Full Changelog: https://github.com/docker/setup-buildx-action/compare/v3.7.0...v3.7.1

v3.7.0

Full Changelog: https://github.com/docker/setup-buildx-action/compare/v3.6.1...v3.7.0

Commits
  • c47758b Merge pull request #369 from crazy-max/revert-crypto
  • 8fea382 chore: update generated content
  • 2874e98 switch back to uuid package
  • 8026d2b Merge pull request #362 from docker/dependabot/npm_and_yarn/docker/actions-to...
  • e51aab5 chore: update generated content
  • fd7390e build(deps): bump @​docker/actions-toolkit from 0.35.0 to 0.39.0
  • 910a304 Merge pull request #366 from crazy-max/remove-uuid
  • 3623ee4 chore: update generated content
  • e0e5ecf remove uuid package and switch to crypto
  • 5334dd0 Merge pull request #363 from crazy-max/set-buildkitd-flags-optin
  • Additional commits viewable in compare view

Updates `docker/build-push-action` from 6.8.0 to 6.9.0
Release notes

Sourced from docker/build-push-action's releases.

v6.9.0

Full Changelog: https://github.com/docker/build-push-action/compare/v6.8.0...v6.9.0

Commits
  • 4f58ea7 Merge pull request #1234 from docker/dependabot/npm_and_yarn/docker/actions-t...
  • 49b5ea6 chore: update generated content
  • 13c9fdd chore(deps): Bump @​docker/actions-toolkit from 0.38.0 to 0.39.0
  • e44afff Merge pull request #1232 from docker/dependabot/npm_and_yarn/path-to-regexp-6...
  • 67ebad3 chore(deps): Bump path-to-regexp from 6.2.2 to 6.3.0
  • See full diff in compare view

Updates `actions-rust-lang/setup-rust-toolchain` from 1.10.0 to 1.10.1
Release notes

Sourced from actions-rust-lang/setup-rust-toolchain's releases.

v1.10.1

  • Fix problem matcher for rustfmt output. The format has changed since rust-lang/rustfmt#5971 and now follows the form "filename:line". Thanks to @​0xcypher02 for pointing out the problem.

Full Changelog: https://github.com/actions-rust-lang/setup-rust-toolchain/compare/v1...v1.10.1

Changelog

Sourced from actions-rust-lang/setup-rust-toolchain's changelog.

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[1.10.1] - 2024-10-01

  • Fix problem matcher for rustfmt output. The format has changed since rust-lang/rustfmt#5971 and now follows the form "filename:line". Thanks to @​0xcypher02 for pointing out the problem.

[1.10.0] - 2024-09-23

  • Add new parameter cache-directories that is propagated to Swatinem/rust-cache (#44 by @​pranc1ngpegasus)
  • Add new parameter cache-key that is propagated to Swatinem/rust-cache as key (#41 by @​iainlane)
  • Make rustup toolchain installation more robust in light of planned changes rust-lang/rustup#3635 and rust-lang/rustup#3985
  • Allow installing multiple Rust toolchains by specifying multiple versions in the toolchain input parameter.
  • Configure the rustup override behavior via the new override input. (#38)

[1.9.0] - 2024-06-08

  • Add extra argument cache-on-failure and forward it to Swatinem/rust-cache. (#39 by @​samuelhnrq)
    Set the default the value to true. This will result in more caching than previously. This helps when large dependencies are compiled only for testing to fail.

[1.8.0] - 2024-01-13

  • Allow specifying subdirectories for cache.
  • Fix toolchain file overriding.

[1.7.0] - 2024-01-11

  • Allow overriding the toolchain file with explicit toolchain input. (#26)

[1.6.0] - 2023-12-04

Added

  • Allow disabling problem matchers (#27) This can be useful when having a matrix of jobs, that produce the same errors.

[1.5.0] - 2023-05-29

Added

... (truncated)

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bastian Köcher Co-authored-by: Oliver Tale-Yazdi --- .github/workflows/check-links.yml | 2 +- .github/workflows/check-semver.yml | 2 +- .github/workflows/publish-check-crates.yml | 2 +- .github/workflows/publish-claim-crates.yml | 2 +- .github/workflows/release-50_publish-docker.yml | 4 ++-- .github/workflows/tests-misc.yml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index baf8cd5b155..3bbb9baba46 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@2b973e86fc7b1f6b36a93795fe2c9c6ae1118621 # for v1.9.1 (10. Jan 2024) + uses: lycheeverse/lychee-action@2bb232618be239862e31382c5c0eaeba12e5e966 # 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 24050d9e098..65f3339b7ac 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -62,7 +62,7 @@ jobs: echo "PRDOC_EXTRA_ARGS=--max-bump minor" >> $GITHUB_ENV - name: Rust Cache - uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 + uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 with: cache-on-failure: true diff --git a/.github/workflows/publish-check-crates.yml b/.github/workflows/publish-check-crates.yml index a5af0411857..3fad3b64147 100644 --- a/.github/workflows/publish-check-crates.yml +++ b/.github/workflows/publish-check-crates.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Rust Cache - uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 + uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 with: cache-on-failure: true diff --git a/.github/workflows/publish-claim-crates.yml b/.github/workflows/publish-claim-crates.yml index f9bc6ce4dae..37bf06bb82d 100644 --- a/.github/workflows/publish-claim-crates.yml +++ b/.github/workflows/publish-claim-crates.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Rust Cache - uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 + uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 with: cache-on-failure: true diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index 1fe68441a62..6e0e8f20aa5 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -322,7 +322,7 @@ jobs: uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 - name: Cache Docker layers uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -346,7 +346,7 @@ jobs: - name: Build and push id: docker_build - uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 # v6.8.0 + uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 with: push: true file: docker/dockerfiles/polkadot/polkadot_injected_debian.Dockerfile diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml index b51f2c249d8..cca32650b10 100644 --- a/.github/workflows/tests-misc.yml +++ b/.github/workflows/tests-misc.yml @@ -348,7 +348,7 @@ jobs: - name: Set up Homebrew uses: Homebrew/actions/setup-homebrew@1ccc07ccd54b6048295516a3eb89b192c35057dc # master from 12.09.2024 - name: Install rust ${{ env.RUST_VERSION }} - uses: actions-rust-lang/setup-rust-toolchain@4d1965c9142484e48d40c19de54b5cba84953a06 # v1.10.0 + uses: actions-rust-lang/setup-rust-toolchain@11df97af8e8102fd60b60a77dfbf58d40cd843b8 # v1.10.1 with: cache: false toolchain: ${{ env.RUST_VERSION }} -- GitLab From ee803b74056fac5101c06ec5998586fa6eaac470 Mon Sep 17 00:00:00 2001 From: Alin Dima Date: Mon, 21 Oct 2024 22:05:03 +0300 Subject: [PATCH 24/50] runtime: remove ttl (#5461) Resolves https://github.com/paritytech/polkadot-sdk/issues/4776 This will enable proper core-sharing between paras, even if one of them is not producing blocks. TODO: - [x] duplicate first entry in the claim queue if the queue used to be empty - [x] don't back anything if at the end of the block there'll be a session change - [x] write migration for removing the availability core storage - [x] update and write unit tests - [x] prdoc - [x] add zombienet test for synchronous backing - [x] add zombienet test for core-sharing paras where one of them is not producing any blocks _Important note:_ The `ttl` and `max_availability_timeouts` fields of the HostConfiguration are not removed in this PR, due to #64. Adding the workaround with the storage version check for every use of the active HostConfiguration in all runtime APIs would be insane, as it's used in almost all runtime APIs. So even though the ttl and max_availability_timeouts fields will now be unused, they will remain part of the host configuration. These will be removed in a separate PR once #64 is fixed. Tracked by https://github.com/paritytech/polkadot-sdk/issues/6067 --------- Signed-off-by: Andrei Sandu Co-authored-by: Andrei Sandu Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Co-authored-by: command-bot <> --- .gitlab/pipeline/zombienet/polkadot.yml | 19 + polkadot/primitives/src/v8/mod.rs | 11 +- .../parachains/src/assigner_coretime/mod.rs | 9 +- .../parachains/src/assigner_coretime/tests.rs | 3 +- .../parachains/src/assigner_parachains.rs | 4 +- .../src/assigner_parachains/tests.rs | 3 +- polkadot/runtime/parachains/src/builder.rs | 131 +-- .../runtime/parachains/src/configuration.rs | 33 +- .../src/configuration/migration/v12.rs | 12 +- .../parachains/src/configuration/tests.rs | 8 +- .../parachains/src/coretime/migration.rs | 5 +- polkadot/runtime/parachains/src/disputes.rs | 8 + .../runtime/parachains/src/inclusion/mod.rs | 74 +- .../runtime/parachains/src/inclusion/tests.rs | 80 +- .../runtime/parachains/src/initializer.rs | 20 +- polkadot/runtime/parachains/src/mock.rs | 21 +- .../runtime/parachains/src/on_demand/mod.rs | 5 + .../runtime/parachains/src/on_demand/tests.rs | 3 +- .../src/paras_inherent/benchmarking.rs | 3 +- .../parachains/src/paras_inherent/mod.rs | 287 +++-- .../parachains/src/paras_inherent/tests.rs | 430 ++++--- .../parachains/src/runtime_api_impl/v11.rs | 120 +- polkadot/runtime/parachains/src/scheduler.rs | 517 ++------- .../parachains/src/scheduler/common.rs | 9 +- .../parachains/src/scheduler/migration.rs | 88 +- .../runtime/parachains/src/scheduler/tests.rs | 1014 +++++------------ .../runtime/parachains/src/session_info.rs | 6 +- polkadot/runtime/parachains/src/shared.rs | 6 + .../runtime/parachains/src/shared/tests.rs | 18 +- polkadot/runtime/rococo/src/lib.rs | 3 +- ...kadot_runtime_parachains_paras_inherent.rs | 155 ++- polkadot/runtime/test-runtime/src/lib.rs | 74 +- polkadot/runtime/westend/src/lib.rs | 1 + ...kadot_runtime_parachains_paras_inherent.rs | 153 ++- .../0015-coretime-shared-core.zndsl | 4 +- .../functional/0017-sync-backing.toml | 48 + .../functional/0017-sync-backing.zndsl | 22 + .../0018-shared-core-idle-parachain.toml | 39 + .../0018-shared-core-idle-parachain.zndsl | 11 + ...ister-paras.js => force-register-paras.js} | 0 prdoc/pr_5461.prdoc | 20 + 41 files changed, 1510 insertions(+), 1967 deletions(-) create mode 100644 polkadot/zombienet_tests/functional/0017-sync-backing.toml create mode 100644 polkadot/zombienet_tests/functional/0017-sync-backing.zndsl create mode 100644 polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.toml create mode 100644 polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.zndsl rename polkadot/zombienet_tests/functional/{0015-force-register-paras.js => force-register-paras.js} (100%) create mode 100644 prdoc/pr_5461.prdoc diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index d1738083994..60870caf26c 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -233,6 +233,25 @@ zombienet-polkadot-functional-0016-approval-voting-parallel: --local-dir="${LOCAL_DIR}/functional" --test="0016-approval-voting-parallel.zndsl" +zombienet-polkadot-functional-0017-sync-backing: + extends: + - .zombienet-polkadot-common + script: + - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh + --local-dir="${LOCAL_DIR}/functional" + --test="0017-sync-backing.zndsl" + +zombienet-polkadot-functional-0018-shared-core-idle-parachain: + extends: + - .zombienet-polkadot-common + before_script: + - !reference [ .zombienet-polkadot-common, before_script ] + - cp --remove-destination ${LOCAL_DIR}/assign-core.js ${LOCAL_DIR}/functional + script: + - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh + --local-dir="${LOCAL_DIR}/functional" + --test="0018-shared-core-idle-parachain.zndsl" + zombienet-polkadot-smoke-0001-parachains-smoke-test: extends: - .zombienet-polkadot-common diff --git a/polkadot/primitives/src/v8/mod.rs b/polkadot/primitives/src/v8/mod.rs index a51ee0bd99b..cca327df42c 100644 --- a/polkadot/primitives/src/v8/mod.rs +++ b/polkadot/primitives/src/v8/mod.rs @@ -2093,7 +2093,9 @@ pub struct SchedulerParams { pub lookahead: u32, /// How many cores are managed by the coretime chain. pub num_cores: u32, - /// The max number of times a claim can time out in availability. + /// Deprecated and no longer used by the runtime. + /// Removal is tracked by . + #[deprecated] pub max_availability_timeouts: u32, /// The maximum queue size of the pay as you go module. pub on_demand_queue_max_size: u32, @@ -2104,13 +2106,14 @@ pub struct SchedulerParams { pub on_demand_fee_variability: Perbill, /// The minimum amount needed to claim a slot in the spot pricing queue. pub on_demand_base_fee: Balance, - /// The number of blocks a claim stays in the scheduler's claim queue before getting cleared. - /// This number should go reasonably higher than the number of blocks in the async backing - /// lookahead. + /// Deprecated and no longer used by the runtime. + /// Removal is tracked by . + #[deprecated] pub ttl: BlockNumber, } impl> Default for SchedulerParams { + #[allow(deprecated)] fn default() -> Self { Self { group_rotation_frequency: 1u32.into(), diff --git a/polkadot/runtime/parachains/src/assigner_coretime/mod.rs b/polkadot/runtime/parachains/src/assigner_coretime/mod.rs index 7ee76600b42..33a36a1bb2e 100644 --- a/polkadot/runtime/parachains/src/assigner_coretime/mod.rs +++ b/polkadot/runtime/parachains/src/assigner_coretime/mod.rs @@ -318,9 +318,12 @@ impl AssignmentProvider> for Pallet { Assignment::Bulk(para_id) } - fn session_core_count() -> u32 { - let config = configuration::ActiveConfig::::get(); - config.scheduler_params.num_cores + fn assignment_duplicated(assignment: &Assignment) { + match assignment { + Assignment::Pool { para_id, core_index } => + on_demand::Pallet::::assignment_duplicated(*para_id, *core_index), + Assignment::Bulk(_) => {}, + } } } diff --git a/polkadot/runtime/parachains/src/assigner_coretime/tests.rs b/polkadot/runtime/parachains/src/assigner_coretime/tests.rs index e7994b8ef82..25007f0eed6 100644 --- a/polkadot/runtime/parachains/src/assigner_coretime/tests.rs +++ b/polkadot/runtime/parachains/src/assigner_coretime/tests.rs @@ -26,7 +26,6 @@ use crate::{ paras::{ParaGenesisArgs, ParaKind}, scheduler::common::Assignment, }; -use alloc::collections::btree_map::BTreeMap; use frame_support::{assert_noop, assert_ok, pallet_prelude::*, traits::Currency}; use pallet_broker::TaskId; use polkadot_primitives::{BlockNumber, Id as ParaId, SessionIndex, ValidationCode}; @@ -78,7 +77,7 @@ fn run_to_block( OnDemand::on_initialize(b + 1); // In the real runtime this is expected to be called by the `InclusionInherent` pallet. - Scheduler::free_cores_and_fill_claim_queue(BTreeMap::new(), b + 1); + Scheduler::advance_claim_queue(&Default::default()); } } diff --git a/polkadot/runtime/parachains/src/assigner_parachains.rs b/polkadot/runtime/parachains/src/assigner_parachains.rs index 3c735b999cf..53edae5c32f 100644 --- a/polkadot/runtime/parachains/src/assigner_parachains.rs +++ b/polkadot/runtime/parachains/src/assigner_parachains.rs @@ -63,7 +63,5 @@ impl AssignmentProvider> for Pallet { Assignment::Bulk(para_id) } - fn session_core_count() -> u32 { - paras::Parachains::::decode_len().unwrap_or(0) as u32 - } + fn assignment_duplicated(_: &Assignment) {} } diff --git a/polkadot/runtime/parachains/src/assigner_parachains/tests.rs b/polkadot/runtime/parachains/src/assigner_parachains/tests.rs index 817e43a7138..6e8e185bb48 100644 --- a/polkadot/runtime/parachains/src/assigner_parachains/tests.rs +++ b/polkadot/runtime/parachains/src/assigner_parachains/tests.rs @@ -23,7 +23,6 @@ use crate::{ }, paras::{ParaGenesisArgs, ParaKind}, }; -use alloc::collections::btree_map::BTreeMap; use frame_support::{assert_ok, pallet_prelude::*}; use polkadot_primitives::{BlockNumber, Id as ParaId, SessionIndex, ValidationCode}; @@ -71,7 +70,7 @@ fn run_to_block( Scheduler::initializer_initialize(b + 1); // In the real runtime this is expected to be called by the `InclusionInherent` pallet. - Scheduler::free_cores_and_fill_claim_queue(BTreeMap::new(), b + 1); + Scheduler::advance_claim_queue(&Default::default()); } } diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index 1654590d109..fa9497f8ccd 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -18,7 +18,10 @@ use crate::{ configuration, inclusion, initializer, paras, paras::ParaKind, paras_inherent, - scheduler::{self, common::AssignmentProvider, CoreOccupied, ParasEntry}, + scheduler::{ + self, + common::{Assignment, AssignmentProvider}, + }, session_info, shared, }; use alloc::{ @@ -138,8 +141,6 @@ pub(crate) struct BenchBuilder { /// Make every candidate include a code upgrade by setting this to `Some` where the interior /// value is the byte length of the new code. code_upgrade: Option, - /// Specifies whether the claimqueue should be filled. - fill_claimqueue: bool, /// Cores which should not be available when being populated with pending candidates. unavailable_cores: Vec, /// Use v2 candidate descriptor. @@ -178,7 +179,6 @@ impl BenchBuilder { backed_in_inherent_paras: Default::default(), elastic_paras: Default::default(), code_upgrade: None, - fill_claimqueue: true, unavailable_cores: vec![], candidate_descriptor_v2: false, candidate_modifier: None, @@ -322,13 +322,6 @@ impl BenchBuilder { self.max_validators() / self.max_validators_per_core() } - /// Set whether the claim queue should be filled. - #[cfg(not(feature = "runtime-benchmarks"))] - pub(crate) fn set_fill_claimqueue(mut self, f: bool) -> Self { - self.fill_claimqueue = f; - self - } - /// Get the minimum number of validity votes in order for a backed candidate to be included. #[cfg(feature = "runtime-benchmarks")] pub(crate) fn fallback_min_backing_votes() -> u32 { @@ -340,10 +333,13 @@ impl BenchBuilder { HeadData(vec![0xFF; max_head_size as usize]) } - fn candidate_descriptor_mock(candidate_descriptor_v2: bool) -> CandidateDescriptorV2 { + fn candidate_descriptor_mock( + para_id: ParaId, + candidate_descriptor_v2: bool, + ) -> CandidateDescriptorV2 { if candidate_descriptor_v2 { CandidateDescriptorV2::new( - 0.into(), + para_id, Default::default(), CoreIndex(200), 2, @@ -356,7 +352,7 @@ impl BenchBuilder { } else { // Convert v1 to v2. CandidateDescriptor:: { - para_id: 0.into(), + para_id, relay_parent: Default::default(), collator: junk_collator(), persisted_validation_data_hash: Default::default(), @@ -373,6 +369,7 @@ impl BenchBuilder { /// Create a mock of `CandidatePendingAvailability`. fn candidate_availability_mock( + para_id: ParaId, group_idx: GroupIndex, core_idx: CoreIndex, candidate_hash: CandidateHash, @@ -381,15 +378,16 @@ impl BenchBuilder { candidate_descriptor_v2: bool, ) -> inclusion::CandidatePendingAvailability> { inclusion::CandidatePendingAvailability::>::new( - core_idx, // core - candidate_hash, // hash - Self::candidate_descriptor_mock(candidate_descriptor_v2), // candidate descriptor - commitments, // commitments - availability_votes, // availability votes - Default::default(), // backers - Zero::zero(), // relay parent - One::one(), /* relay chain block this - * was backed in */ + core_idx, // core + candidate_hash, // hash + Self::candidate_descriptor_mock(para_id, candidate_descriptor_v2), /* candidate descriptor */ + commitments, // commitments + availability_votes, /* availability + * votes */ + Default::default(), // backers + Zero::zero(), // relay parent + One::one(), /* relay chain block this + * was backed in */ group_idx, // backing group ) } @@ -416,6 +414,7 @@ impl BenchBuilder { hrmp_watermark: 0u32.into(), }; let candidate_availability = Self::candidate_availability_mock( + para_id, group_idx, core_idx, candidate_hash, @@ -886,14 +885,11 @@ impl BenchBuilder { extra_cores; assert!(used_cores <= max_cores); - let fill_claimqueue = self.fill_claimqueue; // NOTE: there is an n+2 session delay for these actions to take effect. // We are currently in Session 0, so these changes will take effect in Session 2. Self::setup_para_ids(used_cores - extra_cores); - configuration::ActiveConfig::::mutate(|c| { - c.scheduler_params.num_cores = used_cores as u32; - }); + configuration::Pallet::::set_coretime_cores_unchecked(used_cores as u32).unwrap(); let validator_ids = generate_validator_pairs::(self.max_validators()); let target_session = SessionIndex::from(self.target_session); @@ -902,7 +898,7 @@ impl BenchBuilder { let bitfields = builder.create_availability_bitfields( &builder.backed_and_concluding_paras, &builder.elastic_paras, - used_cores, + scheduler::Pallet::::num_availability_cores(), ); let mut backed_in_inherent = BTreeMap::new(); @@ -930,66 +926,57 @@ impl BenchBuilder { assert_eq!(inclusion::PendingAvailability::::iter().count(), used_cores - extra_cores); - // Mark all the used cores as occupied. We expect that there are - // `backed_and_concluding_paras` that are pending availability and that there are - // `used_cores - backed_and_concluding_paras ` which are about to be disputed. - let now = frame_system::Pallet::::block_number() + One::one(); - + // Sanity check that the occupied cores reported by the inclusion module are what we expect + // to be. let mut core_idx = 0u32; let elastic_paras = &builder.elastic_paras; - // Assign potentially multiple cores to same parachains, - let cores = all_cores + + let mut occupied_cores = inclusion::Pallet::::get_occupied_cores() + .map(|(core, candidate)| (core, candidate.candidate_descriptor().para_id())) + .collect::>(); + occupied_cores.sort_by(|(core_a, _), (core_b, _)| core_a.0.cmp(&core_b.0)); + + let mut expected_cores = all_cores .iter() .flat_map(|(para_id, _)| { (0..elastic_paras.get(¶_id).cloned().unwrap_or(1)) .map(|_para_local_core_idx| { - let ttl = configuration::ActiveConfig::::get().scheduler_params.ttl; - // Load an assignment into provider so that one is present to pop - let assignment = - ::AssignmentProvider::get_mock_assignment( - CoreIndex(core_idx), - ParaId::from(*para_id), - ); + let old_core_idx = core_idx; core_idx += 1; - CoreOccupied::Paras(ParasEntry::new(assignment, now + ttl)) + (CoreIndex(old_core_idx), ParaId::from(*para_id)) }) - .collect::>>() + .collect::>() }) - .collect::>>(); + .collect::>(); - scheduler::AvailabilityCores::::set(cores); + expected_cores.sort_by(|(core_a, _), (core_b, _)| core_a.0.cmp(&core_b.0)); - core_idx = 0u32; + assert_eq!(expected_cores, occupied_cores); // We need entries in the claim queue for those: all_cores.append(&mut builder.backed_in_inherent_paras.clone()); - if fill_claimqueue { - let cores = all_cores - .keys() - .flat_map(|para_id| { - (0..elastic_paras.get(¶_id).cloned().unwrap_or(1)) - .map(|_para_local_core_idx| { - let ttl = configuration::ActiveConfig::::get().scheduler_params.ttl; - // Load an assignment into provider so that one is present to pop - let assignment = - ::AssignmentProvider::get_mock_assignment( - CoreIndex(core_idx), - ParaId::from(*para_id), - ); - - core_idx += 1; - ( - CoreIndex(core_idx - 1), - [ParasEntry::new(assignment, now + ttl)].into(), - ) - }) - .collect::>)>>() - }) - .collect::>>>(); + let mut core_idx = 0u32; + let cores = all_cores + .keys() + .flat_map(|para_id| { + (0..elastic_paras.get(¶_id).cloned().unwrap_or(1)) + .map(|_para_local_core_idx| { + // Load an assignment into provider so that one is present to pop + let assignment = + ::AssignmentProvider::get_mock_assignment( + CoreIndex(core_idx), + ParaId::from(*para_id), + ); - scheduler::ClaimQueue::::set(cores); - } + core_idx += 1; + (CoreIndex(core_idx - 1), [assignment].into()) + }) + .collect::)>>() + }) + .collect::>>(); + + scheduler::ClaimQueue::::set(cores); Bench:: { data: ParachainsInherentData { diff --git a/polkadot/runtime/parachains/src/configuration.rs b/polkadot/runtime/parachains/src/configuration.rs index 36888247580..e5cf7c4d276 100644 --- a/polkadot/runtime/parachains/src/configuration.rs +++ b/polkadot/runtime/parachains/src/configuration.rs @@ -337,8 +337,6 @@ pub enum InconsistentError { ZeroMinimumBackingVotes, /// `executor_params` are inconsistent. InconsistentExecutorParams { inner: ExecutorParamError }, - /// TTL should be bigger than lookahead - LookaheadExceedsTTL, /// Lookahead is zero, while it must be at least 1 for parachains to work. LookaheadZero, /// Passed in queue size for on-demand was too large. @@ -434,10 +432,6 @@ where return Err(InconsistentExecutorParams { inner }) } - if self.scheduler_params.ttl < self.scheduler_params.lookahead.into() { - return Err(LookaheadExceedsTTL) - } - if self.scheduler_params.lookahead == 0 { return Err(LookaheadZero) } @@ -686,18 +680,7 @@ pub mod pallet { Self::set_coretime_cores_unchecked(new) } - /// Set the max number of times a claim may timeout on a core before it is abandoned - #[pallet::call_index(7)] - #[pallet::weight(( - T::WeightInfo::set_config_with_u32(), - DispatchClass::Operational, - ))] - pub fn set_max_availability_timeouts(origin: OriginFor, new: u32) -> DispatchResult { - ensure_root(origin)?; - Self::schedule_config_update(|config| { - config.scheduler_params.max_availability_timeouts = new; - }) - } + // Call index 7 used to be `set_max_availability_timeouts`, which was removed. /// Set the parachain validator-group rotation frequency #[pallet::call_index(8)] @@ -1193,18 +1176,8 @@ pub mod pallet { config.scheduler_params.on_demand_target_queue_utilization = new; }) } - /// Set the on demand (parathreads) ttl in the claimqueue. - #[pallet::call_index(51)] - #[pallet::weight(( - T::WeightInfo::set_config_with_block_number(), - DispatchClass::Operational - ))] - pub fn set_on_demand_ttl(origin: OriginFor, new: BlockNumberFor) -> DispatchResult { - ensure_root(origin)?; - Self::schedule_config_update(|config| { - config.scheduler_params.ttl = new; - }) - } + + // Call index 51 used to be `set_on_demand_ttl`, which was removed. /// Set the minimum backing votes threshold. #[pallet::call_index(52)] diff --git a/polkadot/runtime/parachains/src/configuration/migration/v12.rs b/polkadot/runtime/parachains/src/configuration/migration/v12.rs index 111b1a19996..d1e0cf10a0f 100644 --- a/polkadot/runtime/parachains/src/configuration/migration/v12.rs +++ b/polkadot/runtime/parachains/src/configuration/migration/v12.rs @@ -143,6 +143,7 @@ fn migrate_to_v12() -> Weight { minimum_backing_votes : pre.minimum_backing_votes, node_features : pre.node_features, approval_voting_params : pre.approval_voting_params, + #[allow(deprecated)] scheduler_params: SchedulerParams { group_rotation_frequency : pre.group_rotation_frequency, paras_availability_period : pre.paras_availability_period, @@ -231,7 +232,10 @@ mod tests { assert_eq!(v12.scheduler_params.paras_availability_period, 4); assert_eq!(v12.scheduler_params.lookahead, 1); assert_eq!(v12.scheduler_params.num_cores, 1); - assert_eq!(v12.scheduler_params.max_availability_timeouts, 0); + #[allow(deprecated)] + { + assert_eq!(v12.scheduler_params.max_availability_timeouts, 0); + } assert_eq!(v12.scheduler_params.on_demand_queue_max_size, 10_000); assert_eq!( v12.scheduler_params.on_demand_target_queue_utilization, @@ -239,7 +243,10 @@ mod tests { ); assert_eq!(v12.scheduler_params.on_demand_fee_variability, Perbill::from_percent(3)); assert_eq!(v12.scheduler_params.on_demand_base_fee, 10_000_000); - assert_eq!(v12.scheduler_params.ttl, 5); + #[allow(deprecated)] + { + assert_eq!(v12.scheduler_params.ttl, 5); + } } #[test] @@ -282,6 +289,7 @@ mod tests { for (_, v12) in configs_to_check { #[rustfmt::skip] + #[allow(deprecated)] { assert_eq!(v11.max_code_size , v12.max_code_size); assert_eq!(v11.max_head_data_size , v12.max_head_data_size); diff --git a/polkadot/runtime/parachains/src/configuration/tests.rs b/polkadot/runtime/parachains/src/configuration/tests.rs index 0d20399e471..a8689a04fe0 100644 --- a/polkadot/runtime/parachains/src/configuration/tests.rs +++ b/polkadot/runtime/parachains/src/configuration/tests.rs @@ -316,13 +316,14 @@ fn setting_pending_config_members() { approval_voting_params: ApprovalVotingParams { max_approval_coalesce_count: 1 }, minimum_backing_votes: 5, node_features: bitvec![u8, Lsb0; 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + #[allow(deprecated)] scheduler_params: SchedulerParams { group_rotation_frequency: 20, paras_availability_period: 10, max_validators_per_core: None, lookahead: 3, num_cores: 2, - max_availability_timeouts: 5, + max_availability_timeouts: 0, on_demand_queue_max_size: 10_000u32, on_demand_base_fee: 10_000_000u128, on_demand_fee_variability: Perbill::from_percent(3), @@ -355,11 +356,6 @@ fn setting_pending_config_members() { new_config.scheduler_params.num_cores, ) .unwrap(); - Configuration::set_max_availability_timeouts( - RuntimeOrigin::root(), - new_config.scheduler_params.max_availability_timeouts, - ) - .unwrap(); Configuration::set_group_rotation_frequency( RuntimeOrigin::root(), new_config.scheduler_params.group_rotation_frequency, diff --git a/polkadot/runtime/parachains/src/coretime/migration.rs b/polkadot/runtime/parachains/src/coretime/migration.rs index d4be135aad6..52189be3d24 100644 --- a/polkadot/runtime/parachains/src/coretime/migration.rs +++ b/polkadot/runtime/parachains/src/coretime/migration.rs @@ -19,8 +19,6 @@ pub use v_coretime::{GetLegacyLease, MigrateToCoretime}; mod v_coretime { - #[cfg(feature = "try-runtime")] - use crate::scheduler::common::AssignmentProvider; use crate::{ assigner_coretime, configuration, coretime::{mk_coretime_call, Config, PartsOf57600, WeightInfo}, @@ -142,7 +140,8 @@ mod v_coretime { let dmp_queue_size = crate::dmp::Pallet::::dmq_contents(T::BrokerId::get().into()).len() as u32; - let new_core_count = assigner_coretime::Pallet::::session_core_count(); + let config = configuration::ActiveConfig::::get(); + let new_core_count = config.scheduler_params.num_cores; ensure!(new_core_count == prev_core_count, "Total number of cores need to not change."); ensure!( dmp_queue_size > prev_dmp_queue_size, diff --git a/polkadot/runtime/parachains/src/disputes.rs b/polkadot/runtime/parachains/src/disputes.rs index f86573dadf5..d5a3f31e594 100644 --- a/polkadot/runtime/parachains/src/disputes.rs +++ b/polkadot/runtime/parachains/src/disputes.rs @@ -1309,3 +1309,11 @@ fn check_signature( res } + +#[cfg(all(not(feature = "runtime-benchmarks"), test))] +// Test helper for clearing the on-chain dispute data. +pub(crate) fn clear_dispute_storage() { + let _ = Disputes::::clear(u32::MAX, None); + let _ = BackersOnDisputes::::clear(u32::MAX, None); + let _ = Included::::clear(u32::MAX, None); +} diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 36f874b8db1..ea3a5d3cdda 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -50,7 +50,7 @@ use polkadot_primitives::{ CandidateReceiptV2 as CandidateReceipt, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, }, - well_known_keys, CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, Hash, HeadData, + well_known_keys, CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, HeadData, Id as ParaId, SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId, ValidatorIndex, ValidityAttestation, }; @@ -161,16 +161,6 @@ impl CandidatePendingAvailability { self.relay_parent_number.clone() } - /// Get the candidate backing group. - pub(crate) fn backing_group(&self) -> GroupIndex { - self.backing_group - } - - /// Get the candidate's backers. - pub(crate) fn backers(&self) -> &BitVec { - &self.backers - } - #[cfg(any(feature = "runtime-benchmarks", test))] pub(crate) fn new( core: CoreIndex, @@ -207,24 +197,6 @@ pub trait RewardValidators { fn reward_bitfields(validators: impl IntoIterator); } -/// Helper return type for `process_candidates`. -#[derive(Encode, Decode, PartialEq, TypeInfo)] -#[cfg_attr(test, derive(Debug))] -pub(crate) struct ProcessedCandidates { - pub(crate) core_indices: Vec<(CoreIndex, ParaId)>, - pub(crate) candidate_receipt_with_backing_validator_indices: - Vec<(CandidateReceipt, Vec<(ValidatorIndex, ValidityAttestation)>)>, -} - -impl Default for ProcessedCandidates { - fn default() -> Self { - Self { - core_indices: Vec::new(), - candidate_receipt_with_backing_validator_indices: Vec::new(), - } - } -} - /// Reads the footprint of queues for a specific origin type. pub trait QueueFootprinter { type Origin; @@ -514,6 +486,14 @@ impl Pallet { T::MessageQueue::sweep_queue(AggregateMessageOrigin::Ump(UmpQueueId::Para(para))); } + pub(crate) fn get_occupied_cores( + ) -> impl Iterator>)> + { + PendingAvailability::::iter_values().flat_map(|pending_candidates| { + pending_candidates.into_iter().map(|c| (c.core, c.clone())) + }) + } + /// Extract the freed cores based on cores that became available. /// /// Bitfields are expected to have been sanitized already. E.g. via `sanitize_bitfields`! @@ -640,12 +620,15 @@ impl Pallet { candidates: &BTreeMap, CoreIndex)>>, group_validators: GV, core_index_enabled: bool, - ) -> Result, DispatchError> + ) -> Result< + Vec<(CandidateReceipt, Vec<(ValidatorIndex, ValidityAttestation)>)>, + DispatchError, + > where GV: Fn(GroupIndex) -> Option>, { if candidates.is_empty() { - return Ok(ProcessedCandidates::default()) + return Ok(Default::default()) } let now = frame_system::Pallet::::block_number(); @@ -654,7 +637,6 @@ impl Pallet { // Collect candidate receipts with backers. let mut candidate_receipt_with_backing_validator_indices = Vec::with_capacity(candidates.len()); - let mut core_indices = Vec::with_capacity(candidates.len()); for (para_id, para_candidates) in candidates { let mut latest_head_data = match Self::para_latest_head_data(para_id) { @@ -708,7 +690,6 @@ impl Pallet { latest_head_data = candidate.candidate().commitments.head_data.clone(); candidate_receipt_with_backing_validator_indices .push((candidate.receipt(), backer_idx_and_attestation)); - core_indices.push((*core, *para_id)); // Update storage now PendingAvailability::::mutate(¶_id, |pending_availability| { @@ -743,10 +724,7 @@ impl Pallet { } } - Ok(ProcessedCandidates:: { - core_indices, - candidate_receipt_with_backing_validator_indices, - }) + Ok(candidate_receipt_with_backing_validator_indices) } // Get the latest backed output head data of this para (including pending availability). @@ -1173,7 +1151,9 @@ impl Pallet { /// Returns the first `CommittedCandidateReceipt` pending availability for the para provided, if /// any. - pub(crate) fn candidate_pending_availability( + /// A para_id could have more than one candidates pending availability, if it's using elastic + /// scaling. These candidates form a chain. This function returns the first in the chain. + pub(crate) fn first_candidate_pending_availability( para: ParaId, ) -> Option> { PendingAvailability::::get(¶).and_then(|p| { @@ -1201,24 +1181,6 @@ impl Pallet { }) .unwrap_or_default() } - - /// Returns the metadata around the first candidate pending availability for the - /// para provided, if any. - pub(crate) fn pending_availability( - para: ParaId, - ) -> Option>> { - PendingAvailability::::get(¶).and_then(|p| p.get(0).cloned()) - } - - /// Returns the metadata around the candidate pending availability occupying the supplied core, - /// if any. - pub(crate) fn pending_availability_with_core( - para: ParaId, - core: CoreIndex, - ) -> Option>> { - PendingAvailability::::get(¶) - .and_then(|p| p.iter().find(|c| c.core == core).cloned()) - } } const fn availability_threshold(n_validators: usize) -> usize { diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 87d21e209a4..188ba4995d8 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -1267,7 +1267,7 @@ fn candidate_checks() { &group_validators, false ), - Ok(ProcessedCandidates::default()) + Ok(Default::default()) ); // Check candidate ordering @@ -1563,20 +1563,16 @@ fn candidate_checks() { None, ); - let ProcessedCandidates { - core_indices: occupied_cores, - candidate_receipt_with_backing_validator_indices, - } = ParaInclusion::process_candidates( - &allowed_relay_parents, - &vec![(thread_a_assignment.0, vec![(backed.clone(), thread_a_assignment.1)])] - .into_iter() - .collect(), - &group_validators, - false, - ) - .expect("candidate is accepted with bad collator signature"); - - assert_eq!(occupied_cores, vec![(CoreIndex::from(2), thread_a)]); + let candidate_receipt_with_backing_validator_indices = + ParaInclusion::process_candidates( + &allowed_relay_parents, + &vec![(thread_a_assignment.0, vec![(backed.clone(), thread_a_assignment.1)])] + .into_iter() + .collect(), + &group_validators, + false, + ) + .expect("candidate is accepted with bad collator signature"); let mut expected = std::collections::HashMap::< CandidateHash, @@ -1924,10 +1920,7 @@ fn backing_works() { } }; - let ProcessedCandidates { - core_indices: occupied_cores, - candidate_receipt_with_backing_validator_indices, - } = ParaInclusion::process_candidates( + let candidate_receipt_with_backing_validator_indices = ParaInclusion::process_candidates( &allowed_relay_parents, &backed_candidates, &group_validators, @@ -1935,15 +1928,6 @@ fn backing_works() { ) .expect("candidates scheduled, in order, and backed"); - assert_eq!( - occupied_cores, - vec![ - (CoreIndex::from(0), chain_a), - (CoreIndex::from(1), chain_b), - (CoreIndex::from(2), thread_a) - ] - ); - // Transform the votes into the setup we expect let expected = { let mut intermediate = std::collections::HashMap::< @@ -2224,10 +2208,7 @@ fn backing_works_with_elastic_scaling_mvp() { } }; - let ProcessedCandidates { - core_indices: occupied_cores, - candidate_receipt_with_backing_validator_indices, - } = ParaInclusion::process_candidates( + let candidate_receipt_with_backing_validator_indices = ParaInclusion::process_candidates( &allowed_relay_parents, &backed_candidates, &group_validators, @@ -2235,16 +2216,6 @@ fn backing_works_with_elastic_scaling_mvp() { ) .expect("candidates scheduled, in order, and backed"); - // Both b candidates will be backed. - assert_eq!( - occupied_cores, - vec![ - (CoreIndex::from(0), chain_a), - (CoreIndex::from(1), chain_b), - (CoreIndex::from(2), chain_b), - ] - ); - // Transform the votes into the setup we expect let mut expected = std::collections::HashMap::< CandidateHash, @@ -2420,18 +2391,15 @@ fn can_include_candidate_with_ok_code_upgrade() { None, ); - let ProcessedCandidates { core_indices: occupied_cores, .. } = - ParaInclusion::process_candidates( - &allowed_relay_parents, - &vec![(chain_a_assignment.0, vec![(backed_a, chain_a_assignment.1)])] - .into_iter() - .collect::>(), - group_validators, - false, - ) - .expect("candidates scheduled, in order, and backed"); - - assert_eq!(occupied_cores, vec![(CoreIndex::from(0), chain_a)]); + let _ = ParaInclusion::process_candidates( + &allowed_relay_parents, + &vec![(chain_a_assignment.0, vec![(backed_a, chain_a_assignment.1)])] + .into_iter() + .collect::>(), + group_validators, + false, + ) + .expect("candidates scheduled, in order, and backed"); let backers = { let num_backers = effective_minimum_backing_votes( @@ -2846,7 +2814,7 @@ fn para_upgrade_delay_scheduled_from_inclusion() { None, ); - let ProcessedCandidates { core_indices: occupied_cores, .. } = + let _ = ParaInclusion::process_candidates( &allowed_relay_parents, &vec![(chain_a_assignment.0, vec![(backed_a, chain_a_assignment.1)])] @@ -2857,8 +2825,6 @@ fn para_upgrade_delay_scheduled_from_inclusion() { ) .expect("candidates scheduled, in order, and backed"); - assert_eq!(occupied_cores, vec![(CoreIndex::from(0), chain_a)]); - // Run a couple of blocks before the inclusion. run_to_block(7, |_| None); diff --git a/polkadot/runtime/parachains/src/initializer.rs b/polkadot/runtime/parachains/src/initializer.rs index 340f727097b..6ee245fb523 100644 --- a/polkadot/runtime/parachains/src/initializer.rs +++ b/polkadot/runtime/parachains/src/initializer.rs @@ -87,10 +87,10 @@ impl> Default for SessionChangeNotification, - queued: Vec, - session_index: SessionIndex, +pub(crate) struct BufferedSessionChange { + pub validators: Vec, + pub queued: Vec, + pub session_index: SessionIndex, } pub trait WeightInfo { @@ -149,7 +149,7 @@ pub mod pallet { #[pallet::storage] pub(super) type HasInitialized = StorageValue<_, ()>; - /// Buffered session changes along with the block number at which they should be applied. + /// Buffered session changes. /// /// Typically this will be empty or one element long. Apart from that this item never hits /// the storage. @@ -157,7 +157,7 @@ pub mod pallet { /// However this is a `Vec` regardless to handle various edge cases that may occur at runtime /// upgrade boundaries or if governance intervenes. #[pallet::storage] - pub(super) type BufferedSessionChanges = + pub(crate) type BufferedSessionChanges = StorageValue<_, Vec, ValueQuery>; #[pallet::hooks] @@ -254,9 +254,6 @@ impl Pallet { buf }; - // inform about upcoming new session - scheduler::Pallet::::pre_new_session(); - let configuration::SessionChangeOutcome { prev_config, new_config } = configuration::Pallet::::initializer_on_new_session(&session_index); let new_config = new_config.unwrap_or_else(|| prev_config.clone()); @@ -328,6 +325,11 @@ impl Pallet { { Self::on_new_session(changed, session_index, validators, queued) } + + /// Return whether at the end of this block a new session will be initialized. + pub(crate) fn upcoming_session_change() -> bool { + !BufferedSessionChanges::::get().is_empty() + } } impl sp_runtime::BoundToRuntimeAppPublic for Pallet { diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index 80751a2b7a0..c23918708b2 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -520,9 +520,6 @@ pub mod mock_assigner { #[pallet::storage] pub(super) type MockAssignmentQueue = StorageValue<_, VecDeque, ValueQuery>; - - #[pallet::storage] - pub(super) type MockCoreCount = StorageValue<_, u32, OptionQuery>; } impl Pallet { @@ -531,12 +528,6 @@ pub mod mock_assigner { pub fn add_test_assignment(assignment: Assignment) { MockAssignmentQueue::::mutate(|queue| queue.push_back(assignment)); } - - // Allows for customized core count in scheduler tests, rather than a core count - // derived from on-demand config + parachain count. - pub fn set_core_count(count: u32) { - MockCoreCount::::set(Some(count)); - } } impl AssignmentProvider for Pallet { @@ -554,20 +545,18 @@ pub mod mock_assigner { } // We don't care about core affinity in the test assigner - fn report_processed(_assignment: Assignment) {} + fn report_processed(_: Assignment) {} - // The results of this are tested in on_demand tests. No need to represent it - // in the mock assigner. - fn push_back_assignment(_assignment: Assignment) {} + fn push_back_assignment(assignment: Assignment) { + Self::add_test_assignment(assignment); + } #[cfg(any(feature = "runtime-benchmarks", test))] fn get_mock_assignment(_: CoreIndex, para_id: ParaId) -> Assignment { Assignment::Bulk(para_id) } - fn session_core_count() -> u32 { - MockCoreCount::::get().unwrap_or(5) - } + fn assignment_duplicated(_: &Assignment) {} } } diff --git a/polkadot/runtime/parachains/src/on_demand/mod.rs b/polkadot/runtime/parachains/src/on_demand/mod.rs index dc046c194fd..66400eb00fd 100644 --- a/polkadot/runtime/parachains/src/on_demand/mod.rs +++ b/polkadot/runtime/parachains/src/on_demand/mod.rs @@ -317,6 +317,11 @@ where Some(assignment) } + /// Report that an assignment was duplicated by the scheduler. + pub fn assignment_duplicated(para_id: ParaId, core_index: CoreIndex) { + Pallet::::increase_affinity(para_id, core_index); + } + /// Report that the `para_id` & `core_index` combination was processed. /// /// This should be called once it is clear that the assignment won't get pushed back anymore. diff --git a/polkadot/runtime/parachains/src/on_demand/tests.rs b/polkadot/runtime/parachains/src/on_demand/tests.rs index 97429541181..7da16942c7a 100644 --- a/polkadot/runtime/parachains/src/on_demand/tests.rs +++ b/polkadot/runtime/parachains/src/on_demand/tests.rs @@ -30,7 +30,6 @@ use crate::{ }, paras::{ParaGenesisArgs, ParaKind}, }; -use alloc::collections::btree_map::BTreeMap; use core::cmp::{Ord, Ordering}; use frame_support::{assert_noop, assert_ok}; use pallet_balances::Error as BalancesError; @@ -86,7 +85,7 @@ fn run_to_block( OnDemand::on_initialize(b + 1); // In the real runtime this is expected to be called by the `InclusionInherent` pallet. - Scheduler::free_cores_and_fill_claim_queue(BTreeMap::new(), b + 1); + Scheduler::advance_claim_queue(&Default::default()); } } diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index 266860061be..485e7211c1d 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -43,7 +43,8 @@ benchmarks! { // Variant over `v`, the number of dispute statements in a dispute statement set. This gives the // weight of a single dispute statement set. enter_variable_disputes { - let v in 10..BenchBuilder::::fallback_max_validators(); + // The number of statements needs to be at least a third of the validator set size. + let v in 400..BenchBuilder::::fallback_max_validators(); let scenario = BenchBuilder::::new() .set_dispute_sessions(&[2]) diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index 2aca0f2c728..4c1394fd134 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -27,8 +27,7 @@ use crate::{ inclusion::{self, CandidateCheckContext}, initializer, metrics::METRICS, - paras, - scheduler::{self, FreedReason}, + paras, scheduler, shared::{self, AllowedRelayParentsTracker}, ParaId, }; @@ -38,6 +37,7 @@ use alloc::{ vec::Vec, }; use bitvec::prelude::BitVec; +use core::result::Result; use frame_support::{ defensive, dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, @@ -105,15 +105,6 @@ impl DisputedBitfield { } } -/// The context in which the inherent data is checked or processed. -#[derive(PartialEq)] -pub enum ProcessInherentDataContext { - /// Enables filtering/limits weight of inherent up to maximum block weight. - /// Invariant: InherentWeight <= BlockWeight. - ProvideInherent, - /// Checks the InherentWeight invariant. - Enter, -} pub use pallet::*; #[frame_support::pallet] @@ -140,11 +131,9 @@ pub mod pallet { /// The hash of the submitted parent header doesn't correspond to the saved block hash of /// the parent. InvalidParentHeader, - /// The data given to the inherent will result in an overweight block. - InherentOverweight, - /// A candidate was filtered during inherent execution. This should have only been done + /// Inherent data was filtered during execution. This should have only been done /// during creation. - CandidatesFilteredDuringExecution, + InherentDataFilteredDuringExecution, /// Too many candidates supplied. UnscheduledCandidate, } @@ -253,9 +242,12 @@ pub mod pallet { ensure!(!Included::::exists(), Error::::TooManyInclusionInherents); Included::::set(Some(())); + let initial_data = data.clone(); - Self::process_inherent_data(data, ProcessInherentDataContext::Enter) - .map(|(_processed, post_info)| post_info) + Self::process_inherent_data(data).and_then(|(processed, post_info)| { + ensure!(initial_data == processed, Error::::InherentDataFilteredDuringExecution); + Ok(post_info) + }) } } } @@ -273,10 +265,7 @@ impl Pallet { return None }, }; - match Self::process_inherent_data( - parachains_inherent_data, - ProcessInherentDataContext::ProvideInherent, - ) { + match Self::process_inherent_data(parachains_inherent_data) { Ok((processed, _)) => Some(processed), Err(err) => { log::warn!(target: LOG_TARGET, "Processing inherent data failed: {:?}", err); @@ -290,21 +279,12 @@ impl Pallet { /// The given inherent data is processed and state is altered accordingly. If any data could /// not be applied (inconsistencies, weight limit, ...) it is removed. /// - /// When called from `create_inherent` the `context` must be set to - /// `ProcessInherentDataContext::ProvideInherent` so it guarantees the invariant that inherent - /// is not overweight. - /// It is **mandatory** that calls from `enter` set `context` to - /// `ProcessInherentDataContext::Enter` to ensure the weight invariant is checked. - /// /// Returns: Result containing processed inherent data and weight, the processed inherent would /// consume. fn process_inherent_data( data: ParachainsInherentData>, - context: ProcessInherentDataContext, - ) -> core::result::Result< - (ParachainsInherentData>, PostDispatchInfo), - DispatchErrorWithPostInfo, - > { + ) -> Result<(ParachainsInherentData>, PostDispatchInfo), DispatchErrorWithPostInfo> + { #[cfg(feature = "runtime-metrics")] sp_io::init_tracing(); @@ -333,6 +313,27 @@ impl Pallet { let now = frame_system::Pallet::::block_number(); let config = configuration::ActiveConfig::::get(); + // Before anything else, update the allowed relay-parents. + { + let parent_number = now - One::one(); + let parent_storage_root = *parent_header.state_root(); + + shared::AllowedRelayParents::::mutate(|tracker| { + tracker.update( + parent_hash, + parent_storage_root, + scheduler::ClaimQueue::::get() + .into_iter() + .map(|(core_index, paras)| { + (core_index, paras.into_iter().map(|e| e.para_id()).collect()) + }) + .collect(), + parent_number, + config.async_backing_params.allowed_ancestry_len, + ); + }); + } + let candidates_weight = backed_candidates_weight::(&backed_candidates); let bitfields_weight = signed_bitfields_weight::(&bitfields); let disputes_weight = multi_dispute_statement_sets_weight::(&disputes); @@ -345,7 +346,7 @@ impl Pallet { log::debug!(target: LOG_TARGET, "Time weight before filter: {}, candidates + bitfields: {}, disputes: {}", weight_before_filtering.ref_time(), candidates_weight.ref_time() + bitfields_weight.ref_time(), disputes_weight.ref_time()); let current_session = shared::CurrentSessionIndex::::get(); - let expected_bits = scheduler::AvailabilityCores::::get().len(); + let expected_bits = scheduler::Pallet::::num_availability_cores(); let validator_public = shared::ActiveValidatorKeys::::get(); // We are assuming (incorrectly) to have all the weight (for the mandatory class or even @@ -390,7 +391,7 @@ impl Pallet { T::DisputesHandler::filter_dispute_data(set, post_conclusion_acceptance_period) }; - // Limit the disputes first, since the following statements depend on the votes include + // Limit the disputes first, since the following statements depend on the votes included // here. let (checked_disputes_sets, checked_disputes_sets_consumed_weight) = limit_and_sanitize_disputes::( @@ -399,7 +400,7 @@ impl Pallet { max_block_weight, ); - let mut all_weight_after = if context == ProcessInherentDataContext::ProvideInherent { + let mut all_weight_after = { // Assure the maximum block weight is adhered, by limiting bitfields and backed // candidates. Dispute statement sets were already limited before. let non_disputes_weight = apply_weight_limit::( @@ -427,23 +428,6 @@ impl Pallet { log::warn!(target: LOG_TARGET, "Post weight limiting weight is still too large, time: {}, size: {}", all_weight_after.ref_time(), all_weight_after.proof_size()); } all_weight_after - } else { - // This check is performed in the context of block execution. Ensures inherent weight - // invariants guaranteed by `create_inherent_data` for block authorship. - if weight_before_filtering.any_gt(max_block_weight) { - log::error!( - "Overweight para inherent data reached the runtime {:?}: {} > {}", - parent_hash, - weight_before_filtering, - max_block_weight - ); - } - - ensure!( - weight_before_filtering.all_lte(max_block_weight), - Error::::InherentOverweight - ); - weight_before_filtering }; // Note that `process_checked_multi_dispute_data` will iterate and import each @@ -567,98 +551,9 @@ impl Pallet { log::debug!(target: LOG_TARGET, "Evicted timed out cores: {:?}", freed_timeout); } - // We'll schedule paras again, given freed cores, and reasons for freeing. - let freed = freed_concluded - .into_iter() - .map(|(c, _hash)| (c, FreedReason::Concluded)) - .chain(freed_disputed.into_iter().map(|core| (core, FreedReason::Concluded))) - .chain(freed_timeout.into_iter().map(|c| (c, FreedReason::TimedOut))) - .collect::>(); - scheduler::Pallet::::free_cores_and_fill_claim_queue(freed, now); - - METRICS.on_candidates_processed_total(backed_candidates.len() as u64); - - // After freeing cores and filling claims, but before processing backed candidates - // we update the allowed relay-parents. - { - let parent_number = now - One::one(); - let parent_storage_root = *parent_header.state_root(); - - shared::AllowedRelayParents::::mutate(|tracker| { - tracker.update( - parent_hash, - parent_storage_root, - scheduler::ClaimQueue::::get() - .into_iter() - .map(|(core_index, paras)| { - (core_index, paras.into_iter().map(|e| e.para_id()).collect()) - }) - .collect(), - parent_number, - config.async_backing_params.allowed_ancestry_len, - ); - }); - } - let allowed_relay_parents = shared::AllowedRelayParents::::get(); - - let core_index_enabled = configuration::ActiveConfig::::get() - .node_features - .get(FeatureIndex::ElasticScalingMVP as usize) - .map(|b| *b) - .unwrap_or(false); - - let allow_v2_receipts = configuration::ActiveConfig::::get() - .node_features - .get(FeatureIndex::CandidateReceiptV2 as usize) - .map(|b| *b) - .unwrap_or(false); - - let mut eligible: BTreeMap> = BTreeMap::new(); - let mut total_eligible_cores = 0; - - for (core_idx, para_id) in scheduler::Pallet::::eligible_paras() { - total_eligible_cores += 1; - log::trace!(target: LOG_TARGET, "Found eligible para {:?} on core {:?}", para_id, core_idx); - eligible.entry(para_id).or_default().insert(core_idx); - } - - let initial_candidate_count = backed_candidates.len(); - let backed_candidates_with_core = sanitize_backed_candidates::( - backed_candidates, - &allowed_relay_parents, - concluded_invalid_hashes, - eligible, - core_index_enabled, - allow_v2_receipts, - ); - let count = count_backed_candidates(&backed_candidates_with_core); - - ensure!(count <= total_eligible_cores, Error::::UnscheduledCandidate); - - METRICS.on_candidates_sanitized(count as u64); - - // In `Enter` context (invoked during execution) no more candidates should be filtered, - // because they have already been filtered during `ProvideInherent` context. Abort in such - // cases. - if context == ProcessInherentDataContext::Enter { - ensure!( - initial_candidate_count == count, - Error::::CandidatesFilteredDuringExecution - ); - } - - // Process backed candidates according to scheduled cores. - let inclusion::ProcessedCandidates::< as HeaderT>::Hash> { - core_indices: occupied, - candidate_receipt_with_backing_validator_indices, - } = inclusion::Pallet::::process_candidates( - &allowed_relay_parents, - &backed_candidates_with_core, - scheduler::Pallet::::group_validators, - core_index_enabled, - )?; - // Note which of the scheduled cores were actually occupied by a backed candidate. - scheduler::Pallet::::occupied(occupied.into_iter().map(|e| (e.0, e.1)).collect()); + // Back candidates. + let (candidate_receipt_with_backing_validator_indices, backed_candidates_with_core) = + Self::back_candidates(concluded_invalid_hashes, backed_candidates)?; set_scrapable_on_chain_backings::( current_session, @@ -672,6 +567,7 @@ impl Pallet { let bitfields = bitfields.into_iter().map(|v| v.into_unchecked()).collect(); + let count = backed_candidates_with_core.len(); let processed = ParachainsInherentData { bitfields, backed_candidates: backed_candidates_with_core.into_iter().fold( @@ -686,6 +582,104 @@ impl Pallet { }; Ok((processed, Some(all_weight_after).into())) } + + fn back_candidates( + concluded_invalid_hashes: BTreeSet, + backed_candidates: Vec>, + ) -> Result< + ( + Vec<(CandidateReceipt, Vec<(ValidatorIndex, ValidityAttestation)>)>, + BTreeMap, CoreIndex)>>, + ), + DispatchErrorWithPostInfo, + > { + let allowed_relay_parents = shared::AllowedRelayParents::::get(); + let upcoming_new_session = initializer::Pallet::::upcoming_session_change(); + + METRICS.on_candidates_processed_total(backed_candidates.len() as u64); + + if !upcoming_new_session { + let occupied_cores = + inclusion::Pallet::::get_occupied_cores().map(|(core, _)| core).collect(); + + let mut eligible: BTreeMap> = BTreeMap::new(); + let mut total_eligible_cores = 0; + + for (core_idx, para_id) in Self::eligible_paras(&occupied_cores) { + total_eligible_cores += 1; + log::trace!(target: LOG_TARGET, "Found eligible para {:?} on core {:?}", para_id, core_idx); + eligible.entry(para_id).or_default().insert(core_idx); + } + + let node_features = configuration::ActiveConfig::::get().node_features; + let core_index_enabled = node_features + .get(FeatureIndex::ElasticScalingMVP as usize) + .map(|b| *b) + .unwrap_or(false); + + let allow_v2_receipts = node_features + .get(FeatureIndex::CandidateReceiptV2 as usize) + .map(|b| *b) + .unwrap_or(false); + + let backed_candidates_with_core = sanitize_backed_candidates::( + backed_candidates, + &allowed_relay_parents, + concluded_invalid_hashes, + eligible, + core_index_enabled, + allow_v2_receipts, + ); + let count = count_backed_candidates(&backed_candidates_with_core); + + ensure!(count <= total_eligible_cores, Error::::UnscheduledCandidate); + + METRICS.on_candidates_sanitized(count as u64); + + // Process backed candidates according to scheduled cores. + let candidate_receipt_with_backing_validator_indices = + inclusion::Pallet::::process_candidates( + &allowed_relay_parents, + &backed_candidates_with_core, + scheduler::Pallet::::group_validators, + core_index_enabled, + )?; + + // We need to advance the claim queue on all cores, except for the ones that did not + // get freed in this block. The ones that did not get freed also cannot be newly + // occupied. + scheduler::Pallet::::advance_claim_queue(&occupied_cores); + + Ok((candidate_receipt_with_backing_validator_indices, backed_candidates_with_core)) + } else { + log::debug!( + target: LOG_TARGET, + "Upcoming session change, not backing any new candidates." + ); + // If we'll initialize a new session at the end of the block, we don't want to + // advance the claim queue. + + Ok((vec![], BTreeMap::new())) + } + } + + /// Paras that may get backed on cores. + /// + /// 1. The para must be scheduled on core. + /// 2. Core needs to be free, otherwise backing is not possible. + /// + /// We get a set of the occupied cores as input. + pub(crate) fn eligible_paras<'a>( + occupied_cores: &'a BTreeSet, + ) -> impl Iterator + 'a { + scheduler::ClaimQueue::::get().into_iter().filter_map(|(core_idx, queue)| { + if occupied_cores.contains(&core_idx) { + return None + } + let next_scheduled = queue.front()?; + Some((core_idx, next_scheduled.para_id())) + }) + } } /// Derive a bitfield from dispute @@ -1144,10 +1138,7 @@ fn sanitize_backed_candidates( } fn count_backed_candidates(backed_candidates: &BTreeMap>) -> usize { - backed_candidates.iter().fold(0, |mut count, (_id, candidates)| { - count += candidates.len(); - count - }) + backed_candidates.values().map(|c| c.len()).sum() } /// Derive entropy from babe provided per block randomness. diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index f5c3d507776..2c65298baf0 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -49,11 +49,10 @@ mod enter { use crate::{ builder::{junk_collator, junk_collator_signature, Bench, BenchBuilder, CandidateModifier}, + disputes::clear_dispute_storage, + initializer::BufferedSessionChange, mock::{mock_assigner, new_test_ext, BlockLength, BlockWeights, RuntimeOrigin, Test}, - scheduler::{ - common::{Assignment, AssignmentProvider}, - ParasEntry, - }, + scheduler::common::{Assignment, AssignmentProvider}, session_info, }; use alloc::collections::btree_map::BTreeMap; @@ -73,7 +72,6 @@ mod enter { backed_and_concluding: BTreeMap, num_validators_per_core: u32, code_upgrade: Option, - fill_claimqueue: bool, elastic_paras: BTreeMap, unavailable_cores: Vec, v2_descriptor: bool, @@ -87,7 +85,6 @@ mod enter { backed_and_concluding, num_validators_per_core, code_upgrade, - fill_claimqueue, elastic_paras, unavailable_cores, v2_descriptor, @@ -108,14 +105,11 @@ mod enter { .set_dispute_statements(dispute_statements) .set_backed_and_concluding_paras(backed_and_concluding.clone()) .set_dispute_sessions(&dispute_sessions[..]) - .set_fill_claimqueue(fill_claimqueue) .set_unavailable_cores(unavailable_cores) .set_candidate_descriptor_v2(v2_descriptor) .set_candidate_modifier(candidate_modifier); // Setup some assignments as needed: - mock_assigner::Pallet::::set_core_count(builder.max_cores()); - (0..(builder.max_cores() as usize - extra_cores)).for_each(|para_id| { (0..elastic_paras.get(&(para_id as u32)).cloned().unwrap_or(1)).for_each( |_para_local_core_idx| { @@ -164,7 +158,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: true, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor, @@ -188,6 +181,7 @@ mod enter { inherent_data .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); + assert!(!scheduler::Pallet::::claim_queue_is_empty()); // Nothing is filtered out (including the backed candidates.) assert_eq!( @@ -272,7 +266,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: true, elastic_paras: [(2, 3)].into_iter().collect(), unavailable_cores: vec![], v2_descriptor, @@ -293,6 +286,7 @@ mod enter { .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); + assert!(!scheduler::Pallet::::claim_queue_is_empty()); assert!(pallet::OnChainVotes::::get().is_none()); // Nothing is filtered out (including the backed candidates.) @@ -375,7 +369,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: true, elastic_paras: [(2, 4)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), v2_descriptor: false, @@ -527,6 +520,101 @@ mod enter { }); } + #[test] + // Test that no new candidates are backed if there's an upcoming session change scheduled at the + // end of the block. Claim queue will also not be advanced. + fn session_change() { + let config = MockGenesisConfig::default(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + + new_test_ext(config).execute_with(|| { + let dispute_statements = BTreeMap::new(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + + let scenario = make_inherent_data(TestConfig { + dispute_statements, + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + elastic_paras: BTreeMap::new(), + unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, + }); + + let prev_claim_queue = scheduler::ClaimQueue::::get(); + + assert_eq!(inclusion::PendingAvailability::::iter().count(), 2); + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(0)).unwrap().len(), + 1 + ); + assert_eq!( + inclusion::PendingAvailability::::get(ParaId::from(1)).unwrap().len(), + 1 + ); + + // We expect the scenario to have cores 0 & 1 with pending availability. The backed + // candidates are also created for cores 0 & 1. The pending available candidates will + // become available but the new candidates will not be backed since there is an upcoming + // session change. + let mut expected_para_inherent_data = scenario.data.clone(); + expected_para_inherent_data.backed_candidates.clear(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (2 validators) + assert_eq!(expected_para_inherent_data.bitfields.len(), 2); + // * 0 disputes. + assert_eq!(expected_para_inherent_data.disputes.len(), 0); + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) + .unwrap(); + assert!(!scheduler::Pallet::::claim_queue_is_empty()); + + // Simulate a session change scheduled to happen at the end of the block. + initializer::BufferedSessionChanges::::put(vec![BufferedSessionChange { + validators: vec![], + queued: vec![], + session_index: 3, + }]); + + // Only backed candidates are filtered out. + assert_eq!( + Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(), + expected_para_inherent_data + ); + + assert_eq!( + // No candidates backed. + OnChainVotes::::get().unwrap().backing_validators_per_candidate.len(), + 0 + ); + + assert_eq!( + // The session of the on chain votes should equal the current session, which is 2 + OnChainVotes::::get().unwrap().session, + 2 + ); + + // No pending availability candidates. + assert_eq!(inclusion::PendingAvailability::::iter().count(), 2); + assert!(inclusion::PendingAvailability::::get(ParaId::from(0)) + .unwrap() + .is_empty()); + assert!(inclusion::PendingAvailability::::get(ParaId::from(1)) + .unwrap() + .is_empty()); + + // The claim queue should not have been advanced. + assert_eq!(prev_claim_queue, scheduler::ClaimQueue::::get()); + }); + } + #[test] fn test_session_is_tracked_in_on_chain_scraping() { use crate::disputes::run_to_block; @@ -633,7 +721,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -655,8 +742,7 @@ mod enter { .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); - // The current schedule is empty prior to calling `create_inherent_enter`. - assert!(scheduler::Pallet::::claim_queue_is_empty()); + assert!(!scheduler::Pallet::::claim_queue_is_empty()); let multi_dispute_inherent_data = Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(); @@ -671,6 +757,8 @@ mod enter { &expected_para_inherent_data.disputes[..2], ); + clear_dispute_storage::(); + assert_ok!(Pallet::::enter( frame_system::RawOrigin::None.into(), multi_dispute_inherent_data, @@ -708,7 +796,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 6, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -729,8 +816,7 @@ mod enter { .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); - // The current schedule is empty prior to calling `create_inherent_enter`. - assert!(scheduler::Pallet::::claim_queue_is_empty()); + assert!(!scheduler::Pallet::::claim_queue_is_empty()); let limit_inherent_data = Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(); @@ -742,6 +828,8 @@ mod enter { assert_eq!(limit_inherent_data.disputes[0].session, 1); assert_eq!(limit_inherent_data.disputes[1].session, 2); + clear_dispute_storage::(); + assert_ok!(Pallet::::enter( frame_system::RawOrigin::None.into(), limit_inherent_data, @@ -781,7 +869,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 4, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -803,8 +890,7 @@ mod enter { .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); - // The current schedule is empty prior to calling `create_inherent_enter`. - assert!(scheduler::Pallet::::claim_queue_is_empty()); + assert!(!scheduler::Pallet::::claim_queue_is_empty()); // Nothing is filtered out (including the backed candidates.) let limit_inherent_data = @@ -826,6 +912,8 @@ mod enter { // over weight assert_eq!(limit_inherent_data.backed_candidates.len(), 0); + clear_dispute_storage::(); + assert_ok!(Pallet::::enter( frame_system::RawOrigin::None.into(), limit_inherent_data, @@ -870,7 +958,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -892,10 +979,8 @@ mod enter { .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); - // The current schedule is empty prior to calling `create_inherent_enter`. - assert!(scheduler::Pallet::::claim_queue_is_empty()); + assert!(!scheduler::Pallet::::claim_queue_is_empty()); - // Nothing is filtered out (including the backed candidates.) let limit_inherent_data = Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(); assert_ne!(limit_inherent_data, expected_para_inherent_data); @@ -916,9 +1001,11 @@ mod enter { // over weight assert_eq!(limit_inherent_data.backed_candidates.len(), 0); + clear_dispute_storage::(); + assert_ok!(Pallet::::enter( frame_system::RawOrigin::None.into(), - limit_inherent_data, + limit_inherent_data )); assert_eq!( @@ -959,7 +1046,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -1020,7 +1106,6 @@ mod enter { backed_and_concluding, num_validators_per_core, code_upgrade: None, - fill_claimqueue: true, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -1043,6 +1128,21 @@ mod enter { Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(); assert!(limit_inherent_data == expected_para_inherent_data); + // Cores were scheduled. We should put the assignments back, before calling enter(). + let cores = (0..num_candidates) + .into_iter() + .map(|i| { + // Load an assignment into provider so that one is present to pop + let assignment = + ::AssignmentProvider::get_mock_assignment( + CoreIndex(i), + ParaId::from(i), + ); + (CoreIndex(i), [assignment].into()) + }) + .collect(); + scheduler::ClaimQueue::::set(cores); + assert_ok!(Pallet::::enter( frame_system::RawOrigin::None.into(), limit_inherent_data, @@ -1108,7 +1208,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -1166,24 +1265,23 @@ mod enter { ); // One core was scheduled. We should put the assignment back, before calling enter(). - let now = frame_system::Pallet::::block_number() + 1; let used_cores = 5; let cores = (0..used_cores) .into_iter() .map(|i| { - let SchedulerParams { ttl, .. } = - configuration::ActiveConfig::::get().scheduler_params; // Load an assignment into provider so that one is present to pop let assignment = ::AssignmentProvider::get_mock_assignment( CoreIndex(i), ParaId::from(i), ); - (CoreIndex(i), [ParasEntry::new(assignment, now + ttl)].into()) + (CoreIndex(i), [assignment].into()) }) .collect(); scheduler::ClaimQueue::::set(cores); + clear_dispute_storage::(); + assert_ok!(Pallet::::enter( frame_system::RawOrigin::None.into(), limit_inherent_data, @@ -1217,7 +1315,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -1287,7 +1384,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -1355,7 +1451,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -1446,7 +1541,6 @@ mod enter { } // Ensure that overweight parachain inherents are always rejected by the runtime. - // Runtime should panic and return `InherentOverweight` error. #[rstest] #[case(true)] #[case(false)] @@ -1479,7 +1573,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor, @@ -1548,7 +1641,6 @@ mod enter { } // Ensure that overweight parachain inherents are always rejected by the runtime. - // Runtime should panic and return `InherentOverweight` error. #[test] fn inherent_create_weight_invariant() { new_test_ext(MockGenesisConfig::default()).execute_with(|| { @@ -1570,7 +1662,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, @@ -1600,7 +1691,7 @@ mod enter { .unwrap_err() .error; - assert_eq!(dispatch_error, Error::::InherentOverweight.into()); + assert_eq!(dispatch_error, Error::::InherentDataFilteredDuringExecution.into()); }); } @@ -1630,7 +1721,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 5, code_upgrade: None, - fill_claimqueue: true, elastic_paras: [(2, 8)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), v2_descriptor: true, @@ -1670,7 +1760,7 @@ mod enter { // We expect `enter` to fail because the inherent data contains backed candidates with // v2 descriptors. - assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + assert_eq!(dispatch_error, Error::::InherentDataFilteredDuringExecution.into()); }); } @@ -1698,9 +1788,8 @@ mod enter { dispute_statements: BTreeMap::new(), dispute_sessions: vec![], // No disputes backed_and_concluding, - num_validators_per_core: 5, + num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: true, elastic_paras: [(2, 8)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), v2_descriptor: true, @@ -1719,8 +1808,8 @@ mod enter { let unfiltered_para_inherent_data = scenario.data.clone(); // Check the para inherent data is as expected: - // * 1 bitfield per validator (5 validators per core, 10 backed candidates) - assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 1 bitfield per validator (1 validators per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 10); // * 10 v2 candidate descriptors. assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); @@ -1738,7 +1827,7 @@ mod enter { // We expect `enter` to fail because the inherent data contains backed candidates with // v2 descriptors. - assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + assert_eq!(dispatch_error, Error::::InherentDataFilteredDuringExecution.into()); }); } @@ -1766,9 +1855,8 @@ mod enter { dispute_statements: BTreeMap::new(), dispute_sessions: vec![], // No disputes backed_and_concluding, - num_validators_per_core: 5, + num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: true, elastic_paras: [(2, 8)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), v2_descriptor: true, @@ -1787,8 +1875,8 @@ mod enter { let unfiltered_para_inherent_data = scenario.data.clone(); // Check the para inherent data is as expected: - // * 1 bitfield per validator (5 validators per core, 10 backed candidates) - assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 1 bitfield per validator (1 validator per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 10); // * 10 v2 candidate descriptors. assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); @@ -1806,7 +1894,7 @@ mod enter { // We expect `enter` to fail because the inherent data contains backed candidates with // v2 descriptors. - assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + assert_eq!(dispatch_error, Error::::InherentDataFilteredDuringExecution.into()); }); } #[test] @@ -1843,7 +1931,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: true, elastic_paras: [(2, 3)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), v2_descriptor: true, @@ -1898,7 +1985,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: true, elastic_paras: [(2, 3)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), v2_descriptor: true, @@ -1985,7 +2071,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: true, elastic_paras: Default::default(), unavailable_cores: unavailable_cores.clone(), v2_descriptor: true, @@ -2040,7 +2125,6 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: true, elastic_paras: [(2, 3)].into_iter().collect(), unavailable_cores, v2_descriptor: true, @@ -2372,7 +2456,7 @@ mod sanitizers { mod candidates { use crate::{ mock::{set_disabled_validators, RuntimeOrigin}, - scheduler::{common::Assignment, ParasEntry}, + scheduler::common::Assignment, util::{make_persisted_validation_data, make_persisted_validation_data_with_parent}, }; use alloc::collections::vec_deque::VecDeque; @@ -2453,17 +2537,17 @@ mod sanitizers { scheduler::Pallet::::set_claim_queue(BTreeMap::from([ ( CoreIndex::from(0), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 1.into(), + core_index: CoreIndex(0), + }]), ), ( CoreIndex::from(1), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(1) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 2.into(), + core_index: CoreIndex(1), + }]), ), ])); @@ -2545,7 +2629,7 @@ mod sanitizers { // State sanity checks assert_eq!( - scheduler::Pallet::::scheduled_paras().collect::>(), + Pallet::::eligible_paras(&Default::default()).collect::>(), vec![(CoreIndex(0), ParaId::from(1)), (CoreIndex(1), ParaId::from(2))] ); assert_eq!( @@ -2641,73 +2725,73 @@ mod sanitizers { scheduler::Pallet::::set_claim_queue(BTreeMap::from([ ( CoreIndex::from(0), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 1.into(), + core_index: CoreIndex(0), + }]), ), ( CoreIndex::from(1), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(1) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 1.into(), + core_index: CoreIndex(1), + }]), ), ( CoreIndex::from(2), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(2) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 2.into(), + core_index: CoreIndex(2), + }]), ), ( CoreIndex::from(3), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(3) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 2.into(), + core_index: CoreIndex(3), + }]), ), ( CoreIndex::from(4), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 3.into(), core_index: CoreIndex(4) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 3.into(), + core_index: CoreIndex(4), + }]), ), ( CoreIndex::from(5), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 4.into(), core_index: CoreIndex(5) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 4.into(), + core_index: CoreIndex(5), + }]), ), ( CoreIndex::from(6), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 5.into(), core_index: CoreIndex(6) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 5.into(), + core_index: CoreIndex(6), + }]), ), ( CoreIndex::from(7), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 7.into(), core_index: CoreIndex(7) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 7.into(), + core_index: CoreIndex(7), + }]), ), ( CoreIndex::from(8), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 7.into(), core_index: CoreIndex(8) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 7.into(), + core_index: CoreIndex(8), + }]), ), ( CoreIndex::from(9), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 8.into(), core_index: CoreIndex(9) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 8.into(), + core_index: CoreIndex(9), + }]), ), ])); @@ -3087,7 +3171,7 @@ mod sanitizers { // State sanity checks assert_eq!( - scheduler::Pallet::::scheduled_paras().collect::>(), + Pallet::::eligible_paras(&Default::default()).collect::>(), vec![ (CoreIndex(0), ParaId::from(1)), (CoreIndex(1), ParaId::from(1)), @@ -3102,7 +3186,7 @@ mod sanitizers { ] ); let mut scheduled: BTreeMap> = BTreeMap::new(); - for (core_idx, para_id) in scheduler::Pallet::::scheduled_paras() { + for (core_idx, para_id) in Pallet::::eligible_paras(&Default::default()) { scheduled.entry(para_id).or_default().insert(core_idx); } @@ -3186,66 +3270,66 @@ mod sanitizers { scheduler::Pallet::::set_claim_queue(BTreeMap::from([ ( CoreIndex::from(0), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 1.into(), + core_index: CoreIndex(0), + }]), ), ( CoreIndex::from(1), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(1) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 1.into(), + core_index: CoreIndex(1), + }]), ), ( CoreIndex::from(2), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(2) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 2.into(), + core_index: CoreIndex(2), + }]), ), ( CoreIndex::from(3), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(3) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 2.into(), + core_index: CoreIndex(3), + }]), ), ( CoreIndex::from(4), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(4) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 2.into(), + core_index: CoreIndex(4), + }]), ), ( CoreIndex::from(5), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 3.into(), core_index: CoreIndex(5) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 3.into(), + core_index: CoreIndex(5), + }]), ), ( CoreIndex::from(6), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 3.into(), core_index: CoreIndex(6) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 3.into(), + core_index: CoreIndex(6), + }]), ), ( CoreIndex::from(7), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 4.into(), core_index: CoreIndex(7) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 4.into(), + core_index: CoreIndex(7), + }]), ), ( CoreIndex::from(8), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 4.into(), core_index: CoreIndex(8) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 4.into(), + core_index: CoreIndex(8), + }]), ), ])); @@ -3575,7 +3659,7 @@ mod sanitizers { // State sanity checks assert_eq!( - scheduler::Pallet::::scheduled_paras().collect::>(), + Pallet::::eligible_paras(&Default::default()).collect::>(), vec![ (CoreIndex(0), ParaId::from(1)), (CoreIndex(1), ParaId::from(1)), @@ -3589,7 +3673,7 @@ mod sanitizers { ] ); let mut scheduled: BTreeMap> = BTreeMap::new(); - for (core_idx, para_id) in scheduler::Pallet::::scheduled_paras() { + for (core_idx, para_id) in Pallet::::eligible_paras(&Default::default()) { scheduled.entry(para_id).or_default().insert(core_idx); } @@ -3710,45 +3794,45 @@ mod sanitizers { scheduler::Pallet::::set_claim_queue(BTreeMap::from([ ( CoreIndex::from(0), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(0) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 1.into(), + core_index: CoreIndex(0), + }]), ), ( CoreIndex::from(1), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(1) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 1.into(), + core_index: CoreIndex(1), + }]), ), ( CoreIndex::from(2), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 1.into(), core_index: CoreIndex(2) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 1.into(), + core_index: CoreIndex(2), + }]), ), ( CoreIndex::from(3), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(3) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 2.into(), + core_index: CoreIndex(3), + }]), ), ( CoreIndex::from(4), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(4) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 2.into(), + core_index: CoreIndex(4), + }]), ), ( CoreIndex::from(5), - VecDeque::from([ParasEntry::new( - Assignment::Pool { para_id: 2.into(), core_index: CoreIndex(5) }, - RELAY_PARENT_NUM, - )]), + VecDeque::from([Assignment::Pool { + para_id: 2.into(), + core_index: CoreIndex(5), + }]), ), ])); @@ -3996,7 +4080,7 @@ mod sanitizers { // State sanity checks assert_eq!( - scheduler::Pallet::::scheduled_paras().collect::>(), + Pallet::::eligible_paras(&Default::default()).collect::>(), vec![ (CoreIndex(0), ParaId::from(1)), (CoreIndex(1), ParaId::from(1)), @@ -4007,7 +4091,7 @@ mod sanitizers { ] ); let mut scheduled: BTreeMap> = BTreeMap::new(); - for (core_idx, para_id) in scheduler::Pallet::::scheduled_paras() { + for (core_idx, para_id) in Pallet::::eligible_paras(&Default::default()) { scheduled.entry(para_id).or_default().insert(core_idx); } diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs index a0996d5df0e..e9327bc7641 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs @@ -18,8 +18,7 @@ //! functions. use crate::{ - configuration, disputes, dmp, hrmp, inclusion, initializer, paras, paras_inherent, - scheduler::{self, CoreOccupied}, + configuration, disputes, dmp, hrmp, inclusion, initializer, paras, paras_inherent, scheduler, session_info, shared, }; use alloc::{ @@ -67,15 +66,6 @@ pub fn validator_groups( /// Implementation for the `availability_cores` function of the runtime API. pub fn availability_cores() -> Vec>> { - let cores = scheduler::AvailabilityCores::::get(); - let now = frame_system::Pallet::::block_number() + One::one(); - - // This explicit update is only strictly required for session boundaries: - // - // At the end of a session we clear the claim queues: Without this update call, nothing would be - // scheduled to the client. - scheduler::Pallet::::free_cores_and_fill_claim_queue(Vec::new(), now); - let time_out_for = scheduler::Pallet::::availability_timeout_predicate(); let group_responsible_for = @@ -95,76 +85,42 @@ pub fn availability_cores() -> Vec = scheduler::Pallet::::scheduled_paras().collect(); - - cores - .into_iter() - .enumerate() - .map(|(i, core)| match core { - CoreOccupied::Paras(entry) => { - // Due to https://github.com/paritytech/polkadot-sdk/issues/64, using the new storage types would cause - // this runtime API to panic. We explicitly handle the storage for version 0 to - // prevent that. When removing the inclusion v0 -> v1 migration, this bit of code - // can also be removed. - let pending_availability = if inclusion::Pallet::::on_chain_storage_version() == - StorageVersion::new(0) - { - inclusion::migration::v0::PendingAvailability::::get(entry.para_id()) - .expect("Occupied core always has pending availability; qed") - } else { - let candidate = inclusion::Pallet::::pending_availability_with_core( - entry.para_id(), - CoreIndex(i as u32), - ) - .expect("Occupied core always has pending availability; qed"); - - // Translate to the old candidate format, as we don't need the commitments now. - inclusion::migration::v0::CandidatePendingAvailability { - core: candidate.core_occupied(), - hash: candidate.candidate_hash(), - descriptor: candidate.candidate_descriptor().clone(), - availability_votes: candidate.availability_votes().clone(), - backers: candidate.backers().clone(), - relay_parent_number: candidate.relay_parent_number(), - backed_in_number: candidate.backed_in_number(), - backing_group: candidate.backing_group(), - } - }; - - let backed_in_number = pending_availability.backed_in_number; + let claim_queue = scheduler::Pallet::::get_claim_queue(); + let occupied_cores: BTreeMap> = + inclusion::Pallet::::get_occupied_cores().collect(); + let n_cores = scheduler::Pallet::::num_availability_cores(); + (0..n_cores) + .map(|core_idx| { + let core_idx = CoreIndex(core_idx as u32); + if let Some(pending_availability) = occupied_cores.get(&core_idx) { // Use the same block number for determining the responsible group as what the // backing subsystem would use when it calls validator_groups api. let backing_group_allocation_time = - pending_availability.relay_parent_number + One::one(); + pending_availability.relay_parent_number() + One::one(); CoreState::Occupied(OccupiedCore { - next_up_on_available: scheduler::Pallet::::next_up_on_available(CoreIndex( - i as u32, - )), - occupied_since: backed_in_number, - time_out_at: time_out_for(backed_in_number).live_until, - next_up_on_time_out: scheduler::Pallet::::next_up_on_time_out(CoreIndex( - i as u32, - )), - availability: pending_availability.availability_votes.clone(), + next_up_on_available: scheduler::Pallet::::next_up_on_available(core_idx), + occupied_since: pending_availability.backed_in_number(), + time_out_at: time_out_for(pending_availability.backed_in_number()).live_until, + next_up_on_time_out: scheduler::Pallet::::next_up_on_available(core_idx), + availability: pending_availability.availability_votes().clone(), group_responsible: group_responsible_for( backing_group_allocation_time, - pending_availability.core, + pending_availability.core_occupied(), ), - candidate_hash: pending_availability.hash, - candidate_descriptor: pending_availability.descriptor, + candidate_hash: pending_availability.candidate_hash(), + candidate_descriptor: pending_availability.candidate_descriptor().clone(), }) - }, - CoreOccupied::Free => { - if let Some(para_id) = scheduled.get(&CoreIndex(i as _)).cloned() { + } else { + if let Some(assignment) = claim_queue.get(&core_idx).and_then(|q| q.front()) { CoreState::Scheduled(polkadot_primitives::ScheduledCore { - para_id, + para_id: assignment.para_id(), collator: None, }) } else { CoreState::Free } - }, + } }) .collect() } @@ -195,13 +151,12 @@ where build() }, OccupiedCoreAssumption::TimedOut => build(), - OccupiedCoreAssumption::Free => { - if >::pending_availability(para_id).is_some() { + OccupiedCoreAssumption::Free => + if !>::candidates_pending_availability(para_id).is_empty() { None } else { build() - } - }, + }, } } @@ -240,10 +195,12 @@ pub fn assumed_validation_data( let persisted_validation_data = make_validation_data().or_else(|| { // Try again with force enacting the pending candidates. This check only makes sense if // there are any pending candidates. - inclusion::Pallet::::pending_availability(para_id).and_then(|_| { - inclusion::Pallet::::force_enact(para_id); - make_validation_data() - }) + (!inclusion::Pallet::::candidates_pending_availability(para_id).is_empty()) + .then_some(()) + .and_then(|_| { + inclusion::Pallet::::force_enact(para_id); + make_validation_data() + }) }); // If we were successful, also query current validation code hash. persisted_validation_data.zip(paras::CurrentCodeHash::::get(¶_id)) @@ -319,7 +276,7 @@ pub fn validation_code( pub fn candidate_pending_availability( para_id: ParaId, ) -> Option> { - inclusion::Pallet::::candidate_pending_availability(para_id) + inclusion::Pallet::::first_candidate_pending_availability(para_id) } /// Implementation for the `candidate_events` function of the runtime API. @@ -568,23 +525,12 @@ pub fn approval_voting_params() -> ApprovalVotingParams /// Returns the claimqueue from the scheduler pub fn claim_queue() -> BTreeMap> { - let now = >::block_number() + One::one(); - - // This is needed so that the claim queue always has the right size (equal to - // scheduling_lookahead). Otherwise, if a candidate is backed in the same block where the - // previous candidate is included, the claim queue will have already pop()-ed the next item - // from the queue and the length would be `scheduling_lookahead - 1`. - >::free_cores_and_fill_claim_queue(Vec::new(), now); let config = configuration::ActiveConfig::::get(); // Extra sanity, config should already never be smaller than 1: let n_lookahead = config.scheduler_params.lookahead.max(1); - - scheduler::ClaimQueue::::get() + scheduler::Pallet::::get_claim_queue() .into_iter() .map(|(core_index, entries)| { - // on cores timing out internal claim queue size may be temporarily longer than it - // should be as the timed out assignment might got pushed back to an already full claim - // queue: ( core_index, entries.into_iter().map(|e| e.para_id()).take(n_lookahead as usize).collect(), diff --git a/polkadot/runtime/parachains/src/scheduler.rs b/polkadot/runtime/parachains/src/scheduler.rs index 445583d929a..329df3a8a9d 100644 --- a/polkadot/runtime/parachains/src/scheduler.rs +++ b/polkadot/runtime/parachains/src/scheduler.rs @@ -36,14 +36,9 @@ //! number of groups as availability cores. Validator groups will be assigned to different //! availability cores over time. -use core::iter::Peekable; - use crate::{configuration, initializer::SessionChangeNotification, paras}; use alloc::{ - collections::{ - btree_map::{self, BTreeMap}, - vec_deque::VecDeque, - }, + collections::{btree_map::BTreeMap, btree_set::BTreeSet, vec_deque::VecDeque}, vec::Vec, }; use frame_support::{pallet_prelude::*, traits::Defensive}; @@ -71,7 +66,7 @@ pub mod migration; pub mod pallet { use super::*; - const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); #[pallet::pallet] #[pallet::without_storage_info] @@ -93,47 +88,6 @@ pub mod pallet { #[pallet::storage] pub type ValidatorGroups = StorageValue<_, Vec>, ValueQuery>; - /// One entry for each availability core. The i'th parachain belongs to the i'th core, with the - /// remaining cores all being on demand parachain multiplexers. - /// - /// Bounded by the maximum of either of these two values: - /// * The number of parachains and parathread multiplexers - /// * The number of validators divided by `configuration.max_validators_per_core`. - #[pallet::storage] - pub type AvailabilityCores = StorageValue<_, Vec>, ValueQuery>; - - /// Representation of a core in `AvailabilityCores`. - /// - /// This is not to be confused with `CoreState` which is an enriched variant of this and exposed - /// to the node side. It also provides information about scheduled/upcoming assignments for - /// example and is computed on the fly in the `availability_cores` runtime call. - #[derive(Encode, Decode, TypeInfo, RuntimeDebug, PartialEq)] - pub enum CoreOccupied { - /// No candidate is waiting availability on this core right now (the core is not occupied). - Free, - /// A para is currently waiting for availability/inclusion on this core. - Paras(ParasEntry), - } - - /// Convenience type alias for `CoreOccupied`. - pub type CoreOccupiedType = CoreOccupied>; - - impl CoreOccupied { - /// Is core free? - pub fn is_free(&self) -> bool { - matches!(self, Self::Free) - } - } - - /// Reasons a core might be freed. - #[derive(Clone, Copy)] - pub enum FreedReason { - /// The core's work concluded and the parablock assigned to it is considered available. - Concluded, - /// The core's work timed out. - TimedOut, - } - /// The block number where the session start occurred. Used to track how many group rotations /// have occurred. /// @@ -145,40 +99,9 @@ pub mod pallet { pub type SessionStartBlock = StorageValue<_, BlockNumberFor, ValueQuery>; /// One entry for each availability core. The `VecDeque` represents the assignments to be - /// scheduled on that core. The value contained here will not be valid after the end of - /// a block. Runtime APIs should be used to determine scheduled cores for the upcoming block. + /// scheduled on that core. #[pallet::storage] - pub type ClaimQueue = - StorageValue<_, BTreeMap>>, ValueQuery>; - - /// Assignments as tracked in the claim queue. - #[derive(Encode, Decode, TypeInfo, RuntimeDebug, PartialEq, Clone)] - pub struct ParasEntry { - /// The underlying [`Assignment`]. - pub assignment: Assignment, - /// The number of times the entry has timed out in availability already. - pub availability_timeouts: u32, - /// The block height until this entry needs to be backed. - /// - /// If missed the entry will be removed from the claim queue without ever having occupied - /// the core. - pub ttl: N, - } - - /// Convenience type declaration for `ParasEntry`. - pub type ParasEntryType = ParasEntry>; - - impl ParasEntry { - /// Create a new `ParasEntry`. - pub fn new(assignment: Assignment, now: N) -> Self { - ParasEntry { assignment, availability_timeouts: 0, ttl: now } - } - - /// Return `Id` from the underlying `Assignment`. - pub fn para_id(&self) -> ParaId { - self.assignment.para_id() - } - } + pub type ClaimQueue = StorageValue<_, BTreeMap>, ValueQuery>; /// Availability timeout status of a core. pub(crate) struct AvailabilityTimeoutStatus { @@ -195,30 +118,6 @@ pub mod pallet { } } -type PositionInClaimQueue = u32; - -struct ClaimQueueIterator { - next_idx: u32, - queue: Peekable>>, -} - -impl Iterator for ClaimQueueIterator { - type Item = (CoreIndex, VecDeque); - - fn next(&mut self) -> Option { - let (idx, _) = self.queue.peek()?; - let val = if idx != &CoreIndex(self.next_idx) { - log::trace!(target: LOG_TARGET, "idx did not match claim queue idx: {:?} vs {:?}", idx, self.next_idx); - (CoreIndex(self.next_idx), VecDeque::new()) - } else { - let (idx, q) = self.queue.next()?; - (idx, q) - }; - self.next_idx += 1; - Some(val) - } -} - impl Pallet { /// Called by the initializer to initialize the scheduler pallet. pub(crate) fn initializer_initialize(_now: BlockNumberFor) -> Weight { @@ -228,31 +127,22 @@ impl Pallet { /// Called by the initializer to finalize the scheduler pallet. pub(crate) fn initializer_finalize() {} - /// Called before the initializer notifies of a new session. - pub(crate) fn pre_new_session() { - Self::push_claim_queue_items_to_assignment_provider(); - Self::push_occupied_cores_to_assignment_provider(); - } - /// Called by the initializer to note that a new session has started. pub(crate) fn initializer_on_new_session( notification: &SessionChangeNotification>, ) { - let SessionChangeNotification { validators, new_config, .. } = notification; + let SessionChangeNotification { validators, new_config, prev_config, .. } = notification; let config = new_config; + let assigner_cores = config.scheduler_params.num_cores; let n_cores = core::cmp::max( - T::AssignmentProvider::session_core_count(), + assigner_cores, match config.scheduler_params.max_validators_per_core { Some(x) if x != 0 => validators.len() as u32 / x, _ => 0, }, ); - AvailabilityCores::::mutate(|cores| { - cores.resize_with(n_cores as _, || CoreOccupied::Free); - }); - // shuffle validators into groups. if n_cores == 0 || validators.is_empty() { ValidatorGroups::::set(Vec::new()); @@ -295,151 +185,24 @@ impl Pallet { ValidatorGroups::::set(groups); } + // Resize and populate claim queue. + Self::maybe_resize_claim_queue(prev_config.scheduler_params.num_cores, assigner_cores); + Self::populate_claim_queue_after_session_change(); + let now = frame_system::Pallet::::block_number() + One::one(); SessionStartBlock::::set(now); } - /// Free unassigned cores. Provide a list of cores that should be considered newly-freed along - /// with the reason for them being freed. Returns a tuple of concluded and timedout paras. - fn free_cores( - just_freed_cores: impl IntoIterator, - ) -> (BTreeMap, BTreeMap>) { - let mut timedout_paras: BTreeMap> = BTreeMap::new(); - let mut concluded_paras = BTreeMap::new(); - - AvailabilityCores::::mutate(|cores| { - let c_len = cores.len(); - - just_freed_cores - .into_iter() - .filter(|(freed_index, _)| (freed_index.0 as usize) < c_len) - .for_each(|(freed_index, freed_reason)| { - match core::mem::replace(&mut cores[freed_index.0 as usize], CoreOccupied::Free) - { - CoreOccupied::Free => {}, - CoreOccupied::Paras(entry) => { - match freed_reason { - FreedReason::Concluded => { - concluded_paras.insert(freed_index, entry.assignment); - }, - FreedReason::TimedOut => { - timedout_paras.insert(freed_index, entry); - }, - }; - }, - }; - }) - }); - - (concluded_paras, timedout_paras) - } - - /// Get an iterator into the claim queues. - /// - /// This iterator will have an item for each and every core index up to the maximum core index - /// found in the claim queue. In other words there will be no holes/missing core indices, - /// between core 0 and the maximum, even if the claim queue was missing entries for particular - /// indices in between. (The iterator will return an empty `VecDeque` for those indices. - fn claim_queue_iterator() -> impl Iterator>)> { - let queues = ClaimQueue::::get(); - return ClaimQueueIterator::> { - next_idx: 0, - queue: queues.into_iter().peekable(), - } - } - - /// Note that the given cores have become occupied. Update the claim queue accordingly. - /// This will not push a new entry onto the claim queue, so the length after this call will be - /// the expected length - 1. The claim_queue runtime API will take care of adding another entry - /// here, to ensure the right lookahead. - pub(crate) fn occupied( - now_occupied: BTreeMap, - ) -> BTreeMap { - let mut availability_cores = AvailabilityCores::::get(); - - log::debug!(target: LOG_TARGET, "[occupied] now_occupied {:?}", now_occupied); - - let pos_mapping: BTreeMap = now_occupied - .iter() - .flat_map(|(core_idx, para_id)| { - match Self::remove_from_claim_queue(*core_idx, *para_id) { - Err(e) => { - log::debug!( - target: LOG_TARGET, - "[occupied] error on remove_from_claim queue {}", - e - ); - None - }, - Ok((pos_in_claim_queue, pe)) => { - availability_cores[core_idx.0 as usize] = CoreOccupied::Paras(pe); - - Some((*core_idx, pos_in_claim_queue)) - }, - } - }) - .collect(); - - // Drop expired claims after processing now_occupied. - Self::drop_expired_claims_from_claim_queue(); - - AvailabilityCores::::set(availability_cores); - - pos_mapping - } - - /// Iterates through every element in all claim queues and tries to add new assignments from the - /// `AssignmentProvider`. A claim is considered expired if it's `ttl` field is lower than the - /// current block height. - fn drop_expired_claims_from_claim_queue() { - let now = frame_system::Pallet::::block_number(); - let availability_cores = AvailabilityCores::::get(); - let ttl = configuration::ActiveConfig::::get().scheduler_params.ttl; - - ClaimQueue::::mutate(|cq| { - for (idx, _) in (0u32..).zip(availability_cores) { - let core_idx = CoreIndex(idx); - if let Some(core_claim_queue) = cq.get_mut(&core_idx) { - let mut i = 0; - let mut num_dropped = 0; - while i < core_claim_queue.len() { - let maybe_dropped = if let Some(entry) = core_claim_queue.get(i) { - if entry.ttl < now { - core_claim_queue.remove(i) - } else { - None - } - } else { - None - }; - - if let Some(dropped) = maybe_dropped { - num_dropped += 1; - T::AssignmentProvider::report_processed(dropped.assignment); - } else { - i += 1; - } - } - - for _ in 0..num_dropped { - // For all claims dropped due to TTL, attempt to pop a new entry to - // the back of the claim queue. - if let Some(assignment) = - T::AssignmentProvider::pop_assignment_for_core(core_idx) - { - core_claim_queue.push_back(ParasEntry::new(assignment, now + ttl)); - } - } - } - } - }); - } - /// Get the validators in the given group, if the group index is valid for this session. pub(crate) fn group_validators(group_index: GroupIndex) -> Option> { ValidatorGroups::::get().get(group_index.0 as usize).map(|g| g.clone()) } + /// Get the number of cores. + pub(crate) fn num_availability_cores() -> usize { + ValidatorGroups::::decode_len().unwrap_or(0) + } + /// 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. @@ -531,183 +294,137 @@ impl Pallet { /// Return the next thing that will be scheduled on this core assuming it is currently /// occupied and the candidate occupying it became available. pub(crate) fn next_up_on_available(core: CoreIndex) -> Option { - ClaimQueue::::get() - .get(&core) - .and_then(|a| a.front().map(|pe| Self::paras_entry_to_scheduled_core(pe))) + // Since this is being called from a runtime API, we need to workaround for #64. + if Self::on_chain_storage_version() == StorageVersion::new(2) { + migration::v2::ClaimQueue::::get() + .get(&core) + .and_then(|a| a.front().map(|entry| entry.assignment.para_id())) + } else { + ClaimQueue::::get() + .get(&core) + .and_then(|a| a.front().map(|assignment| assignment.para_id())) + } + .map(|para_id| ScheduledCore { para_id, collator: None }) } - fn paras_entry_to_scheduled_core(pe: &ParasEntryType) -> ScheduledCore { - ScheduledCore { para_id: pe.para_id(), collator: None } + // Since this is being called from a runtime API, we need to workaround for #64. + pub(crate) fn get_claim_queue() -> BTreeMap> { + if Self::on_chain_storage_version() == StorageVersion::new(2) { + migration::v2::ClaimQueue::::get() + .into_iter() + .map(|(core_index, entries)| { + (core_index, entries.into_iter().map(|e| e.assignment).collect()) + }) + .collect() + } else { + ClaimQueue::::get() + } } - /// Return the next thing that will be scheduled on this core assuming it is currently - /// occupied and the candidate occupying it times out. - pub(crate) fn next_up_on_time_out(core: CoreIndex) -> Option { - let max_availability_timeouts = configuration::ActiveConfig::::get() - .scheduler_params - .max_availability_timeouts; - Self::next_up_on_available(core).or_else(|| { - // Or, if none, the claim currently occupying the core, - // as it would be put back on the queue after timing out if number of retries is not at - // the maximum. - let cores = AvailabilityCores::::get(); - cores.get(core.0 as usize).and_then(|c| match c { - CoreOccupied::Free => None, - CoreOccupied::Paras(pe) => - if pe.availability_timeouts < max_availability_timeouts { - Some(Self::paras_entry_to_scheduled_core(pe)) - } else { - None - }, - }) - }) - } + /// For each core that isn't part of the `except_for` set, pop the first item of the claim queue + /// 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; + // 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 { + let core_idx = CoreIndex::from(core_idx); + + if !except_for.contains(&core_idx) { + let core_idx = CoreIndex::from(core_idx); - /// Pushes occupied cores to the assignment provider. - fn push_occupied_cores_to_assignment_provider() { - AvailabilityCores::::mutate(|cores| { - for core in cores.iter_mut() { - match core::mem::replace(core, CoreOccupied::Free) { - CoreOccupied::Free => continue, - CoreOccupied::Paras(entry) => { - Self::maybe_push_assignment(entry); - }, + if let Some(dropped_para) = Self::pop_front_of_claim_queue(&core_idx) { + T::AssignmentProvider::report_processed(dropped_para); } - } - }); - } - // on new session - fn push_claim_queue_items_to_assignment_provider() { - for (_, claim_queue) in ClaimQueue::::take() { - // Push back in reverse order so that when we pop from the provider again, - // the entries in the claim queue are in the same order as they are right now. - for para_entry in claim_queue.into_iter().rev() { - Self::maybe_push_assignment(para_entry); + Self::fill_claim_queue(core_idx, n_lookahead); } } } - /// Push assignments back to the provider on session change unless the paras - /// timed out on availability before. - fn maybe_push_assignment(pe: ParasEntryType) { - if pe.availability_timeouts == 0 { - T::AssignmentProvider::push_back_assignment(pe.assignment); + // on new session + fn maybe_resize_claim_queue(old_core_count: u32, new_core_count: u32) { + if new_core_count < old_core_count { + ClaimQueue::::mutate(|cq| { + let to_remove: Vec<_> = cq + .range(CoreIndex(new_core_count)..CoreIndex(old_core_count)) + .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()); + } + } + }); } } - /// Frees cores and fills the free claim queue spots by popping from the `AssignmentProvider`. - pub fn free_cores_and_fill_claim_queue( - just_freed_cores: impl IntoIterator, - now: BlockNumberFor, - ) { - let (mut concluded_paras, mut timedout_paras) = Self::free_cores(just_freed_cores); - - // This can only happen on new sessions at which we move all assignments back to the - // provider. Hence, there's nothing we need to do here. - if ValidatorGroups::::decode_len().map_or(true, |l| l == 0) { - return - } - let n_session_cores = T::AssignmentProvider::session_core_count(); - let cq = ClaimQueue::::get(); + // Populate the claim queue. To be called on new session, after all the other modules were + // initialized. + fn populate_claim_queue_after_session_change() { 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 max_availability_timeouts = config.scheduler_params.max_availability_timeouts; - let ttl = config.scheduler_params.ttl; + let new_core_count = config.scheduler_params.num_cores; - for core_idx in 0..n_session_cores { + for core_idx in 0..new_core_count { let core_idx = CoreIndex::from(core_idx); + Self::fill_claim_queue(core_idx, n_lookahead); + } + } - let n_lookahead_used = cq.get(&core_idx).map_or(0, |v| v.len() as u32); - - // add previously timedout paras back into the queue - if let Some(mut entry) = timedout_paras.remove(&core_idx) { - if entry.availability_timeouts < max_availability_timeouts { - // Increment the timeout counter. - entry.availability_timeouts += 1; - if n_lookahead_used < n_lookahead { - entry.ttl = now + ttl; - } else { - // Over max capacity, we need to bump ttl (we exceeded the claim queue - // size, so otherwise the entry might get dropped before reaching the top): - entry.ttl = now + ttl + One::one(); - } - Self::add_to_claim_queue(core_idx, entry); - // The claim has been added back into the claim queue. - // Do not pop another assignment for the core. - continue - } else { - // Consider timed out assignments for on demand parachains as concluded for - // the assignment provider - let ret = concluded_paras.insert(core_idx, entry.assignment); - debug_assert!(ret.is_none()); + /// Push some assignments back to the provider. + fn push_back_to_assignment_provider( + assignments: impl core::iter::DoubleEndedIterator, + ) { + // Push back in reverse order so that when we pop from the provider again, + // the entries in the claim queue are in the same order as they are right + // now. + for assignment in assignments.rev() { + T::AssignmentProvider::push_back_assignment(assignment); + } + } + + fn fill_claim_queue(core_idx: CoreIndex, n_lookahead: u32) { + ClaimQueue::::mutate(|la| { + let cq = la.entry(core_idx).or_default(); + + let mut n_lookahead_used = cq.len() as u32; + + // If the claim queue used to be empty, we need to double the first assignment. + // Otherwise, the para will only be able to get the collation in right at the next block + // (synchronous backing). + // Only do this if the configured lookahead is greater than 1. Otherwise, it doesn't + // make sense. + if n_lookahead_used == 0 && n_lookahead > 1 { + if let Some(assignment) = T::AssignmentProvider::pop_assignment_for_core(core_idx) { + T::AssignmentProvider::assignment_duplicated(&assignment); + cq.push_back(assignment.clone()); + cq.push_back(assignment); + n_lookahead_used += 2; } } - if let Some(concluded_para) = concluded_paras.remove(&core_idx) { - T::AssignmentProvider::report_processed(concluded_para); - } for _ in n_lookahead_used..n_lookahead { if let Some(assignment) = T::AssignmentProvider::pop_assignment_for_core(core_idx) { - Self::add_to_claim_queue(core_idx, ParasEntry::new(assignment, now + ttl)); + cq.push_back(assignment); + } else { + break } } - } - debug_assert!(timedout_paras.is_empty()); - debug_assert!(concluded_paras.is_empty()); - } - - fn add_to_claim_queue(core_idx: CoreIndex, pe: ParasEntryType) { - ClaimQueue::::mutate(|la| { - la.entry(core_idx).or_default().push_back(pe); + // If we didn't end up pushing anything, remove the entry. We don't want to waste the + // space if we've no assignments. + if cq.is_empty() { + la.remove(&core_idx); + } }); } - /// Returns `ParasEntry` with `para_id` at `core_idx` if found. - fn remove_from_claim_queue( - core_idx: CoreIndex, - para_id: ParaId, - ) -> Result<(PositionInClaimQueue, ParasEntryType), &'static str> { - ClaimQueue::::mutate(|cq| { - let core_claims = cq.get_mut(&core_idx).ok_or("core_idx not found in lookahead")?; - - let pos = core_claims - .iter() - .position(|pe| pe.para_id() == para_id) - .ok_or("para id not found at core_idx lookahead")?; - - let pe = core_claims.remove(pos).ok_or("remove returned None")?; - - Ok((pos as u32, pe)) - }) - } - - /// Paras scheduled next in the claim queue. - pub(crate) fn scheduled_paras() -> impl Iterator { - let claim_queue = ClaimQueue::::get(); - claim_queue - .into_iter() - .filter_map(|(core_idx, v)| v.front().map(|e| (core_idx, e.assignment.para_id()))) - } - - /// Paras that may get backed on cores. - /// - /// 1. The para must be scheduled on core. - /// 2. Core needs to be free, otherwise backing is not possible. - pub(crate) fn eligible_paras() -> impl Iterator { - let availability_cores = AvailabilityCores::::get(); - - Self::claim_queue_iterator().zip(availability_cores.into_iter()).filter_map( - |((core_idx, queue), core)| { - if core != CoreOccupied::Free { - return None - } - let next_scheduled = queue.front()?; - Some((core_idx, next_scheduled.assignment.para_id())) - }, - ) + fn pop_front_of_claim_queue(core_idx: &CoreIndex) -> Option { + ClaimQueue::::mutate(|cq| cq.get_mut(core_idx)?.pop_front()) } #[cfg(any(feature = "try-runtime", test))] @@ -726,7 +443,7 @@ impl Pallet { } #[cfg(test)] - pub(crate) fn set_claim_queue(claim_queue: BTreeMap>>) { + pub(crate) fn set_claim_queue(claim_queue: BTreeMap>) { ClaimQueue::::set(claim_queue); } } diff --git a/polkadot/runtime/parachains/src/scheduler/common.rs b/polkadot/runtime/parachains/src/scheduler/common.rs index 114cd4b940b..bf8a2bee74e 100644 --- a/polkadot/runtime/parachains/src/scheduler/common.rs +++ b/polkadot/runtime/parachains/src/scheduler/common.rs @@ -77,11 +77,6 @@ pub trait AssignmentProvider { #[cfg(any(feature = "runtime-benchmarks", test))] fn get_mock_assignment(core_idx: CoreIndex, para_id: ParaId) -> Assignment; - /// How many cores are allocated to this provider. - /// - /// As the name suggests the core count has to be session buffered: - /// - /// - Core count has to be predetermined for the next session in the current session. - /// - Core count must not change during a session. - fn session_core_count() -> u32; + /// Report that an assignment was duplicated by the scheduler. + fn assignment_duplicated(assignment: &Assignment); } diff --git a/polkadot/runtime/parachains/src/scheduler/migration.rs b/polkadot/runtime/parachains/src/scheduler/migration.rs index 125f105ef70..e741711cad6 100644 --- a/polkadot/runtime/parachains/src/scheduler/migration.rs +++ b/polkadot/runtime/parachains/src/scheduler/migration.rs @@ -268,7 +268,7 @@ pub type MigrateV0ToV1 = VersionedMigration< ::DbWeight, >; -mod v2 { +pub(crate) mod v2 { use super::*; use crate::scheduler; @@ -406,3 +406,89 @@ pub type MigrateV1ToV2 = VersionedMigration< Pallet, ::DbWeight, >; + +/// Migration for TTL and availability timeout retries removal. +/// AvailabilityCores storage is removed and ClaimQueue now holds `Assignment`s instead of +/// `ParasEntryType` +mod v3 { + use super::*; + use crate::scheduler; + + #[storage_alias] + pub(crate) type ClaimQueue = + StorageValue, BTreeMap>, ValueQuery>; + /// Migration to V3 + pub struct UncheckedMigrateToV3(core::marker::PhantomData); + + impl UncheckedOnRuntimeUpgrade for UncheckedMigrateToV3 { + fn on_runtime_upgrade() -> Weight { + let mut weight: Weight = Weight::zero(); + + // Migrate ClaimQueuee to new format. + + let old = v2::ClaimQueue::::take(); + let new = old + .into_iter() + .map(|(k, v)| { + ( + k, + v.into_iter() + .map(|paras_entry| paras_entry.assignment) + .collect::>(), + ) + }) + .collect::>>(); + + v3::ClaimQueue::::put(new); + + // Clear AvailabilityCores storage + v2::AvailabilityCores::::kill(); + + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + + log::info!(target: scheduler::LOG_TARGET, "Migrating para scheduler storage to v3"); + + weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::DispatchError> { + log::trace!( + target: crate::scheduler::LOG_TARGET, + "ClaimQueue before migration: {}", + v2::ClaimQueue::::get().len() + ); + + let bytes = u32::to_be_bytes(v2::ClaimQueue::::get().len() as u32); + + Ok(bytes.to_vec()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::DispatchError> { + log::trace!(target: crate::scheduler::LOG_TARGET, "Running post_upgrade()"); + + let old_len = u32::from_be_bytes(state.try_into().unwrap()); + ensure!( + v3::ClaimQueue::::get().len() as u32 == old_len, + "Old ClaimQueue completely moved to new ClaimQueue after migration" + ); + + ensure!( + !v2::AvailabilityCores::::exists(), + "AvailabilityCores storage should have been completely killed" + ); + + Ok(()) + } + } +} + +/// Migrate `V2` to `V3` of the storage format. +pub type MigrateV2ToV3 = VersionedMigration< + 2, + 3, + v3::UncheckedMigrateToV3, + Pallet, + ::DbWeight, +>; diff --git a/polkadot/runtime/parachains/src/scheduler/tests.rs b/polkadot/runtime/parachains/src/scheduler/tests.rs index 5f80114b596..5be7e084f3b 100644 --- a/polkadot/runtime/parachains/src/scheduler/tests.rs +++ b/polkadot/runtime/parachains/src/scheduler/tests.rs @@ -16,7 +16,7 @@ use super::*; -use alloc::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; +use alloc::collections::btree_map::BTreeMap; use frame_support::assert_ok; use polkadot_primitives::{ BlockNumber, SchedulerParams, SessionIndex, ValidationCode, ValidatorId, @@ -27,14 +27,14 @@ use crate::{ configuration::HostConfiguration, initializer::SessionChangeNotification, mock::{ - new_test_ext, MockAssigner, MockGenesisConfig, Paras, ParasShared, RuntimeOrigin, - Scheduler, System, Test, + new_test_ext, Configuration, MockAssigner, MockGenesisConfig, Paras, ParasShared, + RuntimeOrigin, Scheduler, System, Test, }, paras::{ParaGenesisArgs, ParaKind}, scheduler::{self, common::Assignment, ClaimQueue}, }; -fn schedule_blank_para(id: ParaId) { +fn register_para(id: ParaId) { let validation_code: ValidationCode = vec![1, 2, 3].into(); assert_ok!(Paras::schedule_para_initialize( id, @@ -58,17 +58,18 @@ fn run_to_block( Scheduler::initializer_finalize(); Paras::initializer_finalize(b); - if let Some(notification) = new_session(b + 1) { - let mut notification_with_session_index = notification; + if let Some(mut notification) = new_session(b + 1) { // We will make every session change trigger an action queue. Normally this may require // 2 or more session changes. - if notification_with_session_index.session_index == SessionIndex::default() { - notification_with_session_index.session_index = ParasShared::scheduled_session(); + if notification.session_index == SessionIndex::default() { + notification.session_index = ParasShared::scheduled_session(); } - Scheduler::pre_new_session(); - Paras::initializer_on_new_session(¬ification_with_session_index); - Scheduler::initializer_on_new_session(¬ification_with_session_index); + Configuration::force_set_active_config(notification.new_config.clone()); + + Paras::initializer_on_new_session(¬ification); + + Scheduler::initializer_on_new_session(¬ification); } System::on_finalize(b); @@ -79,28 +80,8 @@ fn run_to_block( Paras::initializer_initialize(b + 1); Scheduler::initializer_initialize(b + 1); - // In the real runtime this is expected to be called by the `InclusionInherent` pallet. - Scheduler::free_cores_and_fill_claim_queue(BTreeMap::new(), b + 1); - } -} - -fn run_to_end_of_block( - to: BlockNumber, - new_session: impl Fn(BlockNumber) -> Option>, -) { - run_to_block(to, &new_session); - - Scheduler::initializer_finalize(); - Paras::initializer_finalize(to); - - if let Some(notification) = new_session(to + 1) { - Scheduler::pre_new_session(); - - Paras::initializer_on_new_session(¬ification); - Scheduler::initializer_on_new_session(¬ification); + Scheduler::advance_claim_queue(&Default::default()); } - - System::on_finalize(to); } fn default_config() -> HostConfiguration { @@ -110,6 +91,7 @@ fn default_config() -> HostConfiguration { // `minimum_validation_upgrade_delay` is greater than `chain_availability_period` and // `thread_availability_period`. minimum_validation_upgrade_delay: 6, + #[allow(deprecated)] scheduler_params: SchedulerParams { group_rotation_frequency: 10, paras_availability_period: 3, @@ -129,172 +111,27 @@ fn genesis_config(config: &HostConfiguration) -> MockGenesisConfig } } -fn claimqueue_contains_para_ids(pids: Vec) -> bool { - let set: BTreeSet = ClaimQueue::::get() +/// Internal access to assignments at the top of the claim queue. +fn next_assignments() -> impl Iterator { + let claim_queue = ClaimQueue::::get(); + claim_queue .into_iter() - .flat_map(|(_, paras_entries)| paras_entries.into_iter().map(|pe| pe.assignment.para_id())) - .collect(); - - pids.into_iter().all(|pid| set.contains(&pid)) -} - -fn availability_cores_contains_para_ids(pids: Vec) -> bool { - let set: BTreeSet = AvailabilityCores::::get() - .into_iter() - .filter_map(|core| match core { - CoreOccupied::Free => None, - CoreOccupied::Paras(entry) => Some(entry.para_id()), - }) - .collect(); - - pids.into_iter().all(|pid| set.contains(&pid)) -} - -/// Internal access to entries at the top of the claim queue. -fn scheduled_entries() -> impl Iterator>)> { - let claimqueue = ClaimQueue::::get(); - claimqueue - .into_iter() - .filter_map(|(core_idx, v)| v.front().map(|e| (core_idx, e.clone()))) -} - -#[test] -fn claim_queue_iterator_handles_holes_correctly() { - let mut queue = BTreeMap::new(); - queue.insert(CoreIndex(1), ["abc"].into_iter().collect()); - queue.insert(CoreIndex(4), ["cde"].into_iter().collect()); - let queue = queue.into_iter().peekable(); - let mut i = ClaimQueueIterator { next_idx: 0, queue }; - - let (idx, e) = i.next().unwrap(); - assert_eq!(idx, CoreIndex(0)); - assert!(e.is_empty()); - - let (idx, e) = i.next().unwrap(); - assert_eq!(idx, CoreIndex(1)); - assert!(e.len() == 1); - - let (idx, e) = i.next().unwrap(); - assert_eq!(idx, CoreIndex(2)); - assert!(e.is_empty()); - - let (idx, e) = i.next().unwrap(); - assert_eq!(idx, CoreIndex(3)); - assert!(e.is_empty()); - - let (idx, e) = i.next().unwrap(); - assert_eq!(idx, CoreIndex(4)); - assert!(e.len() == 1); - - assert!(i.next().is_none()); + .filter_map(|(core_idx, v)| v.front().map(|a| (core_idx, a.clone()))) } #[test] -fn claimqueue_ttl_drop_fn_works() { +fn session_change_shuffles_validators() { let mut config = default_config(); - config.scheduler_params.lookahead = 3; + // Need five cores for this test + config.scheduler_params.num_cores = 5; let genesis_config = genesis_config(&config); - let para_id = ParaId::from(100); - let core_idx = CoreIndex::from(0); - let mut now = 10; - new_test_ext(genesis_config).execute_with(|| { - assert!(config.scheduler_params.ttl == 5); - // Register and run to a blockheight where the para is in a valid state. - schedule_blank_para(para_id); - run_to_block(now, |n| if n == now { Some(Default::default()) } else { None }); - - // Add a claim on core 0 with a ttl in the past. - let paras_entry = ParasEntry::new(Assignment::Bulk(para_id), now - 5 as u32); - Scheduler::add_to_claim_queue(core_idx, paras_entry.clone()); - - // Claim is in queue prior to call. - assert!(claimqueue_contains_para_ids::(vec![para_id])); - - // Claim is dropped post call. - Scheduler::drop_expired_claims_from_claim_queue(); - assert!(!claimqueue_contains_para_ids::(vec![para_id])); - - // Add a claim on core 0 with a ttl in the future (15). - let paras_entry = ParasEntry::new(Assignment::Bulk(para_id), now + 5); - Scheduler::add_to_claim_queue(core_idx, paras_entry.clone()); - - // Claim is in queue post call. - Scheduler::drop_expired_claims_from_claim_queue(); - assert!(claimqueue_contains_para_ids::(vec![para_id])); - - now = now + 6; - run_to_block(now, |_| None); - - // Claim is dropped - Scheduler::drop_expired_claims_from_claim_queue(); - assert!(!claimqueue_contains_para_ids::(vec![para_id])); + assert!(ValidatorGroups::::get().is_empty()); - // Add a claim on core 0 with a ttl == now (16) - let paras_entry = ParasEntry::new(Assignment::Bulk(para_id), now); - Scheduler::add_to_claim_queue(core_idx, paras_entry.clone()); - - // Claim is in queue post call. - Scheduler::drop_expired_claims_from_claim_queue(); - assert!(claimqueue_contains_para_ids::(vec![para_id])); - - now = now + 1; - run_to_block(now, |_| None); - - // Drop expired claim. - Scheduler::drop_expired_claims_from_claim_queue(); - assert!(!claimqueue_contains_para_ids::(vec![para_id])); - - // Add a claim on core 0 with a ttl == now (17) - let paras_entry_non_expired = ParasEntry::new(Assignment::Bulk(para_id), now); - let paras_entry_expired = ParasEntry::new(Assignment::Bulk(para_id), now - 2); - // ttls = [17, 15, 17] - Scheduler::add_to_claim_queue(core_idx, paras_entry_non_expired.clone()); - Scheduler::add_to_claim_queue(core_idx, paras_entry_expired.clone()); - Scheduler::add_to_claim_queue(core_idx, paras_entry_non_expired.clone()); - let cq = scheduler::ClaimQueue::::get(); - assert_eq!(cq.get(&core_idx).unwrap().len(), 3); - - // Add a claim to the test assignment provider. - let assignment = Assignment::Bulk(para_id); - - MockAssigner::add_test_assignment(assignment.clone()); - - // Drop expired claim. - Scheduler::drop_expired_claims_from_claim_queue(); - - let cq = scheduler::ClaimQueue::::get(); - let cqc = cq.get(&core_idx).unwrap(); - // Same number of claims, because a new claim is popped from `MockAssigner` instead of the - // expired one - assert_eq!(cqc.len(), 3); - - // The first 2 claims in the queue should have a ttl of 17, - // being the ones set up prior in this test as claims 1 and 3. - // The third claim is popped from the assignment provider and - // has a new ttl set by the scheduler of now + - // assignment_provider_ttl. ttls = [17, 17, 22] - assert!(cqc.iter().enumerate().all(|(index, entry)| { - match index { - 0 | 1 => entry.clone().ttl == 17, - 2 => entry.clone().ttl == 22, - _ => false, - } - })) - }); -} - -#[test] -fn session_change_shuffles_validators() { - let genesis_config = genesis_config(&default_config()); - - new_test_ext(genesis_config).execute_with(|| { - // Need five cores for this test - MockAssigner::set_core_count(5); run_to_block(1, |number| match number { 1 => Some(SessionChangeNotification { - new_config: default_config(), + new_config: config.clone(), validators: vec![ ValidatorId::from(Sr25519Keyring::Alice.public()), ValidatorId::from(Sr25519Keyring::Bob.public()), @@ -328,6 +165,8 @@ fn session_change_shuffles_validators() { fn session_change_takes_only_max_per_core() { let config = { let mut config = default_config(); + // Simulate 2 cores between all usage types + config.scheduler_params.num_cores = 2; config.scheduler_params.max_validators_per_core = Some(1); config }; @@ -335,9 +174,6 @@ fn session_change_takes_only_max_per_core() { let genesis_config = genesis_config(&config); new_test_ext(genesis_config).execute_with(|| { - // Simulate 2 cores between all usage types - MockAssigner::set_core_count(2); - run_to_block(1, |number| match number { 1 => Some(SessionChangeNotification { new_config: config.clone(), @@ -367,8 +203,12 @@ fn session_change_takes_only_max_per_core() { } #[test] -fn fill_claimqueue_fills() { - let config = default_config(); +// Test that `advance_claim_queue` doubles the first assignment only for a core that didn't use to +// have any assignments. +fn advance_claim_queue_doubles_assignment_only_if_empty() { + let mut config = default_config(); + config.scheduler_params.lookahead = 3; + config.scheduler_params.num_cores = 2; let genesis_config = genesis_config(&config); let para_a = ParaId::from(3_u32); @@ -380,18 +220,15 @@ fn fill_claimqueue_fills() { let assignment_c = Assignment::Bulk(para_c); new_test_ext(genesis_config).execute_with(|| { - MockAssigner::set_core_count(2); - let coretime_ttl = config.scheduler_params.ttl; - // Add 3 paras - schedule_blank_para(para_a); - schedule_blank_para(para_b); - schedule_blank_para(para_c); + register_para(para_a); + register_para(para_b); + register_para(para_c); // start a new session to activate, 2 validators for 2 cores. run_to_block(1, |number| match number { 1 => Some(SessionChangeNotification { - new_config: default_config(), + new_config: config.clone(), validators: vec![ ValidatorId::from(Sr25519Keyring::Alice.public()), ValidatorId::from(Sr25519Keyring::Bob.public()), @@ -406,224 +243,108 @@ fn fill_claimqueue_fills() { MockAssigner::add_test_assignment(assignment_b.clone()); MockAssigner::add_test_assignment(assignment_c.clone()); + // This will call advance_claim_queue run_to_block(2, |_| None); { - assert_eq!(Scheduler::claim_queue_len(), 3); - let scheduled: BTreeMap<_, _> = scheduled_entries().collect(); + assert_eq!(Scheduler::claim_queue_len(), 5); + let mut claim_queue = scheduler::ClaimQueue::::get(); - // Was added a block later, note the TTL. - assert_eq!( - scheduled.get(&CoreIndex(0)).unwrap(), - &ParasEntry { - assignment: assignment_a.clone(), - availability_timeouts: 0, - ttl: 2 + coretime_ttl - }, - ); - // Sits on the same core as `para_a` + // Because the claim queue used to be empty, the first assignment is doubled for every + // core so that the first para gets a fair shot at backing something. assert_eq!( - scheduler::ClaimQueue::::get().get(&CoreIndex(0)).unwrap()[1], - ParasEntry { - assignment: assignment_b.clone(), - availability_timeouts: 0, - ttl: 2 + coretime_ttl - } + claim_queue.remove(&CoreIndex(0)).unwrap(), + [assignment_a.clone(), assignment_a, assignment_b] + .into_iter() + .collect::>() ); assert_eq!( - scheduled.get(&CoreIndex(1)).unwrap(), - &ParasEntry { - assignment: assignment_c.clone(), - availability_timeouts: 0, - ttl: 2 + coretime_ttl - }, + claim_queue.remove(&CoreIndex(1)).unwrap(), + [assignment_c.clone(), assignment_c].into_iter().collect::>() ); } }); } #[test] -fn schedule_schedules_including_just_freed() { +// Test that `advance_claim_queue` doesn't populate for cores which have no assignments. +fn advance_claim_queue_no_entry_if_empty() { let mut config = default_config(); - // NOTE: This test expects on demand cores to each get slotted on to a different core - // and not fill up the claimqueue of each core first. - config.scheduler_params.lookahead = 1; + config.scheduler_params.lookahead = 3; + config.scheduler_params.num_cores = 2; let genesis_config = genesis_config(&config); let para_a = ParaId::from(3_u32); - let para_b = ParaId::from(4_u32); - let para_c = ParaId::from(5_u32); - let para_d = ParaId::from(6_u32); - let para_e = ParaId::from(7_u32); - let assignment_a = Assignment::Bulk(para_a); - let assignment_b = Assignment::Bulk(para_b); - let assignment_c = Assignment::Bulk(para_c); - let assignment_d = Assignment::Bulk(para_d); - let assignment_e = Assignment::Bulk(para_e); new_test_ext(genesis_config).execute_with(|| { - MockAssigner::set_core_count(3); - - // add 5 paras - schedule_blank_para(para_a); - schedule_blank_para(para_b); - schedule_blank_para(para_c); - schedule_blank_para(para_d); - schedule_blank_para(para_e); + // Add 1 para + register_para(para_a); - // start a new session to activate, 3 validators for 3 cores. + // start a new session to activate, 2 validators for 2 cores. run_to_block(1, |number| match number { 1 => Some(SessionChangeNotification { - new_config: default_config(), + new_config: config.clone(), validators: vec![ ValidatorId::from(Sr25519Keyring::Alice.public()), ValidatorId::from(Sr25519Keyring::Bob.public()), - ValidatorId::from(Sr25519Keyring::Charlie.public()), ], ..Default::default() }), _ => None, }); - // add a couple of para claims now that paras are live - MockAssigner::add_test_assignment(assignment_a.clone()); - MockAssigner::add_test_assignment(assignment_c.clone()); - - let mut now = 2; - run_to_block(now, |_| None); - - assert_eq!(Scheduler::scheduled_paras().collect::>().len(), 2); - - // cores 0, 1 should be occupied. mark them as such. - let mut occupied_map: BTreeMap = BTreeMap::new(); - occupied_map.insert(CoreIndex(0), para_a); - occupied_map.insert(CoreIndex(1), para_c); - Scheduler::occupied(occupied_map); - - { - let cores = AvailabilityCores::::get(); - - // cores 0, 1 are `CoreOccupied::Paras(ParasEntry...)` - assert!(cores[0] != CoreOccupied::Free); - assert!(cores[1] != CoreOccupied::Free); - - // core 2 is free - assert!(cores[2] == CoreOccupied::Free); - - assert!(Scheduler::scheduled_paras().collect::>().is_empty()); - - // All `core_queue`s should be empty - scheduler::ClaimQueue::::get() - .iter() - .for_each(|(_core_idx, core_queue)| assert_eq!(core_queue.len(), 0)) - } - MockAssigner::add_test_assignment(assignment_a.clone()); - MockAssigner::add_test_assignment(assignment_c.clone()); - MockAssigner::add_test_assignment(assignment_b.clone()); - MockAssigner::add_test_assignment(assignment_d.clone()); - MockAssigner::add_test_assignment(assignment_e.clone()); - now = 3; - run_to_block(now, |_| None); - { - let scheduled: BTreeMap<_, _> = scheduled_entries().collect(); - - assert_eq!(scheduled.len(), 3); - assert_eq!( - scheduled.get(&CoreIndex(2)).unwrap(), - &ParasEntry { - assignment: Assignment::Bulk(para_b), - availability_timeouts: 0, - ttl: 8 - }, - ); - } - - // now note that cores 0 and 1 were freed. - let just_updated: BTreeMap = vec![ - (CoreIndex(0), FreedReason::Concluded), - (CoreIndex(1), FreedReason::TimedOut), // should go back on queue. - ] - .into_iter() - .collect(); - Scheduler::free_cores_and_fill_claim_queue(just_updated, now); + // This will call advance_claim_queue + run_to_block(3, |_| None); { - let scheduled: BTreeMap<_, _> = scheduled_entries().collect(); + let mut claim_queue = scheduler::ClaimQueue::::get(); - // 1 thing scheduled before, + 2 cores freed. - assert_eq!(scheduled.len(), 3); - assert_eq!( - scheduled.get(&CoreIndex(0)).unwrap(), - &ParasEntry { - // Next entry in queue is `a` again: - assignment: Assignment::Bulk(para_a), - availability_timeouts: 0, - ttl: 8 - }, - ); - // Although C was descheduled, the core `2` was occupied so C goes back to the queue. assert_eq!( - scheduler::ClaimQueue::::get()[&CoreIndex(1)][1], - ParasEntry { - assignment: Assignment::Bulk(para_c), - // End of the queue should be the pushed back entry: - availability_timeouts: 1, - // ttl 1 higher: - ttl: 9 - }, - ); - assert_eq!( - scheduled.get(&CoreIndex(1)).unwrap(), - &ParasEntry { - assignment: Assignment::Bulk(para_c), - availability_timeouts: 0, - ttl: 8 - }, - ); - assert_eq!( - scheduled.get(&CoreIndex(2)).unwrap(), - &ParasEntry { - assignment: Assignment::Bulk(para_b), - availability_timeouts: 0, - ttl: 8 - }, + claim_queue.remove(&CoreIndex(0)).unwrap(), + [assignment_a].into_iter().collect::>() ); - assert!(claimqueue_contains_para_ids::(vec![para_c])); - assert!(!availability_cores_contains_para_ids::(vec![para_a, para_c])); + // Even though core 1 exists, there's no assignment for it so it's not present in the + // claim queue. + assert!(claim_queue.remove(&CoreIndex(1)).is_none()); } }); } #[test] -fn schedule_clears_availability_cores() { +// Test that `advance_claim_queue` only advances for cores that are not part of the `except_for` +// set. +fn advance_claim_queue_except_for() { let mut config = default_config(); + // NOTE: This test expects on demand cores to each get slotted on to a different core + // and not fill up the claimqueue of each core first. config.scheduler_params.lookahead = 1; + config.scheduler_params.num_cores = 3; + let genesis_config = genesis_config(&config); let para_a = ParaId::from(1_u32); let para_b = ParaId::from(2_u32); let para_c = ParaId::from(3_u32); + let para_d = ParaId::from(4_u32); + let para_e = ParaId::from(5_u32); let assignment_a = Assignment::Bulk(para_a); let assignment_b = Assignment::Bulk(para_b); let assignment_c = Assignment::Bulk(para_c); + let assignment_d = Assignment::Bulk(para_d); + let assignment_e = Assignment::Bulk(para_e); new_test_ext(genesis_config).execute_with(|| { - MockAssigner::set_core_count(3); - - // register 3 paras - schedule_blank_para(para_a); - schedule_blank_para(para_b); - schedule_blank_para(para_c); - - // Adding assignments then running block to populate claim queue - MockAssigner::add_test_assignment(assignment_a.clone()); - MockAssigner::add_test_assignment(assignment_b.clone()); - MockAssigner::add_test_assignment(assignment_c.clone()); + // add 5 paras + register_para(para_a); + register_para(para_b); + register_para(para_c); + register_para(para_d); + register_para(para_e); // start a new session to activate, 3 validators for 3 cores. run_to_block(1, |number| match number { @@ -639,91 +360,69 @@ fn schedule_clears_availability_cores() { _ => None, }); - run_to_block(2, |_| None); - - assert_eq!(scheduler::ClaimQueue::::get().len(), 3); - - // cores 0, 1, and 2 should be occupied. mark them as such. - Scheduler::occupied( - vec![(CoreIndex(0), para_a), (CoreIndex(1), para_b), (CoreIndex(2), para_c)] - .into_iter() - .collect(), - ); + // add a couple of para claims now that paras are live + MockAssigner::add_test_assignment(assignment_a.clone()); + MockAssigner::add_test_assignment(assignment_c.clone()); - { - let cores = AvailabilityCores::::get(); + run_to_block(2, |_| None); - assert_eq!(cores[0].is_free(), false); - assert_eq!(cores[1].is_free(), false); - assert_eq!(cores[2].is_free(), false); + Scheduler::advance_claim_queue(&Default::default()); - // All `core_queue`s should be empty - scheduler::ClaimQueue::::get() - .iter() - .for_each(|(_core_idx, core_queue)| assert!(core_queue.len() == 0)) - } + // Queues of all cores should be empty + assert_eq!(Scheduler::claim_queue_len(), 0); - // Add more assignments MockAssigner::add_test_assignment(assignment_a.clone()); - MockAssigner::add_test_assignment(assignment_b.clone()); MockAssigner::add_test_assignment(assignment_c.clone()); + MockAssigner::add_test_assignment(assignment_b.clone()); + MockAssigner::add_test_assignment(assignment_d.clone()); + MockAssigner::add_test_assignment(assignment_e.clone()); run_to_block(3, |_| None); - // now note that cores 0 and 2 were freed. - Scheduler::free_cores_and_fill_claim_queue( - vec![(CoreIndex(0), FreedReason::Concluded), (CoreIndex(2), FreedReason::Concluded)] - .into_iter() - .collect::>(), - 3, - ); + { + let scheduled: BTreeMap<_, _> = next_assignments().collect(); + + assert_eq!(scheduled.len(), 3); + assert_eq!(scheduled.get(&CoreIndex(0)).unwrap(), &Assignment::Bulk(para_a)); + assert_eq!(scheduled.get(&CoreIndex(1)).unwrap(), &Assignment::Bulk(para_c)); + assert_eq!(scheduled.get(&CoreIndex(2)).unwrap(), &Assignment::Bulk(para_b)); + } + + // now note that cores 0 and 1 were freed. + Scheduler::advance_claim_queue(&std::iter::once(CoreIndex(2)).collect()); { - let claimqueue = ClaimQueue::::get(); - let claimqueue_0 = claimqueue.get(&CoreIndex(0)).unwrap().clone(); - let claimqueue_2 = claimqueue.get(&CoreIndex(2)).unwrap().clone(); - let entry_ttl = 8; - assert_eq!(claimqueue_0.len(), 1); - assert_eq!(claimqueue_2.len(), 1); - let queue_0_expectation: VecDeque> = - vec![ParasEntry::new(assignment_a, entry_ttl as u32)].into_iter().collect(); - let queue_2_expectation: VecDeque> = - vec![ParasEntry::new(assignment_c, entry_ttl as u32)].into_iter().collect(); - assert_eq!(claimqueue_0, queue_0_expectation); - assert_eq!(claimqueue_2, queue_2_expectation); - - // The freed cores should be `Free` in `AvailabilityCores`. - let cores = AvailabilityCores::::get(); - assert!(cores[0].is_free()); - assert!(cores[2].is_free()); + let scheduled: BTreeMap<_, _> = next_assignments().collect(); + + // 1 thing scheduled before, + 2 cores freed. + assert_eq!(scheduled.len(), 3); + assert_eq!(scheduled.get(&CoreIndex(0)).unwrap(), &Assignment::Bulk(para_d)); + assert_eq!(scheduled.get(&CoreIndex(1)).unwrap(), &Assignment::Bulk(para_e)); + assert_eq!(scheduled.get(&CoreIndex(2)).unwrap(), &Assignment::Bulk(para_b)); } }); } #[test] fn schedule_rotates_groups() { + let on_demand_cores = 2; let config = { let mut config = default_config(); config.scheduler_params.lookahead = 1; + config.scheduler_params.num_cores = on_demand_cores; config }; let rotation_frequency = config.scheduler_params.group_rotation_frequency; - let on_demand_cores = 2; let genesis_config = genesis_config(&config); let para_a = ParaId::from(1_u32); let para_b = ParaId::from(2_u32); - let assignment_a = Assignment::Bulk(para_a); - let assignment_b = Assignment::Bulk(para_b); - new_test_ext(genesis_config).execute_with(|| { - MockAssigner::set_core_count(on_demand_cores); - - schedule_blank_para(para_a); - schedule_blank_para(para_b); + register_para(para_a); + register_para(para_b); // start a new session to activate, 2 validators for 2 cores. run_to_block(1, |number| match number { @@ -741,15 +440,10 @@ fn schedule_rotates_groups() { let session_start_block = scheduler::SessionStartBlock::::get(); assert_eq!(session_start_block, 1); - MockAssigner::add_test_assignment(assignment_a.clone()); - MockAssigner::add_test_assignment(assignment_b.clone()); - let mut now = 2; run_to_block(now, |_| None); let assert_groups_rotated = |rotations: u32, now: &BlockNumberFor| { - let scheduled: BTreeMap<_, _> = Scheduler::scheduled_paras().collect(); - assert_eq!(scheduled.len(), 2); assert_eq!( Scheduler::group_assigned_to_core(CoreIndex(0), *now).unwrap(), GroupIndex((0u32 + rotations) % on_demand_cores) @@ -764,7 +458,7 @@ fn schedule_rotates_groups() { // one block before first rotation. now = rotation_frequency; - run_to_block(rotation_frequency, |_| None); + run_to_block(now, |_| None); assert_groups_rotated(0, &now); @@ -785,134 +479,6 @@ fn schedule_rotates_groups() { }); } -#[test] -fn on_demand_claims_are_pruned_after_timing_out() { - let max_timeouts = 20; - let mut config = default_config(); - config.scheduler_params.lookahead = 1; - // Need more timeouts for this test - config.scheduler_params.max_availability_timeouts = max_timeouts; - config.scheduler_params.ttl = BlockNumber::from(5u32); - let genesis_config = genesis_config(&config); - - let para_a = ParaId::from(1_u32); - - let assignment_a = Assignment::Bulk(para_a); - - new_test_ext(genesis_config).execute_with(|| { - MockAssigner::set_core_count(2); - schedule_blank_para(para_a); - - // #1 - let mut now = 1; - run_to_block(now, |number| match number { - 1 => Some(SessionChangeNotification { - new_config: default_config(), - validators: vec![ - ValidatorId::from(Sr25519Keyring::Alice.public()), - ValidatorId::from(Sr25519Keyring::Eve.public()), - ], - ..Default::default() - }), - _ => None, - }); - - MockAssigner::add_test_assignment(assignment_a.clone()); - - // #2 - now += 1; - run_to_block(now, |_| None); - assert_eq!(scheduler::ClaimQueue::::get().len(), 1); - // ParaId a is in the claimqueue. - assert!(claimqueue_contains_para_ids::(vec![para_a])); - - Scheduler::occupied(vec![(CoreIndex(0), para_a)].into_iter().collect()); - // ParaId a is no longer in the claimqueue. - assert!(!claimqueue_contains_para_ids::(vec![para_a])); - // It is in availability cores. - assert!(availability_cores_contains_para_ids::(vec![para_a])); - - // #3 - now += 1; - // Run to block #n over the max_retries value. - // In this case, both validator groups with time out on availability and - // the assignment will be dropped. - for n in now..=(now + max_timeouts + 1) { - // #n - run_to_block(n, |_| None); - // Time out on core 0. - let just_updated: BTreeMap = vec![ - (CoreIndex(0), FreedReason::TimedOut), // should go back on queue. - ] - .into_iter() - .collect(); - Scheduler::free_cores_and_fill_claim_queue(just_updated, now); - - // ParaId a exists in the claim queue until max_retries is reached. - if n < max_timeouts + now { - assert!(claimqueue_contains_para_ids::(vec![para_a])); - } else { - assert!(!claimqueue_contains_para_ids::(vec![para_a])); - } - - let core_assignments = Scheduler::scheduled_paras().collect(); - Scheduler::occupied(core_assignments); - } - - // ParaId a does not exist in the claimqueue/availability_cores after - // threshold has been reached. - assert!(!claimqueue_contains_para_ids::(vec![para_a])); - assert!(!availability_cores_contains_para_ids::(vec![para_a])); - - // #25 - now += max_timeouts + 2; - - // Add assignment back to the mix. - MockAssigner::add_test_assignment(assignment_a.clone()); - run_to_block(now, |_| None); - - assert!(claimqueue_contains_para_ids::(vec![para_a])); - - // #26 - now += 1; - // Run to block #n but this time have group 1 conclude the availability. - for n in now..=(now + max_timeouts + 1) { - // #n - run_to_block(n, |_| None); - // Time out core 0 if group 0 is assigned to it, if group 1 is assigned, conclude. - let mut just_updated: BTreeMap = BTreeMap::new(); - if let Some(group) = Scheduler::group_assigned_to_core(CoreIndex(0), n) { - match group { - GroupIndex(0) => { - just_updated.insert(CoreIndex(0), FreedReason::TimedOut); // should go back on queue. - }, - GroupIndex(1) => { - just_updated.insert(CoreIndex(0), FreedReason::Concluded); - }, - _ => panic!("Should only have 2 groups here"), - } - } - - Scheduler::free_cores_and_fill_claim_queue(just_updated, now); - - // ParaId a exists in the claim queue until groups are rotated. - if n < 31 { - assert!(claimqueue_contains_para_ids::(vec![para_a])); - } else { - assert!(!claimqueue_contains_para_ids::(vec![para_a])); - } - - let core_assignments = Scheduler::scheduled_paras().collect(); - Scheduler::occupied(core_assignments); - } - - // ParaId a does not exist in the claimqueue/availability_cores after - // being concluded - assert!(!claimqueue_contains_para_ids::(vec![para_a])); - assert!(!availability_cores_contains_para_ids::(vec![para_a])); - }); -} - #[test] fn availability_predicate_works() { let genesis_config = genesis_config(&default_config()); @@ -948,20 +514,21 @@ fn availability_predicate_works() { #[test] fn next_up_on_available_uses_next_scheduled_or_none() { - let genesis_config = genesis_config(&default_config()); + let mut config = default_config(); + config.scheduler_params.num_cores = 1; + let genesis_config = genesis_config(&config); let para_a = ParaId::from(1_u32); let para_b = ParaId::from(2_u32); new_test_ext(genesis_config).execute_with(|| { - MockAssigner::set_core_count(1); - schedule_blank_para(para_a); - schedule_blank_para(para_b); + register_para(para_a); + register_para(para_b); // start a new session to activate, 2 validators for 2 cores. run_to_block(1, |number| match number { 1 => Some(SessionChangeNotification { - new_config: default_config(), + new_config: config.clone(), validators: vec![ ValidatorId::from(Sr25519Keyring::Alice.public()), ValidatorId::from(Sr25519Keyring::Eve.public()), @@ -971,69 +538,57 @@ fn next_up_on_available_uses_next_scheduled_or_none() { _ => None, }); - let entry_a = ParasEntry { - assignment: Assignment::Bulk(para_a), - availability_timeouts: 0 as u32, - ttl: 5 as u32, - }; - let entry_b = ParasEntry { - assignment: Assignment::Bulk(para_b), - availability_timeouts: 0 as u32, - ttl: 5 as u32, - }; - - Scheduler::add_to_claim_queue(CoreIndex(0), entry_a.clone()); + MockAssigner::add_test_assignment(Assignment::Bulk(para_a)); run_to_block(2, |_| None); { - assert_eq!(Scheduler::claim_queue_len(), 1); - assert_eq!(scheduler::AvailabilityCores::::get().len(), 1); - - let mut map = BTreeMap::new(); - map.insert(CoreIndex(0), para_a); - Scheduler::occupied(map); + // Two assignments for A on core 0, because the claim queue used to be empty. + assert_eq!(Scheduler::claim_queue_len(), 2); - let cores = scheduler::AvailabilityCores::::get(); - match &cores[0] { - CoreOccupied::Paras(entry) => assert_eq!(entry, &entry_a), - _ => panic!("There should only be one test assigner core"), - } - - assert!(Scheduler::next_up_on_available(CoreIndex(0)).is_none()); + assert!(Scheduler::next_up_on_available(CoreIndex(1)).is_none()); - Scheduler::add_to_claim_queue(CoreIndex(0), entry_b); + assert_eq!( + Scheduler::next_up_on_available(CoreIndex(0)).unwrap(), + ScheduledCore { para_id: para_a, collator: None } + ); + Scheduler::advance_claim_queue(&Default::default()); assert_eq!( Scheduler::next_up_on_available(CoreIndex(0)).unwrap(), - ScheduledCore { para_id: para_b, collator: None } + ScheduledCore { para_id: para_a, collator: None } ); + + Scheduler::advance_claim_queue(&Default::default()); + assert!(Scheduler::next_up_on_available(CoreIndex(0)).is_none()); } }); } #[test] -fn next_up_on_time_out_reuses_claim_if_nothing_queued() { - let genesis_config = genesis_config(&default_config()); +fn session_change_increasing_number_of_cores() { + let mut config = default_config(); + config.scheduler_params.num_cores = 2; + let genesis_config = genesis_config(&config); - let para_a = ParaId::from(1_u32); - let para_b = ParaId::from(2_u32); + let para_a = ParaId::from(3_u32); + let para_b = ParaId::from(4_u32); let assignment_a = Assignment::Bulk(para_a); let assignment_b = Assignment::Bulk(para_b); new_test_ext(genesis_config).execute_with(|| { - MockAssigner::set_core_count(1); - schedule_blank_para(para_a); - schedule_blank_para(para_b); + // Add 2 paras + register_para(para_a); + register_para(para_b); // start a new session to activate, 2 validators for 2 cores. run_to_block(1, |number| match number { 1 => Some(SessionChangeNotification { - new_config: default_config(), + new_config: config.clone(), validators: vec![ ValidatorId::from(Sr25519Keyring::Alice.public()), - ValidatorId::from(Sr25519Keyring::Eve.public()), + ValidatorId::from(Sr25519Keyring::Bob.public()), ], ..Default::default() }), @@ -1041,193 +596,236 @@ fn next_up_on_time_out_reuses_claim_if_nothing_queued() { }); MockAssigner::add_test_assignment(assignment_a.clone()); + MockAssigner::add_test_assignment(assignment_b.clone()); + // This will call advance_claim_queue run_to_block(2, |_| None); { - assert_eq!(scheduler::ClaimQueue::::get().len(), 1); - assert_eq!(scheduler::AvailabilityCores::::get().len(), 1); - - let mut map = BTreeMap::new(); - map.insert(CoreIndex(0), para_a); - Scheduler::occupied(map); - - let cores = scheduler::AvailabilityCores::::get(); - match cores.get(0).unwrap() { - CoreOccupied::Paras(entry) => { - assert_eq!(entry.assignment, assignment_a.clone()); - }, - _ => panic!("There should only be a single test assigner core"), - } - - // There's nothing more to pop for core 0 from the assignment provider. - assert!(MockAssigner::pop_assignment_for_core(CoreIndex(0)).is_none()); + let mut claim_queue = scheduler::ClaimQueue::::get(); + assert_eq!(Scheduler::claim_queue_len(), 4); assert_eq!( - Scheduler::next_up_on_time_out(CoreIndex(0)).unwrap(), - ScheduledCore { para_id: para_a, collator: None } + claim_queue.remove(&CoreIndex(0)).unwrap(), + [assignment_a.clone(), assignment_a.clone()] + .into_iter() + .collect::>() + ); + assert_eq!( + claim_queue.remove(&CoreIndex(1)).unwrap(), + [assignment_b.clone(), assignment_b.clone()] + .into_iter() + .collect::>() ); + } + + // Increase number of cores to 4. + let old_config = config; + let mut new_config = old_config.clone(); + new_config.scheduler_params.num_cores = 4; - MockAssigner::add_test_assignment(assignment_b.clone()); + // add another assignment for para b. + MockAssigner::add_test_assignment(assignment_b.clone()); + + run_to_block(3, |number| match number { + 3 => Some(SessionChangeNotification { + new_config: new_config.clone(), + prev_config: old_config.clone(), + validators: vec![ + ValidatorId::from(Sr25519Keyring::Alice.public()), + ValidatorId::from(Sr25519Keyring::Bob.public()), + ValidatorId::from(Sr25519Keyring::Charlie.public()), + ValidatorId::from(Sr25519Keyring::Dave.public()), + ], + ..Default::default() + }), + _ => None, + }); - // Pop assignment_b into the claimqueue - Scheduler::free_cores_and_fill_claim_queue(BTreeMap::new(), 2); + { + let mut claim_queue = scheduler::ClaimQueue::::get(); + assert_eq!(Scheduler::claim_queue_len(), 3); - //// Now that there is an earlier next-up, we use that. assert_eq!( - Scheduler::next_up_on_available(CoreIndex(0)).unwrap(), - ScheduledCore { para_id: para_b, collator: None } + claim_queue.remove(&CoreIndex(0)).unwrap(), + [assignment_a].into_iter().collect::>() + ); + assert_eq!( + claim_queue.remove(&CoreIndex(1)).unwrap(), + [assignment_b.clone()].into_iter().collect::>() + ); + assert_eq!( + claim_queue.remove(&CoreIndex(2)).unwrap(), + [assignment_b.clone()].into_iter().collect::>() ); } }); } #[test] -fn session_change_requires_reschedule_dropping_removed_paras() { +fn session_change_decreasing_number_of_cores() { let mut config = default_config(); - config.scheduler_params.lookahead = 1; + config.scheduler_params.num_cores = 3; let genesis_config = genesis_config(&config); - let para_a = ParaId::from(1_u32); - let para_b = ParaId::from(2_u32); + let para_a = ParaId::from(3_u32); + let para_b = ParaId::from(4_u32); let assignment_a = Assignment::Bulk(para_a); let assignment_b = Assignment::Bulk(para_b); new_test_ext(genesis_config).execute_with(|| { - // Setting explicit core count - MockAssigner::set_core_count(5); - let coretime_ttl = configuration::ActiveConfig::::get().scheduler_params.ttl; - - schedule_blank_para(para_a); - schedule_blank_para(para_b); - - // Add assignments - MockAssigner::add_test_assignment(assignment_a.clone()); - MockAssigner::add_test_assignment(assignment_b.clone()); + // Add 2 paras + register_para(para_a); + register_para(para_b); + // start a new session to activate, 2 validators for 2 cores. run_to_block(1, |number| match number { 1 => Some(SessionChangeNotification { - new_config: default_config(), + new_config: config.clone(), validators: vec![ ValidatorId::from(Sr25519Keyring::Alice.public()), ValidatorId::from(Sr25519Keyring::Bob.public()), - ValidatorId::from(Sr25519Keyring::Charlie.public()), - ValidatorId::from(Sr25519Keyring::Dave.public()), - ValidatorId::from(Sr25519Keyring::Eve.public()), - ValidatorId::from(Sr25519Keyring::Ferdie.public()), - ValidatorId::from(Sr25519Keyring::One.public()), ], - random_seed: [99; 32], ..Default::default() }), _ => None, }); - assert_eq!(scheduler::ClaimQueue::::get().len(), 2); + scheduler::Pallet::::set_claim_queue(BTreeMap::from([ + (CoreIndex::from(0), VecDeque::from([assignment_a.clone()])), + // Leave a hole for core 1. + (CoreIndex::from(2), VecDeque::from([assignment_b.clone(), assignment_b.clone()])), + ])); - let groups = ValidatorGroups::::get(); - assert_eq!(groups.len(), 5); + // Decrease number of cores to 1. + let old_config = config; + let mut new_config = old_config.clone(); + new_config.scheduler_params.num_cores = 1; - assert_ok!(Paras::schedule_para_cleanup(para_b)); + // Session change. + // Assignment A had its shot already so will be dropped for good. + // The two assignments of B will be pushed back to the assignment provider. + run_to_block(3, |number| match number { + 3 => Some(SessionChangeNotification { + new_config: new_config.clone(), + prev_config: old_config.clone(), + validators: vec![ValidatorId::from(Sr25519Keyring::Alice.public())], + ..Default::default() + }), + _ => None, + }); - // Add assignment - MockAssigner::add_test_assignment(assignment_a.clone()); + let mut claim_queue = scheduler::ClaimQueue::::get(); + assert_eq!(Scheduler::claim_queue_len(), 1); - run_to_end_of_block(2, |number| match number { - 2 => Some(SessionChangeNotification { - new_config: default_config(), + // There's only one assignment for B because run_to_block also calls advance_claim_queue at + // the end. + assert_eq!( + claim_queue.remove(&CoreIndex(0)).unwrap(), + [assignment_b.clone()].into_iter().collect::>() + ); + + // No more assignments now. + Scheduler::advance_claim_queue(&Default::default()); + assert_eq!(Scheduler::claim_queue_len(), 0); + }); +} + +#[test] +fn session_change_increasing_lookahead() { + let mut config = default_config(); + config.scheduler_params.num_cores = 2; + config.scheduler_params.lookahead = 2; + let genesis_config = genesis_config(&config); + + let para_a = ParaId::from(3_u32); + let para_b = ParaId::from(4_u32); + + let assignment_a = Assignment::Bulk(para_a); + let assignment_b = Assignment::Bulk(para_b); + + new_test_ext(genesis_config).execute_with(|| { + // Add 2 paras + register_para(para_a); + register_para(para_b); + + // start a new session to activate, 2 validators for 2 cores. + run_to_block(1, |number| match number { + 1 => Some(SessionChangeNotification { + new_config: config.clone(), validators: vec![ ValidatorId::from(Sr25519Keyring::Alice.public()), ValidatorId::from(Sr25519Keyring::Bob.public()), - ValidatorId::from(Sr25519Keyring::Charlie.public()), - ValidatorId::from(Sr25519Keyring::Dave.public()), - ValidatorId::from(Sr25519Keyring::Eve.public()), - ValidatorId::from(Sr25519Keyring::Ferdie.public()), - ValidatorId::from(Sr25519Keyring::One.public()), ], - random_seed: [99; 32], ..Default::default() }), _ => None, }); - Scheduler::free_cores_and_fill_claim_queue(BTreeMap::new(), 3); + MockAssigner::add_test_assignment(assignment_a.clone()); + MockAssigner::add_test_assignment(assignment_a.clone()); + MockAssigner::add_test_assignment(assignment_a.clone()); + MockAssigner::add_test_assignment(assignment_b.clone()); + MockAssigner::add_test_assignment(assignment_b.clone()); + MockAssigner::add_test_assignment(assignment_b.clone()); + + // Lookahead is currently 2. - assert_eq!( - scheduler::ClaimQueue::::get(), - vec![( - CoreIndex(0), - vec![ParasEntry::new( - Assignment::Bulk(para_a), - // At end of block 2 - coretime_ttl + 2 - )] - .into_iter() - .collect() - )] - .into_iter() - .collect() - ); + run_to_block(2, |_| None); - // Add para back - schedule_blank_para(para_b); + { + let mut claim_queue = scheduler::ClaimQueue::::get(); + assert_eq!(Scheduler::claim_queue_len(), 4); - // Add assignments - MockAssigner::add_test_assignment(assignment_a.clone()); - MockAssigner::add_test_assignment(assignment_b.clone()); + assert_eq!( + claim_queue.remove(&CoreIndex(0)).unwrap(), + [assignment_a.clone(), assignment_a.clone()] + .into_iter() + .collect::>() + ); + assert_eq!( + claim_queue.remove(&CoreIndex(1)).unwrap(), + [assignment_a.clone(), assignment_a.clone()] + .into_iter() + .collect::>() + ); + } + + // Increase lookahead to 4. + let old_config = config; + let mut new_config = old_config.clone(); + new_config.scheduler_params.lookahead = 4; run_to_block(3, |number| match number { 3 => Some(SessionChangeNotification { - new_config: default_config(), + new_config: new_config.clone(), + prev_config: old_config.clone(), validators: vec![ ValidatorId::from(Sr25519Keyring::Alice.public()), ValidatorId::from(Sr25519Keyring::Bob.public()), - ValidatorId::from(Sr25519Keyring::Charlie.public()), - ValidatorId::from(Sr25519Keyring::Dave.public()), - ValidatorId::from(Sr25519Keyring::Eve.public()), - ValidatorId::from(Sr25519Keyring::Ferdie.public()), - ValidatorId::from(Sr25519Keyring::One.public()), ], - random_seed: [99; 32], ..Default::default() }), _ => None, }); - assert_eq!(scheduler::ClaimQueue::::get().len(), 2); - - let groups = ValidatorGroups::::get(); - assert_eq!(groups.len(), 5); - - Scheduler::free_cores_and_fill_claim_queue(BTreeMap::new(), 4); + { + let mut claim_queue = scheduler::ClaimQueue::::get(); + assert_eq!(Scheduler::claim_queue_len(), 6); - assert_eq!( - scheduler::ClaimQueue::::get(), - vec![ - ( - CoreIndex(0), - vec![ParasEntry::new( - Assignment::Bulk(para_a), - // At block 3 - coretime_ttl + 3 - )] + assert_eq!( + claim_queue.remove(&CoreIndex(0)).unwrap(), + [assignment_a.clone(), assignment_a.clone(), assignment_b.clone()] .into_iter() - .collect() - ), - ( - CoreIndex(1), - vec![ParasEntry::new( - Assignment::Bulk(para_b), - // At block 3 - coretime_ttl + 3 - )] + .collect::>() + ); + assert_eq!( + claim_queue.remove(&CoreIndex(1)).unwrap(), + [assignment_a.clone(), assignment_b.clone(), assignment_b.clone()] .into_iter() - .collect() - ), - ] - .into_iter() - .collect() - ); + .collect::>() + ); + } }); } diff --git a/polkadot/runtime/parachains/src/session_info.rs b/polkadot/runtime/parachains/src/session_info.rs index ea05c1aacaa..0ec01755095 100644 --- a/polkadot/runtime/parachains/src/session_info.rs +++ b/polkadot/runtime/parachains/src/session_info.rs @@ -135,8 +135,8 @@ impl Pallet { let assignment_keys = AssignmentKeysUnsafe::::get(); let active_set = shared::ActiveValidatorIndices::::get(); - let validator_groups = scheduler::ValidatorGroups::::get().into(); - let n_cores = scheduler::AvailabilityCores::::get().len() as u32; + let validator_groups = scheduler::ValidatorGroups::::get(); + let n_cores = validator_groups.len() as u32; let zeroth_delay_tranche_width = config.zeroth_delay_tranche_width; let relay_vrf_modulo_samples = config.relay_vrf_modulo_samples; let n_delay_tranches = config.n_delay_tranches; @@ -177,7 +177,7 @@ impl Pallet { validators, // these are from the notification and are thus already correct. discovery_keys: take_active_subset_and_inactive(&active_set, &discovery_keys), assignment_keys: take_active_subset(&active_set, &assignment_keys), - validator_groups, + validator_groups: validator_groups.into(), n_cores, zeroth_delay_tranche_width, relay_vrf_modulo_samples, diff --git a/polkadot/runtime/parachains/src/shared.rs b/polkadot/runtime/parachains/src/shared.rs index f582bf0d90b..473c1aba7a0 100644 --- a/polkadot/runtime/parachains/src/shared.rs +++ b/polkadot/runtime/parachains/src/shared.rs @@ -80,6 +80,7 @@ impl /// Add a new relay-parent to the allowed relay parents, along with info about the header. /// Provide a maximum ancestry length for the buffer, which will cause old relay-parents to be /// pruned. + /// If the relay parent hash is already present, do nothing. pub(crate) fn update( &mut self, relay_parent: Hash, @@ -88,6 +89,11 @@ impl number: BlockNumber, max_ancestry_len: u32, ) { + if self.buffer.iter().any(|info| info.relay_parent == relay_parent) { + // Already present. + return + } + let claim_queue = transpose_claim_queue(claim_queue); // + 1 for the most recent block, which is always allowed. diff --git a/polkadot/runtime/parachains/src/shared/tests.rs b/polkadot/runtime/parachains/src/shared/tests.rs index 6da84e254f0..f7ea5148ce3 100644 --- a/polkadot/runtime/parachains/src/shared/tests.rs +++ b/polkadot/runtime/parachains/src/shared/tests.rs @@ -43,7 +43,13 @@ fn tracker_earliest_block_number() { let max_ancestry_len = 4; let now = 4; for i in 1..now { - tracker.update(Hash::zero(), Hash::zero(), Default::default(), i, max_ancestry_len); + tracker.update( + Hash::from([i as u8; 32]), + Hash::zero(), + Default::default(), + i, + max_ancestry_len, + ); assert_eq!(tracker.hypothetical_earliest_block_number(i + 1, max_ancestry_len), 0); } @@ -53,7 +59,7 @@ fn tracker_earliest_block_number() { } #[test] -fn tracker_claim_queue_remap() { +fn tracker_claim_queue_transpose() { let mut tracker = AllowedRelayParentsTracker::::default(); let mut claim_queue = BTreeMap::new(); @@ -120,6 +126,14 @@ fn tracker_acquire_info() { Some((s, b)) if s.state_root == state_root && b == 0 ); + // Try to push a duplicate. Should be ignored. + tracker.update(relay_parent, Hash::repeat_byte(13), Default::default(), 0, max_ancestry_len); + assert_eq!(tracker.buffer.len(), 1); + assert_matches!( + tracker.acquire_info(relay_parent, None), + Some((s, b)) if s.state_root == state_root && b == 0 + ); + let (relay_parent, state_root) = blocks[1]; tracker.update(relay_parent, state_root, Default::default(), 1u32, max_ancestry_len); let (relay_parent, state_root) = blocks[2]; diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 6266febaa0b..e94b6666ed0 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1710,7 +1710,8 @@ pub mod migrations { // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, parachains_inclusion::migration::MigrateToV1, - parachains_shared::migration::MigrateToV1, + parachains_shared::migration::MigrateToV1, + parachains_scheduler::migration::MigrateV2ToV3, ); } diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs index b7b3d12d4d9..71a0bb6fc7b 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_paras_inherent.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `polkadot_runtime_parachains::paras_inherent` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-07, 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: @@ -54,10 +54,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:0) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -70,23 +72,21 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn enter_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `8967` - // Estimated: `12432` - // Minimum execution time: 144_751_000 picoseconds. - Weight::from_parts(153_966_000, 0) - .saturating_add(Weight::from_parts(0, 12432)) + // Measured: `42760` + // Estimated: `46225` + // Minimum execution time: 228_252_000 picoseconds. + Weight::from_parts(234_368_000, 0) + .saturating_add(Weight::from_parts(0, 46225)) .saturating_add(T::DbWeight::get().reads(15)) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -94,10 +94,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -128,16 +130,14 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Registrar::Paras` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) - /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::Heads` (r:0 w:1) @@ -146,19 +146,18 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::MostRecentContext` (r:0 w:1) /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `v` is `[10, 200]`. + /// The range of component `v` is `[400, 1024]`. fn enter_variable_disputes(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `67786` - // Estimated: `73726 + v * (23 ±0)` - // Minimum execution time: 972_311_000 picoseconds. - Weight::from_parts(645_559_304, 0) - .saturating_add(Weight::from_parts(0, 73726)) - // Standard Error: 53_320 - .saturating_add(Weight::from_parts(41_795_493, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(25)) - .saturating_add(T::DbWeight::get().writes(15)) - .saturating_add(Weight::from_parts(0, 23).saturating_mul(v.into())) + // Measured: `203155` + // Estimated: `209095` + // Minimum execution time: 17_510_015_000 picoseconds. + Weight::from_parts(948_178_084, 0) + .saturating_add(Weight::from_parts(0, 209095)) + // Standard Error: 16_345 + .saturating_add(Weight::from_parts(41_627_958, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(26)) + .saturating_add(T::DbWeight::get().writes(16)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -166,10 +165,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:0) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -182,25 +183,21 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) - /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn enter_bitfields() -> Weight { // Proof Size summary in bytes: - // Measured: `42374` - // Estimated: `48314` - // Minimum execution time: 361_262_000 picoseconds. - Weight::from_parts(370_617_000, 0) - .saturating_add(Weight::from_parts(0, 48314)) - .saturating_add(T::DbWeight::get().reads(17)) - .saturating_add(T::DbWeight::get().writes(7)) + // Measured: `76066` + // Estimated: `82006` + // Minimum execution time: 501_266_000 picoseconds. + Weight::from_parts(517_989_000, 0) + .saturating_add(Weight::from_parts(0, 82006)) + .saturating_add(T::DbWeight::get().reads(16)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -208,10 +205,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -236,12 +235,8 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) - /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::ParaLifecycles` (r:1 w:0) @@ -252,6 +247,8 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParasDisputes::Included` (r:0 w:1) /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) @@ -262,18 +259,18 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::MostRecentContext` (r:0 w:1) /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `v` is `[101, 200]`. + /// The range of component `v` is `[2, 3]`. fn enter_backed_candidates_variable(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `42830` - // Estimated: `48770` - // Minimum execution time: 1_322_051_000 picoseconds. - Weight::from_parts(1_379_846_608, 0) - .saturating_add(Weight::from_parts(0, 48770)) - // Standard Error: 19_959 - .saturating_add(Weight::from_parts(24_630, 0).saturating_mul(v.into())) + // Measured: `76842` + // Estimated: `82782` + // Minimum execution time: 1_861_799_000 picoseconds. + Weight::from_parts(1_891_155_030, 0) + .saturating_add(Weight::from_parts(0, 82782)) + // Standard Error: 2_415_944 + .saturating_add(Weight::from_parts(7_924_189, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(26)) - .saturating_add(T::DbWeight::get().writes(15)) + .saturating_add(T::DbWeight::get().writes(14)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -281,10 +278,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -309,12 +308,8 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) - /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::FutureCodeHash` (r:1 w:0) @@ -329,6 +324,8 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParasDisputes::Included` (r:0 w:1) /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) @@ -341,12 +338,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_backed_candidate_code_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `42843` - // Estimated: `48783` - // Minimum execution time: 37_550_515_000 picoseconds. - Weight::from_parts(37_886_489_000, 0) - .saturating_add(Weight::from_parts(0, 48783)) + // Measured: `76855` + // Estimated: `82795` + // Minimum execution time: 37_682_370_000 picoseconds. + Weight::from_parts(41_118_445_000, 0) + .saturating_add(Weight::from_parts(0, 82795)) .saturating_add(T::DbWeight::get().reads(28)) - .saturating_add(T::DbWeight::get().writes(15)) + .saturating_add(T::DbWeight::get().writes(14)) } } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 96104ace7d7..9e7ee488af7 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -31,11 +31,11 @@ use codec::Encode; use pallet_transaction_payment::FungibleAdapter; use polkadot_runtime_parachains::{ - assigner_parachains as parachains_assigner_parachains, - configuration as parachains_configuration, - configuration::ActiveConfigHrmpChannelSizeAndCapacityRatio, disputes as parachains_disputes, - disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, - inclusion as parachains_inclusion, initializer as parachains_initializer, + assigner_coretime as parachains_assigner_coretime, configuration as parachains_configuration, + configuration::ActiveConfigHrmpChannelSizeAndCapacityRatio, coretime, + disputes as parachains_disputes, disputes::slashing as parachains_slashing, + dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, + initializer as parachains_initializer, on_demand as parachains_on_demand, origin as parachains_origin, paras as parachains_paras, paras_inherent as parachains_paras_inherent, runtime_api_impl::v11 as runtime_impl, scheduler as parachains_scheduler, session_info as parachains_session_info, @@ -49,8 +49,10 @@ use frame_election_provider_support::{ use frame_support::{ construct_runtime, derive_impl, genesis_builder_helper::{build_state, get_preset}, + pallet_prelude::Get, parameter_types, traits::{KeyOwnerProofSystem, WithdrawReasons}, + PalletId, }; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId}; use pallet_session::historical as session_historical; @@ -92,6 +94,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}; pub use pallet_balances::Call as BalancesCall; #[cfg(feature = "std")] @@ -559,7 +562,7 @@ impl parachains_initializer::Config for Runtime { type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type ForceOrigin = frame_system::EnsureRoot; type WeightInfo = (); - type CoretimeOnNewSession = (); + type CoretimeOnNewSession = Coretime; } impl parachains_session_info::Config for Runtime { @@ -577,15 +580,26 @@ impl parachains_paras::Config for Runtime { type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; type OnNewHead = (); - type AssignCoretime = (); + type AssignCoretime = CoretimeAssignmentProvider; } parameter_types! { pub const BrokerId: u32 = 10u32; + pub MaxXcmTransactWeight: Weight = Weight::from_parts(10_000_000, 10_000); +} + +pub struct BrokerPot; +impl Get for BrokerPot { + fn get() -> InteriorLocation { + unimplemented!() + } } parameter_types! { pub const OnDemandTrafficDefaultValue: FixedU128 = FixedU128::from_u32(1); + // Keep 2 timeslices worth of revenue information. + pub const MaxHistoricalRevenue: BlockNumber = 2 * 5; + pub const OnDemandPalletId: PalletId = PalletId(*b"py/ondmd"); } impl parachains_dmp::Config for Runtime {} @@ -607,10 +621,48 @@ impl parachains_hrmp::Config for Runtime { type WeightInfo = parachains_hrmp::TestWeightInfo; } -impl parachains_assigner_parachains::Config for Runtime {} +impl parachains_on_demand::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type TrafficDefaultValue = OnDemandTrafficDefaultValue; + type WeightInfo = parachains_on_demand::TestWeightInfo; + type MaxHistoricalRevenue = MaxHistoricalRevenue; + type PalletId = OnDemandPalletId; +} + +impl parachains_assigner_coretime::Config for Runtime {} impl parachains_scheduler::Config for Runtime { - type AssignmentProvider = ParaAssignmentProvider; + type AssignmentProvider = CoretimeAssignmentProvider; +} + +pub struct DummyXcmSender; +impl SendXcm for DummyXcmSender { + type Ticket = (); + fn validate( + _: &mut Option, + _: &mut Option>, + ) -> SendResult { + Ok(((), Assets::new())) + } + + /// Actually carry out the delivery operation for a previously validated message sending. + fn deliver(_ticket: Self::Ticket) -> Result { + Ok([0u8; 32]) + } +} + +impl coretime::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Currency = pallet_balances::Pallet; + type BrokerId = BrokerId; + type WeightInfo = crate::coretime::TestWeightInfo; + type SendXcm = DummyXcmSender; + type MaxXcmTransactWeight = MaxXcmTransactWeight; + type BrokerPotLocation = BrokerPot; + type AssetTransactor = (); + type AccountToLocation = (); } impl paras_sudo_wrapper::Config for Runtime {} @@ -753,7 +805,9 @@ construct_runtime! { Xcm: pallet_xcm, ParasDisputes: parachains_disputes, ParasSlashing: parachains_slashing, - ParaAssignmentProvider: parachains_assigner_parachains, + OnDemandAssignmentProvider: parachains_on_demand, + CoretimeAssignmentProvider: parachains_assigner_coretime, + Coretime: coretime, Sudo: pallet_sudo, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index b7dae533224..461be186ee5 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1805,6 +1805,7 @@ pub mod migrations { MaxAgentsToMigrate, >, parachains_shared::migration::MigrateToV1, + parachains_scheduler::migration::MigrateV2ToV3, ); } diff --git a/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs index 32f6f28f242..36aafc1d2f2 100644 --- a/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs +++ b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_paras_inherent.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `polkadot_runtime_parachains::paras_inherent` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-dr4vwrkf-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -54,10 +54,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:0) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -70,23 +72,21 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn enter_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `37553` - // Estimated: `41018` - // Minimum execution time: 237_414_000 picoseconds. - Weight::from_parts(245_039_000, 0) - .saturating_add(Weight::from_parts(0, 41018)) + // Measured: `37559` + // Estimated: `41024` + // Minimum execution time: 217_257_000 picoseconds. + Weight::from_parts(228_878_000, 0) + .saturating_add(Weight::from_parts(0, 41024)) .saturating_add(T::DbWeight::get().reads(15)) - .saturating_add(T::DbWeight::get().writes(5)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -94,10 +94,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -134,16 +136,14 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) - /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) /// Proof: `Hrmp::HrmpWatermarks` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::Heads` (r:0 w:1) @@ -152,19 +152,18 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::UpgradeGoAheadSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::MostRecentContext` (r:0 w:1) /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `v` is `[10, 1024]`. + /// The range of component `v` is `[400, 1024]`. fn enter_variable_disputes(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `199504` - // Estimated: `205444 + v * (5 ±0)` - // Minimum execution time: 1_157_489_000 picoseconds. - Weight::from_parts(629_243_559, 0) - .saturating_add(Weight::from_parts(0, 205444)) - // Standard Error: 10_997 - .saturating_add(Weight::from_parts(50_752_930, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(28)) - .saturating_add(T::DbWeight::get().writes(16)) - .saturating_add(Weight::from_parts(0, 5).saturating_mul(v.into())) + // Measured: `117547` + // Estimated: `123487` + // Minimum execution time: 21_077_090_000 picoseconds. + Weight::from_parts(703_350_265, 0) + .saturating_add(Weight::from_parts(0, 123487)) + // Standard Error: 21_944 + .saturating_add(Weight::from_parts(51_197_317, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(29)) + .saturating_add(T::DbWeight::get().writes(17)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -172,10 +171,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:0) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -188,25 +189,21 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParaInclusion::V1` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) - /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorIndices` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn enter_bitfields() -> Weight { // Proof Size summary in bytes: - // Measured: `75131` - // Estimated: `81071` - // Minimum execution time: 466_928_000 picoseconds. - Weight::from_parts(494_342_000, 0) - .saturating_add(Weight::from_parts(0, 81071)) - .saturating_add(T::DbWeight::get().reads(17)) - .saturating_add(T::DbWeight::get().writes(7)) + // Measured: `74967` + // Estimated: `80907` + // Minimum execution time: 487_605_000 picoseconds. + Weight::from_parts(506_014_000, 0) + .saturating_add(Weight::from_parts(0, 80907)) + .saturating_add(T::DbWeight::get().reads(16)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -214,10 +211,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -248,12 +247,8 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) - /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::ParaLifecycles` (r:1 w:0) @@ -264,6 +259,8 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParasDisputes::Included` (r:0 w:1) /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) @@ -277,15 +274,15 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// The range of component `v` is `[2, 5]`. fn enter_backed_candidates_variable(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `76369` - // Estimated: `82309` - // Minimum execution time: 1_468_919_000 picoseconds. - Weight::from_parts(1_433_315_477, 0) - .saturating_add(Weight::from_parts(0, 82309)) - // Standard Error: 419_886 - .saturating_add(Weight::from_parts(42_880_485, 0).saturating_mul(v.into())) + // Measured: `76491` + // Estimated: `82431` + // Minimum execution time: 1_496_985_000 picoseconds. + Weight::from_parts(1_466_448_265, 0) + .saturating_add(Weight::from_parts(0, 82431)) + // Standard Error: 403_753 + .saturating_add(Weight::from_parts(44_015_233, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(29)) - .saturating_add(T::DbWeight::get().writes(16)) + .saturating_add(T::DbWeight::get().writes(15)) } /// Storage: `ParaInherent::Included` (r:1 w:1) /// Proof: `ParaInherent::Included` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -293,10 +290,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `System::ParentHash` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `ParasShared::AllowedRelayParents` (r:1 w:1) /// Proof: `ParasShared::AllowedRelayParents` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) + /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::AvailabilityCores` (r:1 w:1) - /// Proof: `ParaScheduler::AvailabilityCores` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) + /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParasShared::ActiveValidatorKeys` (r:1 w:0) /// Proof: `ParasShared::ActiveValidatorKeys` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Babe::AuthorVrfRandomness` (r:1 w:0) @@ -327,12 +326,8 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasDisputes::Disputes` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParaScheduler::SessionStartBlock` (r:1 w:0) /// Proof: `ParaScheduler::SessionStartBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ValidatorGroups` (r:1 w:0) - /// Proof: `ParaScheduler::ValidatorGroups` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParaScheduler::ClaimQueue` (r:1 w:1) - /// Proof: `ParaScheduler::ClaimQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) - /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Initializer::BufferedSessionChanges` (r:1 w:0) + /// Proof: `Initializer::BufferedSessionChanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Paras::CurrentCodeHash` (r:1 w:0) /// Proof: `Paras::CurrentCodeHash` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::FutureCodeHash` (r:1 w:0) @@ -347,6 +342,8 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `ParasShared::ActiveValidatorIndices` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Session::DisabledValidators` (r:1 w:0) /// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `ParasDisputes::Included` (r:0 w:1) /// Proof: `ParasDisputes::Included` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Hrmp::HrmpWatermarks` (r:0 w:1) @@ -359,12 +356,12 @@ impl polkadot_runtime_parachains::paras_inherent::Weigh /// Proof: `Paras::MostRecentContext` (`max_values`: None, `max_size`: None, mode: `Measured`) fn enter_backed_candidate_code_upgrade() -> Weight { // Proof Size summary in bytes: - // Measured: `76382` - // Estimated: `82322` - // Minimum execution time: 34_577_233_000 picoseconds. - Weight::from_parts(39_530_352_000, 0) - .saturating_add(Weight::from_parts(0, 82322)) + // Measured: `76504` + // Estimated: `82444` + // Minimum execution time: 40_136_167_000 picoseconds. + Weight::from_parts(41_572_376_000, 0) + .saturating_add(Weight::from_parts(0, 82444)) .saturating_add(T::DbWeight::get().reads(31)) - .saturating_add(T::DbWeight::get().writes(16)) + .saturating_add(T::DbWeight::get().writes(15)) } } diff --git a/polkadot/zombienet_tests/functional/0015-coretime-shared-core.zndsl b/polkadot/zombienet_tests/functional/0015-coretime-shared-core.zndsl index b8b8887df85..8f883dffa5e 100644 --- a/polkadot/zombienet_tests/functional/0015-coretime-shared-core.zndsl +++ b/polkadot/zombienet_tests/functional/0015-coretime-shared-core.zndsl @@ -5,8 +5,8 @@ Creds: config validator: reports node_roles is 4 # register paras 2 by 2 to speed up the test. registering all at once will exceed the weight limit. -validator-0: js-script ./0015-force-register-paras.js with "2000,2001" return is 0 within 600 seconds -validator-0: js-script ./0015-force-register-paras.js with "2002,2003" return is 0 within 600 seconds +validator-0: js-script ./force-register-paras.js with "2000,2001" return is 0 within 600 seconds +validator-0: js-script ./force-register-paras.js with "2002,2003" return is 0 within 600 seconds # assign core 0 to be shared by all paras. validator-0: js-script ./assign-core.js with "0,2000,14400,2001,14400,2002,14400,2003,14400" return is 0 within 600 seconds diff --git a/polkadot/zombienet_tests/functional/0017-sync-backing.toml b/polkadot/zombienet_tests/functional/0017-sync-backing.toml new file mode 100644 index 00000000000..2550054c8da --- /dev/null +++ b/polkadot/zombienet_tests/functional/0017-sync-backing.toml @@ -0,0 +1,48 @@ +[settings] +timeout = 1000 + +[relaychain] +default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" +chain = "rococo-local" + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.async_backing_params] + max_candidate_depth = 0 + allowed_ancestry_len = 0 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + lookahead = 2 + group_rotation_frequency = 4 + +[relaychain.default_resources] +limits = { memory = "4G", cpu = "2" } +requests = { memory = "2G", cpu = "1" } + + [[relaychain.node_groups]] + name = "alice" + args = [ "-lparachain=debug" ] + count = 10 + +[[parachains]] +id = 2000 +addToGenesis = true + + [parachains.collator] + name = "collator01" + image = "{{COL_IMAGE}}" + command = "adder-collator" + args = ["-lparachain=debug"] + +[[parachains]] +id = 2001 +cumulus_based = true + + [parachains.collator] + name = "collator02" + image = "{{CUMULUS_IMAGE}}" + command = "polkadot-parachain" + args = ["-lparachain=debug"] + +[types.Header] +number = "u64" +parent_hash = "Hash" +post_state = "Hash" \ No newline at end of file diff --git a/polkadot/zombienet_tests/functional/0017-sync-backing.zndsl b/polkadot/zombienet_tests/functional/0017-sync-backing.zndsl new file mode 100644 index 00000000000..a53de784b2d --- /dev/null +++ b/polkadot/zombienet_tests/functional/0017-sync-backing.zndsl @@ -0,0 +1,22 @@ +Description: Test we are producing 12-second parachain blocks if sync backing is configured +Network: ./0017-sync-backing.toml +Creds: config + +# Check authority status. +alice: reports node_roles is 4 + +# Ensure parachains are registered. +alice: parachain 2000 is registered within 60 seconds +alice: parachain 2001 is registered within 60 seconds + +# Ensure parachains made progress. +alice: reports substrate_block_height{status="finalized"} is at least 10 within 100 seconds + +# This parachains should produce blocks at 12s clip, let's assume an 14s rate, allowing for +# some slots to be missed on slower machines +alice: parachain 2000 block height is at least 21 within 300 seconds +alice: parachain 2000 block height is lower than 25 within 2 seconds + +# This should already have produced the needed blocks +alice: parachain 2001 block height is at least 21 within 10 seconds +alice: parachain 2001 block height is lower than 25 within 2 seconds diff --git a/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.toml b/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.toml new file mode 100644 index 00000000000..745c4f9e24b --- /dev/null +++ b/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.toml @@ -0,0 +1,39 @@ +[settings] +timeout = 1000 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 2 + lookahead = 2 + num_cores = 4 + group_rotation_frequency = 4 + + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.approval_voting_params] + needed_approvals = 3 + +[relaychain] +default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" +chain = "rococo-local" +command = "polkadot" + + [[relaychain.node_groups]] + name = "validator" + args = ["-lruntime=debug,parachain=debug"] + count = 4 + +[[parachains]] +id = 2000 +register_para = false +onboard_as_parachain = false +add_to_genesis = false +chain = "glutton-westend-local-2000" + [parachains.genesis.runtimeGenesis.patch.glutton] + compute = "50000000" + storage = "2500000000" + trashDataCount = 5120 + + [parachains.collator] + name = "collator-2000" + image = "{{CUMULUS_IMAGE}}" + command = "polkadot-parachain" + args = ["-lparachain=debug"] diff --git a/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.zndsl b/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.zndsl new file mode 100644 index 00000000000..80ecf6ae1b9 --- /dev/null +++ b/polkadot/zombienet_tests/functional/0018-shared-core-idle-parachain.zndsl @@ -0,0 +1,11 @@ +Description: Test that a parachain can keep producing blocks even if the other parachain with which it's sharing a core doesn't +Network: ./0018-shared-core-idle-parachain.toml +Creds: config + +validator: reports node_roles is 4 + +validator-0: js-script ./force-register-paras.js with "2000" return is 0 within 600 seconds +# 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 diff --git a/polkadot/zombienet_tests/functional/0015-force-register-paras.js b/polkadot/zombienet_tests/functional/force-register-paras.js similarity index 100% rename from polkadot/zombienet_tests/functional/0015-force-register-paras.js rename to polkadot/zombienet_tests/functional/force-register-paras.js diff --git a/prdoc/pr_5461.prdoc b/prdoc/pr_5461.prdoc new file mode 100644 index 00000000000..bf343216e29 --- /dev/null +++ b/prdoc/pr_5461.prdoc @@ -0,0 +1,20 @@ +title: "runtime: remove ttl" + +doc: + - audience: [Runtime Dev, Node Dev] + description: | + Resolves https://github.com/paritytech/polkadot-sdk/issues/4776. Removes the scheduling ttl used in the relay chain + runtimes, as well as the availability timeout retries. The extrinsics for configuring these two values are also removed. + Deprecates the `ttl` and `max_availability_timeouts` fields of the `HostConfiguration` primitive. + +crates: + - name: polkadot-runtime-parachains + bump: major + - name: polkadot-primitives + bump: major + - name: rococo-runtime + bump: major + - name: westend-runtime + bump: major + - name: polkadot + bump: none -- GitLab From 03f1d2d646c35239405514cbb73bd376b85636cd Mon Sep 17 00:00:00 2001 From: Dusan Morhac <55763425+dudo50@users.noreply.github.com> Date: Mon, 21 Oct 2024 23:40:00 +0200 Subject: [PATCH 25/50] [pallet-nfts, pallet_uniques] - Expose private structs (#6087) # Description This PR exposes pallet_nfts and pallet_uniques structs, so other pallets can access storage to use it for extending nft functionalities. In pallet uniques it also exposes collection and asset metadata storage as they are private. ## Integration This integration allows nfts and uniques extension pallets to use then private - now public structs to retrieve and parse storage from pallet_nfts. We are building cross-chain NFT pallet and in order to transfer collection that houses multiple NFT owners we need to manually remove NFTs and Collections from storage without signers. We would also like to refund deposits on origin chain and we were unable to as struct data was private. We have built cross-chain pallet that allows to send nfts or collections between two pallets in abstract way without having to look which pallet parachain (If nfts or uniques) implements. ## Review Notes Code exposes private structs to public structs. No breaking change. Build runs fine, tests are also ok. screen1 screen2 screen3 PR is tied with following issue: Closes #5959 # Checklist * [x] My PR includes a detailed description as outlined in the "Description" and its two subsections above. * [x] My PR follows the [labeling requirements]( https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md#Process ) of this project (at minimum one label for `T` required) * External contributors: ask maintainers to put the right label on your PR. * [ ] I have made corresponding changes to the documentation (if applicable) * [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) --------- Co-authored-by: Branislav Kontur Co-authored-by: command-bot <> --- prdoc/pr_6087.prdoc | 12 +++++ substrate/frame/nfts/src/types.rs | 77 ++++++++++++++-------------- substrate/frame/uniques/src/lib.rs | 4 +- substrate/frame/uniques/src/types.rs | 40 +++++++-------- 4 files changed, 72 insertions(+), 61 deletions(-) create mode 100644 prdoc/pr_6087.prdoc diff --git a/prdoc/pr_6087.prdoc b/prdoc/pr_6087.prdoc new file mode 100644 index 00000000000..db083ba645b --- /dev/null +++ b/prdoc/pr_6087.prdoc @@ -0,0 +1,12 @@ +title: Expose private structs in pallet_nfts and pallet_uniques. + +doc: + - audience: Runtime Dev + description: | + PR changes certain structs in pallet_nfts and pallet_uniques into public. It also changes 2 storages (collection & asset metadata) into public in pallet_uniques. + +crates: + - name: pallet-nfts + bump: patch + - name: pallet-uniques + bump: patch diff --git a/substrate/frame/nfts/src/types.rs b/substrate/frame/nfts/src/types.rs index 60d7c639c88..d67fb404ea7 100644 --- a/substrate/frame/nfts/src/types.rs +++ b/substrate/frame/nfts/src/types.rs @@ -31,49 +31,48 @@ use frame_system::pallet_prelude::BlockNumberFor; use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter}; /// A type alias for handling balance deposits. -pub(super) type DepositBalanceOf = +pub type DepositBalanceOf = <>::Currency as Currency<::AccountId>>::Balance; /// A type alias representing the details of a collection. -pub(super) type CollectionDetailsFor = +pub type CollectionDetailsFor = CollectionDetails<::AccountId, DepositBalanceOf>; /// A type alias for keeping track of approvals used by a single item. -pub(super) type ApprovalsOf = BoundedBTreeMap< +pub type ApprovalsOf = BoundedBTreeMap< ::AccountId, Option>, >::ApprovalsLimit, >; /// A type alias for keeping track of approvals for an item's attributes. -pub(super) type ItemAttributesApprovals = +pub type ItemAttributesApprovals = BoundedBTreeSet<::AccountId, >::ItemAttributesApprovalsLimit>; /// A type that holds the deposit for a single item. -pub(super) type ItemDepositOf = - ItemDeposit, ::AccountId>; +pub type ItemDepositOf = ItemDeposit, ::AccountId>; /// A type that holds the deposit amount for an item's attribute. -pub(super) type AttributeDepositOf = +pub type AttributeDepositOf = AttributeDeposit, ::AccountId>; /// A type that holds the deposit amount for an item's metadata. -pub(super) type ItemMetadataDepositOf = +pub type ItemMetadataDepositOf = ItemMetadataDeposit, ::AccountId>; /// A type that holds the details of a single item. -pub(super) type ItemDetailsFor = +pub type ItemDetailsFor = ItemDetails<::AccountId, ItemDepositOf, ApprovalsOf>; /// A type alias for an accounts balance. -pub(super) type BalanceOf = +pub type BalanceOf = <>::Currency as Currency<::AccountId>>::Balance; /// A type alias to represent the price of an item. -pub(super) type ItemPrice = BalanceOf; +pub type ItemPrice = BalanceOf; /// A type alias for the tips held by a single item. -pub(super) type ItemTipOf = ItemTip< +pub type ItemTipOf = ItemTip< >::CollectionId, >::ItemId, ::AccountId, BalanceOf, >; /// A type alias for the settings configuration of a collection. -pub(super) type CollectionConfigFor = +pub type CollectionConfigFor = CollectionConfig, BlockNumberFor, >::CollectionId>; /// A type alias for the pre-signed minting configuration for a specified collection. -pub(super) type PreSignedMintOf = PreSignedMint< +pub type PreSignedMintOf = PreSignedMint< >::CollectionId, >::ItemId, ::AccountId, @@ -81,7 +80,7 @@ pub(super) type PreSignedMintOf = PreSignedMint< BalanceOf, >; /// A type alias for the pre-signed minting configuration on the attribute level of an item. -pub(super) type PreSignedAttributesOf = PreSignedAttributes< +pub type PreSignedAttributesOf = PreSignedAttributes< >::CollectionId, >::ItemId, ::AccountId, @@ -92,18 +91,18 @@ pub(super) type PreSignedAttributesOf = PreSignedAttributes< #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct CollectionDetails { /// Collection's owner. - pub(super) owner: AccountId, + pub owner: AccountId, /// The total balance deposited by the owner for all the storage data associated with this /// collection. Used by `destroy`. - pub(super) owner_deposit: DepositBalance, + pub owner_deposit: DepositBalance, /// The total number of outstanding items of this collection. - pub(super) items: u32, + pub items: u32, /// The total number of outstanding item metadata of this collection. - pub(super) item_metadatas: u32, + pub item_metadatas: u32, /// The total number of outstanding item configs of this collection. - pub(super) item_configs: u32, + pub item_configs: u32, /// The total number of attributes for this collection. - pub(super) attributes: u32, + pub attributes: u32, } /// Witness data for the destroy transactions. @@ -143,21 +142,21 @@ pub struct MintWitness { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)] pub struct ItemDetails { /// The owner of this item. - pub(super) owner: AccountId, + pub owner: AccountId, /// The approved transferrer of this item, if one is set. - pub(super) approvals: Approvals, + pub approvals: Approvals, /// The amount held in the pallet's default account for this item. Free-hold items will have /// this as zero. - pub(super) deposit: Deposit, + pub deposit: Deposit, } /// Information about the reserved item deposit. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct ItemDeposit { /// A depositor account. - pub(super) account: AccountId, + pub account: AccountId, /// An amount that gets reserved. - pub(super) amount: DepositBalance, + pub amount: DepositBalance, } /// Information about the collection's metadata. @@ -168,11 +167,11 @@ pub struct CollectionMetadata> { /// The balance deposited for this metadata. /// /// This pays for the data stored in this struct. - pub(super) deposit: Deposit, + pub deposit: Deposit, /// General information concerning this collection. Limited in length by `StringLimit`. This /// will generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. - pub(super) data: BoundedVec, + pub data: BoundedVec, } /// Information about the item's metadata. @@ -182,11 +181,11 @@ pub struct ItemMetadata> { /// The balance deposited for this metadata. /// /// This pays for the data stored in this struct. - pub(super) deposit: Deposit, + pub deposit: Deposit, /// General information concerning this item. Limited in length by `StringLimit`. This will - /// generally be either a JSON dump or the hash of some JSON which can be found on a + /// generally be either a JSON dump or the hash of some JSON which can be found on /// hash-addressable global publication system such as IPFS. - pub(super) data: BoundedVec, + pub data: BoundedVec, } /// Information about the tip. @@ -206,31 +205,31 @@ pub struct ItemTip { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)] pub struct PendingSwap { /// The collection that contains the item that the user wants to receive. - pub(super) desired_collection: CollectionId, + pub desired_collection: CollectionId, /// The item the user wants to receive. - pub(super) desired_item: Option, + pub desired_item: Option, /// A price for the desired `item` with the direction. - pub(super) price: Option, + pub price: Option, /// A deadline for the swap. - pub(super) deadline: Deadline, + pub deadline: Deadline, } /// Information about the reserved attribute deposit. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct AttributeDeposit { /// A depositor account. - pub(super) account: Option, + pub account: Option, /// An amount that gets reserved. - pub(super) amount: DepositBalance, + pub amount: DepositBalance, } /// Information about the reserved item's metadata deposit. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct ItemMetadataDeposit { /// A depositor account, None means the deposit is collection's owner. - pub(super) account: Option, + pub account: Option, /// An amount that gets reserved. - pub(super) amount: DepositBalance, + pub amount: DepositBalance, } /// Specifies whether the tokens will be sent or received. diff --git a/substrate/frame/uniques/src/lib.rs b/substrate/frame/uniques/src/lib.rs index dc27c335623..84f122c08bb 100644 --- a/substrate/frame/uniques/src/lib.rs +++ b/substrate/frame/uniques/src/lib.rs @@ -223,7 +223,7 @@ pub mod pallet { #[pallet::storage] #[pallet::storage_prefix = "ClassMetadataOf"] /// Metadata of a collection. - pub(super) type CollectionMetadataOf, I: 'static = ()> = StorageMap< + pub type CollectionMetadataOf, I: 'static = ()> = StorageMap< _, Blake2_128Concat, T::CollectionId, @@ -234,7 +234,7 @@ pub mod pallet { #[pallet::storage] #[pallet::storage_prefix = "InstanceMetadataOf"] /// Metadata of an item. - pub(super) type ItemMetadataOf, I: 'static = ()> = StorageDoubleMap< + pub type ItemMetadataOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, diff --git a/substrate/frame/uniques/src/types.rs b/substrate/frame/uniques/src/types.rs index a2e804f245f..e2e170c72f2 100644 --- a/substrate/frame/uniques/src/types.rs +++ b/substrate/frame/uniques/src/types.rs @@ -40,26 +40,26 @@ pub(super) type ItemPrice = #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct CollectionDetails { /// Can change `owner`, `issuer`, `freezer` and `admin` accounts. - pub(super) owner: AccountId, + pub owner: AccountId, /// Can mint tokens. - pub(super) issuer: AccountId, + pub issuer: AccountId, /// Can thaw tokens, force transfers and burn tokens from any account. - pub(super) admin: AccountId, + pub admin: AccountId, /// Can freeze tokens. - pub(super) freezer: AccountId, + pub freezer: AccountId, /// The total balance deposited for the all storage associated with this collection. /// Used by `destroy`. - pub(super) total_deposit: DepositBalance, + pub total_deposit: DepositBalance, /// If `true`, then no deposit is needed to hold items of this collection. - pub(super) free_holding: bool, + pub free_holding: bool, /// The total number of outstanding items of this collection. - pub(super) items: u32, + pub items: u32, /// The total number of outstanding item metadata of this collection. - pub(super) item_metadatas: u32, + pub item_metadatas: u32, /// The total number of attributes for this collection. - pub(super) attributes: u32, + pub attributes: u32, /// Whether the collection is frozen for non-admin transfers. - pub(super) is_frozen: bool, + pub is_frozen: bool, } /// Witness data for the destroy transactions. @@ -90,14 +90,14 @@ impl CollectionDetails { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)] pub struct ItemDetails { /// The owner of this item. - pub(super) owner: AccountId, + pub owner: AccountId, /// The approved transferrer of this item, if one is set. - pub(super) approved: Option, + pub approved: Option, /// Whether the item can be transferred or not. - pub(super) is_frozen: bool, + pub is_frozen: bool, /// The amount held in the pallet's default account for this item. Free-hold items will have /// this as zero. - pub(super) deposit: DepositBalance, + pub deposit: DepositBalance, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)] @@ -107,13 +107,13 @@ pub struct CollectionMetadata> { /// The balance deposited for this metadata. /// /// This pays for the data stored in this struct. - pub(super) deposit: DepositBalance, + pub deposit: DepositBalance, /// General information concerning this collection. Limited in length by `StringLimit`. This /// will generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. - pub(super) data: BoundedVec, + pub data: BoundedVec, /// Whether the collection's metadata may be changed by a non Force origin. - pub(super) is_frozen: bool, + pub is_frozen: bool, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)] @@ -123,11 +123,11 @@ pub struct ItemMetadata> { /// The balance deposited for this metadata. /// /// This pays for the data stored in this struct. - pub(super) deposit: DepositBalance, + pub deposit: DepositBalance, /// General information concerning this item. Limited in length by `StringLimit`. This will /// generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. - pub(super) data: BoundedVec, + pub data: BoundedVec, /// Whether the item metadata may be changed by a non Force origin. - pub(super) is_frozen: bool, + pub is_frozen: bool, } -- GitLab From d1cf996354342b33bd7ae327ddae68b286784363 Mon Sep 17 00:00:00 2001 From: Egor_P Date: Tue, 22 Oct 2024 09:25:11 +0200 Subject: [PATCH 26/50] [Backport] Version bumps from stable2409-1 (#6153) This PR backports regular version bumps and prdocs reordering from the current stable release back to master --- cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs | 2 +- cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs | 2 +- polkadot/node/primitives/src/lib.rs | 2 +- prdoc/{ => 1.16.1}/pr_4803.prdoc | 0 prdoc/{ => 1.16.1}/pr_5599.prdoc | 0 prdoc/{ => 1.16.1}/pr_5753.prdoc | 0 prdoc/{ => 1.16.1}/pr_5887.prdoc | 0 prdoc/{ => 1.16.1}/pr_5913.prdoc | 0 prdoc/{ => 1.16.1}/pr_6031.prdoc | 0 9 files changed, 3 insertions(+), 3 deletions(-) rename prdoc/{ => 1.16.1}/pr_4803.prdoc (100%) rename prdoc/{ => 1.16.1}/pr_5599.prdoc (100%) rename prdoc/{ => 1.16.1}/pr_5753.prdoc (100%) rename prdoc/{ => 1.16.1}/pr_5887.prdoc (100%) rename prdoc/{ => 1.16.1}/pr_5913.prdoc (100%) rename prdoc/{ => 1.16.1}/pr_6031.prdoc (100%) 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 2f25fa0ec1d..ae5d2102ff6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -123,7 +123,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("statemine"), impl_name: create_runtime_str!("statemine"), authoring_version: 1, - spec_version: 1_016_001, + spec_version: 1_016_002, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, 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 14f24228d0a..0da80098b28 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -123,7 +123,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westmint"), impl_name: create_runtime_str!("westmint"), authoring_version: 1, - spec_version: 1_016_001, + spec_version: 1_016_002, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index 99d3a3e515b..a525b2bc977 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -59,7 +59,7 @@ pub use disputes::{ /// relatively rare. /// /// The associated worker binaries should use the same version as the node that spawns them. -pub const NODE_VERSION: &'static str = "1.16.0"; +pub const NODE_VERSION: &'static str = "1.16.1"; // For a 16-ary Merkle Prefix Trie, we can expect at most 16 32-byte hashes per node // plus some overhead: diff --git a/prdoc/pr_4803.prdoc b/prdoc/1.16.1/pr_4803.prdoc similarity index 100% rename from prdoc/pr_4803.prdoc rename to prdoc/1.16.1/pr_4803.prdoc diff --git a/prdoc/pr_5599.prdoc b/prdoc/1.16.1/pr_5599.prdoc similarity index 100% rename from prdoc/pr_5599.prdoc rename to prdoc/1.16.1/pr_5599.prdoc diff --git a/prdoc/pr_5753.prdoc b/prdoc/1.16.1/pr_5753.prdoc similarity index 100% rename from prdoc/pr_5753.prdoc rename to prdoc/1.16.1/pr_5753.prdoc diff --git a/prdoc/pr_5887.prdoc b/prdoc/1.16.1/pr_5887.prdoc similarity index 100% rename from prdoc/pr_5887.prdoc rename to prdoc/1.16.1/pr_5887.prdoc diff --git a/prdoc/pr_5913.prdoc b/prdoc/1.16.1/pr_5913.prdoc similarity index 100% rename from prdoc/pr_5913.prdoc rename to prdoc/1.16.1/pr_5913.prdoc diff --git a/prdoc/pr_6031.prdoc b/prdoc/1.16.1/pr_6031.prdoc similarity index 100% rename from prdoc/pr_6031.prdoc rename to prdoc/1.16.1/pr_6031.prdoc -- GitLab From 356386b56881a7a2cb1b0be5628ad2a97678b34d Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Tue, 22 Oct 2024 12:18:48 +0300 Subject: [PATCH 27/50] Fix TrustedQueryApi Error (#6170) Related to https://github.com/paritytech/polkadot-sdk/issues/6161 This seems to fix the `JavaScript heap out of memory` error encountered in the bridge zombienet tests lately. This is just a partial fix, since we also need to address https://github.com/paritytech/polkadot-sdk/issues/6133 in order to fully fix the bridge zombienet tests --- .gitlab/pipeline/zombienet.yml | 1 - polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/.gitlab/pipeline/zombienet.yml b/.gitlab/pipeline/zombienet.yml index 5aa37f783a0..08bfed2e24c 100644 --- a/.gitlab/pipeline/zombienet.yml +++ b/.gitlab/pipeline/zombienet.yml @@ -9,7 +9,6 @@ RUN_IN_CI: "1" KUBERNETES_CPU_REQUEST: "512m" KUBERNETES_MEMORY_REQUEST: "1Gi" - NODE_OPTIONS: "--max-old-space-size=8192" timeout: 60m include: diff --git a/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs b/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs index e75af54ad2f..a2e3e162548 100644 --- a/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs +++ b/polkadot/xcm/xcm-runtime-apis/src/trusted_query.rs @@ -44,9 +44,7 @@ sp_api::decl_runtime_apis! { #[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] pub enum Error { /// Converting a versioned Asset structure from one version to another failed. - #[codec(index = 1)] VersionedAssetConversionFailed, /// Converting a versioned Location structure from one version to another failed. - #[codec(index = 1)] VersionedLocationConversionFailed, } -- GitLab From 21930ed2019219c2ffd57c08c0bf675db467a91f Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Tue, 22 Oct 2024 12:27:30 +0200 Subject: [PATCH 28/50] [pallet-revive] Eth RPC integration (#5866) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR introduces the necessary changes to pallet-revive for integrating with our Ethereum JSON-RPC. The RPC proxy itself will be added in a follow up. ## Changes - A new pallet::call `Call::eth_transact`. This is used as a wrapper to accept unsigned Ethereum transaction, valid call will be routed to `Call::call` or `Call::instantiate_with_code` - A custom UncheckedExtrinsic struct, that wraps the generic one usually and add the ability to check eth_transact calls sent from an Ethereum JSON-RPC proxy. - Generated types and traits to support implementing a JSON-RPC Ethereum proxy. ## Flow Overview: - A user submits a transaction via MetaMask or another Ethereum-compatible wallet. - The proxy dry run the transaction and add metadata to the call (gas limit in Weight, storage deposit limit, and length of bytecode and constructor input for contract instantiation) - The raw transaction, along with the additional metadata, is submitted to the node as an unsigned extrinsic. - On the runtime, our custom UncheckedExtrinsic define a custom Checkable implementation that converts the unsigned extrinsics into checked one - It recovers the signer - validates the payload, and injects signed extensions, allowing the system to increment the nonce and charge the appropriate fees. - re-route the call to pallet-revive::Call::call or pallet-revive::Call::instantiateWithCode ## Dependencies - https://github.com/koute/polkavm/pull/188 ## Follow up PRs - #5926 - #6147 (previously #5953) - #5502 --------- Co-authored-by: Alexander Theißen Co-authored-by: Cyrill Leutwiler --- Cargo.lock | 171 ++++- prdoc/pr_5866.prdoc | 23 + substrate/bin/node/cli/Cargo.toml | 3 +- .../bin/node/cli/benches/block_production.rs | 10 +- substrate/bin/node/cli/src/chain_spec.rs | 33 +- substrate/bin/node/cli/src/service.rs | 19 +- substrate/bin/node/cli/tests/basic.rs | 8 +- substrate/bin/node/cli/tests/fees.rs | 2 +- .../bin/node/cli/tests/submit_transaction.rs | 9 +- substrate/bin/node/runtime/Cargo.toml | 2 + substrate/bin/node/runtime/src/lib.rs | 79 +- substrate/bin/node/testing/Cargo.toml | 1 + substrate/bin/node/testing/src/bench.rs | 15 +- substrate/bin/node/testing/src/keyring.rs | 15 +- substrate/frame/revive/Cargo.toml | 41 +- substrate/frame/revive/fixtures/Cargo.toml | 11 +- substrate/frame/revive/fixtures/build.rs | 9 +- .../frame/revive/fixtures/build/Cargo.toml | 2 +- substrate/frame/revive/fixtures/src/lib.rs | 23 +- .../frame/revive/mock-network/Cargo.toml | 14 + substrate/frame/revive/src/address.rs | 2 +- substrate/frame/revive/src/evm.rs | 22 + substrate/frame/revive/src/evm/api.rs | 38 + substrate/frame/revive/src/evm/api/account.rs | 51 ++ substrate/frame/revive/src/evm/api/byte.rs | 154 ++++ .../frame/revive/src/evm/api/rlp_codec.rs | 219 ++++++ .../frame/revive/src/evm/api/rpc_types.rs | 32 + .../frame/revive/src/evm/api/rpc_types_gen.rs | 682 +++++++++++++++++ .../frame/revive/src/evm/api/signature.rs | 80 ++ substrate/frame/revive/src/evm/api/type_id.rs | 95 +++ substrate/frame/revive/src/evm/runtime.rs | 685 ++++++++++++++++++ substrate/frame/revive/src/exec.rs | 15 +- substrate/frame/revive/src/lib.rs | 271 ++++++- substrate/frame/revive/src/primitives.rs | 21 +- .../frame/revive/src/test_utils/builder.rs | 9 +- substrate/frame/revive/src/tests.rs | 36 +- substrate/frame/revive/src/wasm/mod.rs | 5 +- substrate/frame/revive/src/wasm/runtime.rs | 21 +- substrate/frame/revive/uapi/Cargo.toml | 2 +- umbrella/Cargo.toml | 2 +- 40 files changed, 2769 insertions(+), 163 deletions(-) create mode 100644 prdoc/pr_5866.prdoc create mode 100644 substrate/frame/revive/src/evm.rs create mode 100644 substrate/frame/revive/src/evm/api.rs create mode 100644 substrate/frame/revive/src/evm/api/account.rs create mode 100644 substrate/frame/revive/src/evm/api/byte.rs create mode 100644 substrate/frame/revive/src/evm/api/rlp_codec.rs create mode 100644 substrate/frame/revive/src/evm/api/rpc_types.rs create mode 100644 substrate/frame/revive/src/evm/api/rpc_types_gen.rs create mode 100644 substrate/frame/revive/src/evm/api/signature.rs create mode 100644 substrate/frame/revive/src/evm/api/type_id.rs create mode 100644 substrate/frame/revive/src/evm/runtime.rs diff --git a/Cargo.lock b/Cargo.lock index e2cce727856..a42f5baa476 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1619,6 +1619,22 @@ dependencies = [ "syn 2.0.82", ] +[[package]] +name = "bip32" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa13fae8b6255872fd86f7faf4b41168661d7d78609f7bfe6771b85c6739a15b" +dependencies = [ + "bs58", + "hmac 0.12.1", + "k256", + "rand_core 0.6.4", + "ripemd", + "sha2 0.10.8", + "subtle 2.5.0", + "zeroize", +] + [[package]] name = "bip39" version = "2.0.0" @@ -2571,6 +2587,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ + "sha2 0.10.8", "tinyvec", ] @@ -6798,6 +6815,10 @@ name = "futures-timer" version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +dependencies = [ + "gloo-timers", + "send_wrapper 0.4.0", +] [[package]] name = "futures-util" @@ -6933,6 +6954,27 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 1.1.0", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "gloo-timers" version = "0.2.6" @@ -6945,6 +6987,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "glutton-westend-runtime" version = "3.0.0" @@ -8046,11 +8101,13 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ec465b607a36dc5dd45d48b7689bc83f679f66a3ac6b6b21cc787a11e0f8685" dependencies = [ + "jsonrpsee-client-transport 0.24.3", "jsonrpsee-core 0.24.3", "jsonrpsee-http-client 0.24.3", "jsonrpsee-proc-macros", "jsonrpsee-server", "jsonrpsee-types 0.24.3", + "jsonrpsee-wasm-client", "jsonrpsee-ws-client 0.24.3", "tokio", "tracing", @@ -8107,7 +8164,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90f0977f9c15694371b8024c35ab58ca043dbbf4b51ccb03db8858a021241df1" dependencies = [ "base64 0.22.1", + "futures-channel", "futures-util", + "gloo-net", "http 1.1.0", "jsonrpsee-core 0.24.3", "pin-project", @@ -8192,6 +8251,7 @@ dependencies = [ "tokio", "tokio-stream", "tracing", + "wasm-bindgen-futures", ] [[package]] @@ -8317,6 +8377,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0470d0ae043ffcb0cd323797a631e637fb4b55fe3eaa6002934819458bba62a7" +dependencies = [ + "jsonrpsee-client-transport 0.24.3", + "jsonrpsee-core 0.24.3", + "jsonrpsee-types 0.24.3", +] + [[package]] name = "jsonrpsee-ws-client" version = "0.23.2" @@ -8380,6 +8451,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-hash" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b286e6b663fb926e1eeb68528e69cb70ed46c6d65871a21b2215ae8154c6d3c" +dependencies = [ + "primitive-types 0.12.2", + "tiny-keccak", +] + [[package]] name = "keccak-hasher" version = "0.16.0" @@ -8410,6 +8491,7 @@ dependencies = [ "primitive-types 0.13.1", "scale-info", "serde_json", + "sp-debug-derive 14.0.0", "static_assertions", "substrate-wasm-builder", ] @@ -9045,7 +9127,7 @@ dependencies = [ "futures", "js-sys", "libp2p-core", - "send_wrapper", + "send_wrapper 0.6.0", "wasm-bindgen", "wasm-bindgen-futures", ] @@ -10235,6 +10317,7 @@ dependencies = [ "pallet-asset-conversion-tx-payment", "pallet-asset-tx-payment", "pallet-assets", + "pallet-revive", "pallet-skip-feeless-payment", "parity-scale-codec", "sc-block-builder", @@ -12340,11 +12423,16 @@ dependencies = [ "array-bytes", "assert_matches", "bitflags 1.3.2", + "derive_more", "environmental", + "ethereum-types", "frame-benchmarking", "frame-support", "frame-system", + "hex", + "hex-literal", "impl-trait-for-tuples", + "jsonrpsee 0.24.3", "log", "pallet-assets", "pallet-balances", @@ -12354,25 +12442,30 @@ dependencies = [ "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-timestamp", + "pallet-transaction-payment", "pallet-utility", "parity-scale-codec", "paste", - "polkavm 0.12.0", - "polkavm-common 0.12.0", + "polkavm 0.13.0", + "polkavm-common 0.13.0", "pretty_assertions", "rlp 0.6.1", "scale-info", + "secp256k1", "serde", + "serde_json", "sp-api 26.0.0", + "sp-arithmetic 23.0.0", "sp-core 28.0.0", "sp-io 30.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "sp-std 14.0.0", "sp-tracing 16.0.0", + "sp-weights 27.0.0", "staging-xcm", "staging-xcm-builder", - "wat", + "subxt-signer", ] [[package]] @@ -12383,7 +12476,7 @@ dependencies = [ "frame-system", "log", "parity-wasm", - "polkavm-linker 0.12.0", + "polkavm-linker 0.13.0", "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", @@ -12443,7 +12536,7 @@ dependencies = [ "bitflags 1.3.2", "parity-scale-codec", "paste", - "polkavm-derive 0.12.0", + "polkavm-derive 0.13.0", "scale-info", ] @@ -16111,15 +16204,15 @@ dependencies = [ [[package]] name = "polkavm" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27910c5061e4cea6be6c66684b49d0f42b6a05900c9b0da9e7f3dd2d587a8d4" +checksum = "57e79a14b15ed38cb5b9a1e38d02e933f19e3d180ae5b325fed606c5e5b9177e" dependencies = [ "libc", "log", - "polkavm-assembler 0.12.0", - "polkavm-common 0.12.0", - "polkavm-linux-raw 0.12.0", + "polkavm-assembler 0.13.0", + "polkavm-common 0.13.0", + "polkavm-linux-raw 0.13.0", ] [[package]] @@ -16133,9 +16226,9 @@ dependencies = [ [[package]] name = "polkavm-assembler" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82f0e374fa043f31459b30d629d7e866247ac4b6c7662ac72e4e5bf50d052b92" +checksum = "4e8da55465000feb0a61bbf556ed03024db58f3420eca37721fc726b3b2136bf" dependencies = [ "log", ] @@ -16157,13 +16250,13 @@ dependencies = [ [[package]] name = "polkavm-common" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e42e082c3d89da2346555baf4d951fe07dcb9208e42a02c272e6d5d0326f9a" +checksum = "084b4339aae7dfdaaa5aa7d634110afd95970e0737b6fb2a0cb10db8b56b753c" dependencies = [ "blake3", "log", - "polkavm-assembler 0.12.0", + "polkavm-assembler 0.13.0", ] [[package]] @@ -16186,11 +16279,11 @@ dependencies = [ [[package]] name = "polkavm-derive" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540b798393e68a890202d5dc9f86a985b7ea83611e3406d90dc1043e7997b4d1" +checksum = "f4456b9657b2abd04ac41a61c99e206b7410f93daf0e9b42b49089508d836c40" dependencies = [ - "polkavm-derive-impl-macro 0.12.0", + "polkavm-derive-impl-macro 0.13.0", ] [[package]] @@ -16219,11 +16312,11 @@ dependencies = [ [[package]] name = "polkavm-derive-impl" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d179eddaaef62ce5960faaa2ec9e8f131c81661c8b9365c4d55b275011688534" +checksum = "5e4f2c19e7ccc53d8e21429e83b6589bd4139d15481e455a90ba4335a4decb5a" dependencies = [ - "polkavm-common 0.12.0", + "polkavm-common 0.13.0", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", @@ -16251,11 +16344,11 @@ dependencies = [ [[package]] name = "polkavm-derive-impl-macro" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd35472599d35d90e24afe9eb39ae6ee6cb1b924f0c03b277ef8b5f174a63853" +checksum = "e6f3ad876ca1855038c21d48cbe35164552208a54b21f8295a7d76bc33ef1e38" dependencies = [ - "polkavm-derive-impl 0.12.0", + "polkavm-derive-impl 0.13.0", "syn 2.0.82", ] @@ -16276,15 +16369,15 @@ dependencies = [ [[package]] name = "polkavm-linker" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f917b16db9ab13819a738a321b48a2d0d20d9e32dedcff75054148676afbec4" +checksum = "4aa6e5a396abf195289d6d63d70182e59a7c27b9b06d0b7361317df05c07c8a8" dependencies = [ "gimli 0.28.0", "hashbrown 0.14.5", "log", "object 0.36.1", - "polkavm-common 0.12.0", + "polkavm-common 0.13.0", "regalloc2 0.9.3", "rustc-demangle", ] @@ -16297,9 +16390,9 @@ checksum = "26e85d3456948e650dff0cfc85603915847faf893ed1e66b020bb82ef4557120" [[package]] name = "polkavm-linux-raw" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d280301d5b5a321c732173c969058f4b5726f3a0046f6802f396df2599f3753d" +checksum = "686c4dd9c9c16cc22565b51bdbb269792318d0fd2e6b966b5f6c788534cad0e9" [[package]] name = "polling" @@ -17568,6 +17661,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "rle-decode-fast" version = "1.0.3" @@ -20194,6 +20296,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "send_wrapper" version = "0.6.0" @@ -23293,6 +23401,7 @@ dependencies = [ "sp-keyring", "staging-node-inspect", "substrate-cli-test-utils", + "subxt-signer", "tempfile", "tokio", "tokio-util", @@ -24126,10 +24235,12 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49888ae6ae90fe01b471193528eea5bd4ed52d8eecd2d13f4a2333b87388850" dependencies = [ + "bip32", "bip39", "cfg-if", "hex", "hmac 0.12.1", + "keccak-hash", "parity-scale-codec", "pbkdf2", "regex", diff --git a/prdoc/pr_5866.prdoc b/prdoc/pr_5866.prdoc new file mode 100644 index 00000000000..44fffe1d212 --- /dev/null +++ b/prdoc/pr_5866.prdoc @@ -0,0 +1,23 @@ +title: "[pallet-revive] Ethereum JSON-RPC integration" + +doc: + - audience: Runtime Dev + description: | + Related PR: https://github.com/paritytech/revive-ethereum-rpc/pull/5 + + Changes Included: + - A new pallet::call eth_transact. + - A custom UncheckedExtrinsic struct to dispatch unsigned eth_transact calls from an Ethereum JSON-RPC proxy. + - Generated types and traits to support implementing a JSON-RPC Ethereum proxy. +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: patch + - name: pallet-revive-mock-network + bump: patch + - name: pallet-revive-uapi + bump: patch + - name: polkadot-sdk + bump: patch + diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 2e53c18645f..933406670e5 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -46,6 +46,7 @@ futures = { workspace = true } log = { workspace = true, default-features = true } rand = { workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } +subxt-signer = { workspace = true, features = ["unstable-eth"] } # The Polkadot-SDK: polkadot-sdk = { features = [ @@ -56,8 +57,6 @@ polkadot-sdk = { features = [ "generate-bags", "mmr-gadget", "mmr-rpc", - "pallet-contracts-mock-network", - "pallet-revive-mock-network", "pallet-transaction-payment-rpc", "sc-allocator", "sc-authority-discovery", diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index d8041ed400c..da82729dbec 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -39,6 +39,7 @@ use sp_blockchain::{ApplyExtrinsicFailed::Validity, Error::ApplyExtrinsicFailed} use sp_consensus::BlockOrigin; use sp_keyring::Sr25519Keyring; use sp_runtime::{ + generic, transaction_validity::{InvalidTransaction, TransactionValidityError}, AccountId32, MultiAddress, OpaqueExtrinsic, }; @@ -120,10 +121,11 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { } fn extrinsic_set_time(now: u64) -> OpaqueExtrinsic { - kitchensink_runtime::UncheckedExtrinsic::new_bare(kitchensink_runtime::RuntimeCall::Timestamp( - pallet_timestamp::Call::set { now }, - )) - .into() + let utx: kitchensink_runtime::UncheckedExtrinsic = generic::UncheckedExtrinsic::new_bare( + kitchensink_runtime::RuntimeCall::Timestamp(pallet_timestamp::Call::set { now }), + ) + .into(); + utx.into() } fn import_block(client: &FullClient, built: BuiltBlock) { diff --git a/substrate/bin/node/cli/src/chain_spec.rs b/substrate/bin/node/cli/src/chain_spec.rs index 6cd9adfd7f2..362deacba5f 100644 --- a/substrate/bin/node/cli/src/chain_spec.rs +++ b/substrate/bin/node/cli/src/chain_spec.rs @@ -20,6 +20,7 @@ use polkadot_sdk::*; +use crate::chain_spec::{sc_service::Properties, sp_runtime::AccountId32}; use kitchensink_runtime::{ constants::currency::*, wasm_binary_unwrap, Block, MaxNominations, SessionKeys, StakerStatus, }; @@ -291,8 +292,8 @@ fn configure_accounts( usize, Vec<(AccountId, AccountId, Balance, StakerStatus)>, ) { - let mut endowed_accounts: Vec = endowed_accounts - .unwrap_or_else(|| Sr25519Keyring::well_known().map(|k| k.to_account_id()).collect()); + let mut endowed_accounts: Vec = + endowed_accounts.unwrap_or_else(default_endowed_accounts); // endow all authorities and nominators. initial_authorities .iter() @@ -417,12 +418,40 @@ fn development_config_genesis_json() -> serde_json::Value { ) } +fn props() -> Properties { + let mut properties = Properties::new(); + properties.insert("tokenDecimals".to_string(), 12.into()); + properties +} + +fn eth_account(from: subxt_signer::eth::Keypair) -> AccountId32 { + let mut account_id = AccountId32::new([0xEE; 32]); + >::as_mut(&mut account_id)[..20] + .copy_from_slice(&from.account_id().0); + account_id +} + +fn default_endowed_accounts() -> Vec { + Sr25519Keyring::well_known() + .map(|k| k.to_account_id()) + .chain([ + eth_account(subxt_signer::eth::dev::alith()), + eth_account(subxt_signer::eth::dev::baltathar()), + eth_account(subxt_signer::eth::dev::charleth()), + eth_account(subxt_signer::eth::dev::dorothy()), + eth_account(subxt_signer::eth::dev::ethan()), + eth_account(subxt_signer::eth::dev::faith()), + ]) + .collect() +} + /// Development config (single validator Alice). pub fn development_config() -> ChainSpec { ChainSpec::builder(wasm_binary_unwrap(), Default::default()) .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) + .with_properties(props()) .with_genesis_config_patch(development_config_genesis_json()) .build() } diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 00658b361df..d71f1304caf 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -158,12 +158,13 @@ pub fn create_extrinsic( ); let signature = raw_payload.using_encoded(|e| sender.sign(e)); - kitchensink_runtime::UncheckedExtrinsic::new_signed( + generic::UncheckedExtrinsic::new_signed( function, sp_runtime::AccountId32::from(sender.public()).into(), kitchensink_runtime::Signature::Sr25519(signature), tx_ext, ) + .into() } /// Creates a new partial node. @@ -866,7 +867,7 @@ mod tests { use codec::Encode; use kitchensink_runtime::{ constants::{currency::CENTS, time::SLOT_DURATION}, - Address, BalancesCall, RuntimeCall, TxExtension, UncheckedExtrinsic, + Address, BalancesCall, RuntimeCall, TxExtension, }; use node_primitives::{Block, DigestItem, Signature}; use polkadot_sdk::{sc_transaction_pool_api::MaintainedTransactionPool, *}; @@ -883,7 +884,7 @@ mod tests { use sp_keyring::AccountKeyring; use sp_keystore::KeystorePtr; use sp_runtime::{ - generic::{Digest, Era, SignedPayload}, + generic::{self, Digest, Era, SignedPayload}, key_types::BABE, traits::{Block as BlockT, Header as HeaderT, IdentifyAccount, Verify}, RuntimeAppPublic, @@ -1099,8 +1100,16 @@ mod tests { let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); let (function, tx_ext, _) = raw_payload.deconstruct(); index += 1; - UncheckedExtrinsic::new_signed(function, from.into(), signature.into(), tx_ext) - .into() + let utx: kitchensink_runtime::UncheckedExtrinsic = + generic::UncheckedExtrinsic::new_signed( + function, + from.into(), + signature.into(), + tx_ext, + ) + .into(); + + utx.into() }, ); } diff --git a/substrate/bin/node/cli/tests/basic.rs b/substrate/bin/node/cli/tests/basic.rs index 616d813f78e..8f1475fce4f 100644 --- a/substrate/bin/node/cli/tests/basic.rs +++ b/substrate/bin/node/cli/tests/basic.rs @@ -61,14 +61,14 @@ pub fn bloaty_code_unwrap() -> &'static [u8] { /// correct multiplier. fn transfer_fee(extrinsic: &UncheckedExtrinsic) -> Balance { let mut info = default_transfer_call().get_dispatch_info(); - info.extension_weight = extrinsic.extension_weight(); + info.extension_weight = extrinsic.0.extension_weight(); TransactionPayment::compute_fee(extrinsic.encode().len() as u32, &info, 0) } /// Default transfer fee, same as `transfer_fee`, but with a weight refund factored in. fn transfer_fee_with_refund(extrinsic: &UncheckedExtrinsic, weight_refund: Weight) -> Balance { let mut info = default_transfer_call().get_dispatch_info(); - info.extension_weight = extrinsic.extension_weight(); + info.extension_weight = extrinsic.0.extension_weight(); let post_info = (Some(info.total_weight().saturating_sub(weight_refund)), info.pays_fee).into(); TransactionPayment::compute_actual_fee(extrinsic.encode().len() as u32, &info, &post_info, 0) } @@ -324,7 +324,7 @@ fn full_native_block_import_works() { let mut alice_last_known_balance: Balance = Default::default(); let mut fees = t.execute_with(|| transfer_fee(&xt())); - let extension_weight = xt().extension_weight(); + let extension_weight = xt().0.extension_weight(); let weight_refund = Weight::zero(); let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund)); @@ -427,7 +427,7 @@ fn full_native_block_import_works() { fees = t.execute_with(|| transfer_fee(&xt())); let pot = t.execute_with(|| Treasury::pot()); - let extension_weight = xt().extension_weight(); + let extension_weight = xt().0.extension_weight(); let weight_refund = Weight::zero(); let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund)); diff --git a/substrate/bin/node/cli/tests/fees.rs b/substrate/bin/node/cli/tests/fees.rs index 59ade9b8547..da9d2662408 100644 --- a/substrate/bin/node/cli/tests/fees.rs +++ b/substrate/bin/node/cli/tests/fees.rs @@ -175,7 +175,7 @@ fn transaction_fee_is_correct() { balance_alice -= length_fee; let mut info = default_transfer_call().get_dispatch_info(); - info.extension_weight = xt.extension_weight(); + info.extension_weight = xt.0.extension_weight(); let weight = info.total_weight(); let weight_fee = IdentityFee::::weight_to_fee(&weight); diff --git a/substrate/bin/node/cli/tests/submit_transaction.rs b/substrate/bin/node/cli/tests/submit_transaction.rs index 3b7d82dcab1..3672432ae34 100644 --- a/substrate/bin/node/cli/tests/submit_transaction.rs +++ b/substrate/bin/node/cli/tests/submit_transaction.rs @@ -23,6 +23,7 @@ use sp_application_crypto::AppCrypto; use sp_core::offchain::{testing::TestTransactionPoolExt, TransactionPoolExt}; use sp_keyring::sr25519::Keyring::Alice; use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; +use sp_runtime::generic; pub mod common; use self::common::*; @@ -44,7 +45,7 @@ fn should_submit_unsigned_transaction() { }; let call = pallet_im_online::Call::heartbeat { heartbeat: heartbeat_data, signature }; - let xt = UncheckedExtrinsic::new_bare(call.into()); + let xt = generic::UncheckedExtrinsic::new_bare(call.into()).into(); SubmitTransaction::>::submit_transaction(xt) .unwrap(); @@ -130,7 +131,7 @@ fn should_submit_signed_twice_from_the_same_account() { // now check that the transaction nonces are not equal let s = state.read(); fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce { - let extra = tx.preamble.to_signed().unwrap().2; + let extra = tx.0.preamble.to_signed().unwrap().2; extra.5 } let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap()); @@ -179,7 +180,7 @@ fn should_submit_signed_twice_from_all_accounts() { // now check that the transaction nonces are not equal let s = state.read(); fn nonce(tx: UncheckedExtrinsic) -> frame_system::CheckNonce { - let extra = tx.preamble.to_signed().unwrap().2; + let extra = tx.0.preamble.to_signed().unwrap().2; extra.5 } let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap()); @@ -236,7 +237,7 @@ fn submitted_transaction_should_be_valid() { let source = TransactionSource::External; let extrinsic = UncheckedExtrinsic::decode(&mut &*tx0).unwrap(); // add balance to the account - let author = extrinsic.preamble.clone().to_signed().clone().unwrap().0; + let author = extrinsic.0.preamble.clone().to_signed().clone().unwrap().0; let address = Indices::lookup(author).unwrap(); let data = pallet_balances::AccountData { free: 5_000_000_000_000, ..Default::default() }; let account = frame_system::AccountInfo { providers: 1, data, ..Default::default() }; diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index 6310e16d5a1..7acf4294c51 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -27,6 +27,7 @@ scale-info = { features = ["derive", "serde"], workspace = true } static_assertions = { workspace = true, default-features = true } log = { workspace = true } serde_json = { features = ["alloc", "arbitrary_precision"], workspace = true } +sp-debug-derive = { workspace = true, features = ["force-debug"] } # pallet-asset-conversion: turn on "num-traits" feature primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true } @@ -56,6 +57,7 @@ std = [ "primitive-types/std", "scale-info/std", "serde_json/std", + "sp-debug-derive/std", "substrate-wasm-builder", ] runtime-benchmarks = [ diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index a2112e5977f..d6a17856e47 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -76,6 +76,7 @@ use pallet_identity::legacy::IdentityInfo; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_nfts::PalletFeatures; use pallet_nis::WithMaximumOf; +use pallet_revive::evm::runtime::EthExtra; use pallet_session::historical as pallet_session_historical; // Can't use `FungibleAdapter` here until Treasury pallet migrates to fungibles // @@ -1449,7 +1450,7 @@ where type Extension = TxExtension; fn create_transaction(call: RuntimeCall, extension: TxExtension) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_transaction(call, extension) + generic::UncheckedExtrinsic::new_transaction(call, extension).into() } } @@ -1499,7 +1500,8 @@ where let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; let address = Indices::unlookup(account); let (call, tx_ext, _) = raw_payload.deconstruct(); - let transaction = UncheckedExtrinsic::new_signed(call, address, signature, tx_ext); + let transaction = + generic::UncheckedExtrinsic::new_signed(call, address, signature, tx_ext).into(); Some(transaction) } } @@ -1509,7 +1511,7 @@ where RuntimeCall: From, { fn create_inherent(call: RuntimeCall) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_bare(call) + generic::UncheckedExtrinsic::new_bare(call).into() } } @@ -2587,6 +2589,16 @@ mod runtime { pub type VerifySignature = pallet_verify_signature::Pallet; } +impl TryFrom for pallet_revive::Call { + type Error = (); + + fn try_from(value: RuntimeCall) -> Result { + match value { + RuntimeCall::Revive(call) => Ok(call), + _ => Err(()), + } + } +} /// The address format for describing accounts. pub type Address = sp_runtime::MultiAddress; /// Block header type as expected by this runtime. @@ -2617,9 +2629,32 @@ pub type TxExtension = ( frame_metadata_hash_extension::CheckMetadataHash, ); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct EthExtraImpl; + +impl EthExtra for EthExtraImpl { + type Config = Runtime; + type Extension = TxExtension; + + fn get_eth_extension(nonce: u32, tip: Balance) -> Self::Extension { + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::from(crate::generic::Era::Immortal), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::::from(tip, None) + .into(), + frame_metadata_hash_extension::CheckMetadataHash::::new(false), + ) + } +} + /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + pallet_revive::evm::runtime::UncheckedExtrinsic; /// Unchecked signature payload type as expected by this runtime. pub type UncheckedSignaturePayload = generic::UncheckedSignaturePayload; @@ -3124,6 +3159,38 @@ impl_runtime_apis! { impl pallet_revive::ReviveApi for Runtime { + fn eth_transact( + from: H160, + dest: Option, + value: Balance, + input: Vec, + gas_limit: Option, + storage_deposit_limit: Option, + ) -> pallet_revive::EthContractResult + { + use pallet_revive::AddressMapper; + let blockweights: BlockWeights = ::BlockWeights::get(); + let origin = ::AddressMapper::to_account_id(&from); + + let encoded_size = |pallet_call| { + let call = RuntimeCall::Revive(pallet_call); + let uxt: UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic::new_bare(call).into(); + uxt.encoded_size() as u32 + }; + + Revive::bare_eth_transact( + origin, + dest, + value, + input, + gas_limit.unwrap_or(blockweights.max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + encoded_size, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + fn call( origin: AccountId, dest: H160, @@ -3131,7 +3198,7 @@ impl_runtime_apis! { gas_limit: Option, storage_deposit_limit: Option, input_data: Vec, - ) -> pallet_revive::ContractExecResult { + ) -> pallet_revive::ContractResult { Revive::bare_call( RuntimeOrigin::signed(origin), dest, @@ -3152,7 +3219,7 @@ impl_runtime_apis! { code: pallet_revive::Code, data: Vec, salt: Option<[u8; 32]>, - ) -> pallet_revive::ContractInstantiateResult + ) -> pallet_revive::ContractResult { Revive::bare_instantiate( RuntimeOrigin::signed(origin), diff --git a/substrate/bin/node/testing/Cargo.toml b/substrate/bin/node/testing/Cargo.toml index a5cec856717..16112386ad7 100644 --- a/substrate/bin/node/testing/Cargo.toml +++ b/substrate/bin/node/testing/Cargo.toml @@ -28,6 +28,7 @@ node-primitives = { workspace = true, default-features = true } kitchensink-runtime = { workspace = true } pallet-asset-conversion = { workspace = true, default-features = true } pallet-assets = { workspace = true, default-features = true } +pallet-revive = { workspace = true, default-features = true } pallet-asset-conversion-tx-payment = { workspace = true, default-features = true } pallet-asset-tx-payment = { workspace = true, default-features = true } pallet-skip-feeless-payment = { workspace = true, default-features = true } diff --git a/substrate/bin/node/testing/src/bench.rs b/substrate/bin/node/testing/src/bench.rs index cce1627a2ad..3812524f0b1 100644 --- a/substrate/bin/node/testing/src/bench.rs +++ b/substrate/bin/node/testing/src/bench.rs @@ -53,7 +53,7 @@ use sp_core::{ use sp_crypto_hashing::blake2_256; use sp_inherents::InherentData; use sp_runtime::{ - generic::{ExtrinsicFormat, Preamble, EXTRINSIC_FORMAT_VERSION}, + generic::{self, ExtrinsicFormat, Preamble, EXTRINSIC_FORMAT_VERSION}, traits::{Block as BlockT, IdentifyAccount, Verify}, OpaqueExtrinsic, }; @@ -586,7 +586,7 @@ impl BenchKeyring { key.sign(b) } }); - UncheckedExtrinsic { + generic::UncheckedExtrinsic { preamble: Preamble::Signed( sp_runtime::MultiAddress::Id(signed), signature, @@ -595,15 +595,18 @@ impl BenchKeyring { ), function: payload.0, } + .into() }, - ExtrinsicFormat::Bare => UncheckedExtrinsic { + ExtrinsicFormat::Bare => generic::UncheckedExtrinsic { preamble: Preamble::Bare(EXTRINSIC_FORMAT_VERSION), function: xt.function, - }, - ExtrinsicFormat::General(tx_ext) => UncheckedExtrinsic { + } + .into(), + ExtrinsicFormat::General(tx_ext) => generic::UncheckedExtrinsic { preamble: sp_runtime::generic::Preamble::General(0, tx_ext), function: xt.function, - }, + } + .into(), } } diff --git a/substrate/bin/node/testing/src/keyring.rs b/substrate/bin/node/testing/src/keyring.rs index 2334cb3c4df..20497e85eab 100644 --- a/substrate/bin/node/testing/src/keyring.rs +++ b/substrate/bin/node/testing/src/keyring.rs @@ -24,7 +24,7 @@ use node_primitives::{AccountId, Balance, Nonce}; use sp_core::{crypto::get_public_from_string_or_panic, ecdsa, ed25519, sr25519}; use sp_crypto_hashing::blake2_256; use sp_keyring::Sr25519Keyring; -use sp_runtime::generic::{Era, ExtrinsicFormat, EXTRINSIC_FORMAT_VERSION}; +use sp_runtime::generic::{self, Era, ExtrinsicFormat, EXTRINSIC_FORMAT_VERSION}; /// Alice's account id. pub fn alice() -> AccountId { @@ -119,7 +119,7 @@ pub fn sign( } }) .into(); - UncheckedExtrinsic { + generic::UncheckedExtrinsic { preamble: sp_runtime::generic::Preamble::Signed( sp_runtime::MultiAddress::Id(signed), signature, @@ -128,14 +128,17 @@ pub fn sign( ), function: payload.0, } + .into() }, - ExtrinsicFormat::Bare => UncheckedExtrinsic { + ExtrinsicFormat::Bare => generic::UncheckedExtrinsic { preamble: sp_runtime::generic::Preamble::Bare(EXTRINSIC_FORMAT_VERSION), function: xt.function, - }, - ExtrinsicFormat::General(tx_ext) => UncheckedExtrinsic { + } + .into(), + ExtrinsicFormat::General(tx_ext) => generic::UncheckedExtrinsic { preamble: sp_runtime::generic::Preamble::General(0, tx_ext), function: xt.function, - }, + } + .into(), } } diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index e896d9e8fa2..8dbad5ffd8b 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -19,18 +19,22 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] environmental = { workspace = true } paste = { workspace = true } -polkavm = { version = "0.12.0", default-features = false } -polkavm-common = { version = "0.12.0", default-features = false } +polkavm = { version = "0.13.0", default-features = false } +polkavm-common = { version = "0.13.0", default-features = false } bitflags = { workspace = true } -codec = { features = [ - "derive", - "max-encoded-len", -], workspace = true } +codec = { features = ["derive", "max-encoded-len"], workspace = true } scale-info = { features = ["derive"], workspace = true } log = { workspace = true } -serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +serde = { features = [ + "alloc", + "derive", +], workspace = true, default-features = false } impl-trait-for-tuples = { workspace = true } rlp = { workspace = true } +derive_more = { workspace = true } +hex = { workspace = true } +jsonrpsee = { workspace = true, features = ["full"], optional = true } +ethereum-types = { workspace = true, features = ["codec", "rlp", "serialize"] } # Polkadot SDK Dependencies frame-benchmarking = { optional = true, workspace = true } @@ -40,20 +44,28 @@ pallet-balances = { optional = true, workspace = true } pallet-revive-fixtures = { workspace = true, default-features = false } pallet-revive-uapi = { workspace = true, default-features = true } pallet-revive-proc-macro = { workspace = true, default-features = true } +pallet-transaction-payment = { workspace = true } sp-api = { workspace = true } +sp-arithmetic = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +sp-weights = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } +subxt-signer = { workspace = true, optional = true, features = [ + "unstable-eth", +] } [dev-dependencies] array-bytes = { workspace = true, default-features = true } assert_matches = { workspace = true } pretty_assertions = { workspace = true } -wat = { workspace = true } pallet-revive-fixtures = { workspace = true, default-features = true } +secp256k1 = { workspace = true, features = ["recovery"] } +serde_json = { workspace = true } +hex-literal = { workspace = true } # Polkadot SDK Dependencies pallet-balances = { workspace = true, default-features = true } @@ -75,26 +87,35 @@ riscv = ["pallet-revive-fixtures/riscv"] std = [ "codec/std", "environmental/std", + "ethereum-types/std", "frame-benchmarking?/std", "frame-support/std", "frame-system/std", + "hex/std", + "jsonrpsee", "log/std", "pallet-balances?/std", "pallet-proxy/std", "pallet-revive-fixtures/std", "pallet-timestamp/std", + "pallet-transaction-payment/std", "pallet-utility/std", "polkavm-common/std", "polkavm/std", "rlp/std", "scale-info/std", - "serde", + "secp256k1/std", + "serde/std", + "serde_json/std", "sp-api/std", + "sp-arithmetic/std", "sp-core/std", "sp-io/std", "sp-keystore/std", "sp-runtime/std", "sp-std/std", + "sp-weights/std", + "subxt-signer", "xcm-builder/std", "xcm/std", ] @@ -107,6 +128,7 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", @@ -119,6 +141,7 @@ try-runtime = [ "pallet-message-queue/try-runtime", "pallet-proxy/try-runtime", "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", "pallet-utility/try-runtime", "sp-runtime/try-runtime", ] diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index 75b23fdd44d..1d89db002b7 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -21,7 +21,7 @@ log = { workspace = true } parity-wasm = { workspace = true } tempfile = { workspace = true } toml = { workspace = true } -polkavm-linker = { version = "0.12.0" } +polkavm-linker = { version = "0.13.0" } anyhow = { workspace = true, default-features = true } [features] @@ -31,11 +31,4 @@ default = ["std"] # we will remove this once there is an upstream toolchain riscv = [] # only when std is enabled all fixtures are available -std = [ - "anyhow", - "frame-system", - "log/std", - "sp-core", - "sp-io", - "sp-runtime", -] +std = ["anyhow", "frame-system", "log/std", "sp-core", "sp-io", "sp-runtime"] diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index 3178baf6bbe..cb4b7640814 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -65,7 +65,6 @@ mod build { } /// Collect all contract entries from the given source directory. - /// Contracts that have already been compiled are filtered out. fn collect_entries(contracts_dir: &Path) -> Vec { fs::read_dir(contracts_dir) .expect("src dir exists; qed") @@ -184,7 +183,13 @@ mod build { let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); let contracts_dir = fixtures_dir.join("contracts"); let uapi_dir = fixtures_dir.parent().expect("uapi dir exits; qed").join("uapi"); - let out_dir: PathBuf = env::var("OUT_DIR")?.into(); + let ws_dir: PathBuf = env::var("CARGO_WORKSPACE_ROOT_DIR")?.into(); + let out_dir: PathBuf = ws_dir.join("target").join("pallet-revive-fixtures"); + + // create out_dir if it does not exist + if !out_dir.exists() { + fs::create_dir_all(&out_dir)?; + } // the fixtures have a dependency on the uapi crate println!("cargo::rerun-if-changed={}", fixtures_dir.display()); diff --git a/substrate/frame/revive/fixtures/build/Cargo.toml b/substrate/frame/revive/fixtures/build/Cargo.toml index 948d7438cf9..c4aaf131148 100644 --- a/substrate/frame/revive/fixtures/build/Cargo.toml +++ b/substrate/frame/revive/fixtures/build/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] uapi = { package = 'pallet-revive-uapi', path = "", default-features = false } common = { package = 'pallet-revive-fixtures-common', path = "" } -polkavm-derive = { version = "0.12.0" } +polkavm-derive = { version = "0.13.0" } [profile.release] opt-level = 3 diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs index 5548dca66d0..eacd63b97e5 100644 --- a/substrate/frame/revive/fixtures/src/lib.rs +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -22,8 +22,11 @@ extern crate alloc; /// Load a given wasm module and returns a wasm binary contents along with it's hash. #[cfg(feature = "std")] pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, sp_core::H256)> { - let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); - let fixture_path = out_dir.join(format!("{fixture_name}.polkavm")); + let ws_dir: std::path::PathBuf = env!("CARGO_WORKSPACE_ROOT_DIR").into(); + let fixture_path = ws_dir + .join("target") + .join("pallet-revive-fixtures") + .join(format!("{fixture_name}.polkavm")); log::debug!("Loading fixture from {fixture_path:?}"); let binary = std::fs::read(fixture_path)?; let code_hash = sp_io::hashing::keccak_256(&binary); @@ -40,7 +43,12 @@ pub mod bench { #[cfg(feature = "riscv")] macro_rules! fixture { ($name: literal) => { - include_bytes!(concat!(env!("OUT_DIR"), "/", $name, ".polkavm")) + include_bytes!(concat!( + env!("CARGO_WORKSPACE_ROOT_DIR"), + "/target/pallet-revive-fixtures/", + $name, + ".polkavm" + )) }; } #[cfg(not(feature = "riscv"))] @@ -63,12 +71,3 @@ pub mod bench { dummy } } - -#[cfg(test)] -mod test { - #[test] - fn out_dir_should_have_compiled_mocks() { - let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); - assert!(out_dir.join("dummy.polkavm").exists()); - } -} diff --git a/substrate/frame/revive/mock-network/Cargo.toml b/substrate/frame/revive/mock-network/Cargo.toml index 85656a57b49..12de634b0b4 100644 --- a/substrate/frame/revive/mock-network/Cargo.toml +++ b/substrate/frame/revive/mock-network/Cargo.toml @@ -87,3 +87,17 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-proxy/try-runtime", + "pallet-revive/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-utility/try-runtime", + "pallet-xcm/try-runtime", + "polkadot-runtime-parachains/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/revive/src/address.rs b/substrate/frame/revive/src/address.rs index c51940ba771..4633fce1f32 100644 --- a/substrate/frame/revive/src/address.rs +++ b/substrate/frame/revive/src/address.rs @@ -34,7 +34,7 @@ use sp_runtime::AccountId32; /// case for all existing runtimes as of right now. Reasing is that this will allow /// us to reverse an address -> account_id mapping by just stripping the prefix. pub trait AddressMapper: private::Sealed { - /// Convert an account id to an ethereum adress. + /// Convert an account id to an ethereum address. /// /// This mapping is **not** required to be reversible. fn to_address(account_id: &T) -> H160; diff --git a/substrate/frame/revive/src/evm.rs b/substrate/frame/revive/src/evm.rs new file mode 100644 index 00000000000..c3495fc0559 --- /dev/null +++ b/substrate/frame/revive/src/evm.rs @@ -0,0 +1,22 @@ +// 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. +//!Types, and traits to integrate pallet-revive with EVM. +#![warn(missing_docs)] + +mod api; +pub use api::*; +pub mod runtime; diff --git a/substrate/frame/revive/src/evm/api.rs b/substrate/frame/revive/src/evm/api.rs new file mode 100644 index 00000000000..fe18c8735be --- /dev/null +++ b/substrate/frame/revive/src/evm/api.rs @@ -0,0 +1,38 @@ +// 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. +//! JSON-RPC methods and types, for Ethereum. + +mod byte; +pub use byte::*; + +mod rlp_codec; +pub use rlp; + +mod type_id; +pub use type_id::*; + +mod rpc_types; +mod rpc_types_gen; +pub use rpc_types_gen::*; + +#[cfg(feature = "std")] +mod account; + +#[cfg(feature = "std")] +pub use account::*; + +mod signature; diff --git a/substrate/frame/revive/src/evm/api/account.rs b/substrate/frame/revive/src/evm/api/account.rs new file mode 100644 index 00000000000..c2217defc31 --- /dev/null +++ b/substrate/frame/revive/src/evm/api/account.rs @@ -0,0 +1,51 @@ +// 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. +//! Utilities for working with Ethereum accounts. +use crate::{ + evm::{TransactionLegacySigned, TransactionLegacyUnsigned}, + H160, +}; +use rlp::Encodable; + +/// A simple account that can sign transactions +pub struct Account(subxt_signer::eth::Keypair); + +impl Default for Account { + fn default() -> Self { + Self(subxt_signer::eth::dev::alith()) + } +} + +impl From for Account { + fn from(kp: subxt_signer::eth::Keypair) -> Self { + Self(kp) + } +} + +impl Account { + /// Get the [`H160`] address of the account. + pub fn address(&self) -> H160 { + H160::from_slice(&self.0.account_id().as_ref()) + } + + /// Sign a transaction. + pub fn sign_transaction(&self, tx: TransactionLegacyUnsigned) -> TransactionLegacySigned { + let rlp_encoded = tx.rlp_bytes(); + let signature = self.0.sign(&rlp_encoded); + TransactionLegacySigned::from(tx, signature.as_ref()) + } +} diff --git a/substrate/frame/revive/src/evm/api/byte.rs b/substrate/frame/revive/src/evm/api/byte.rs new file mode 100644 index 00000000000..df4ed1740ec --- /dev/null +++ b/substrate/frame/revive/src/evm/api/byte.rs @@ -0,0 +1,154 @@ +// 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. +//! Define Byte wrapper types for encoding and decoding hex strings +use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode}; +use core::{ + fmt::{Debug, Display, Formatter, Result as FmtResult}, + str::FromStr, +}; +use hex_serde::HexCodec; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; + +mod hex_serde { + #[cfg(not(feature = "std"))] + use alloc::{format, string::String, vec::Vec}; + use serde::{Deserialize, Deserializer, Serializer}; + + pub trait HexCodec: Sized { + type Error; + fn to_hex(&self) -> String; + fn from_hex(s: String) -> Result; + } + + impl HexCodec for u8 { + type Error = core::num::ParseIntError; + fn to_hex(&self) -> String { + format!("0x{:x}", self) + } + fn from_hex(s: String) -> Result { + u8::from_str_radix(s.trim_start_matches("0x"), 16) + } + } + + impl HexCodec for [u8; T] { + type Error = hex::FromHexError; + fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self)) + } + fn from_hex(s: String) -> Result { + let data = hex::decode(s.trim_start_matches("0x"))?; + data.try_into().map_err(|_| hex::FromHexError::InvalidStringLength) + } + } + + impl HexCodec for Vec { + type Error = hex::FromHexError; + fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self)) + } + fn from_hex(s: String) -> Result { + hex::decode(s.trim_start_matches("0x")) + } + } + + pub fn serialize(value: &T, serializer: S) -> Result + where + S: Serializer, + T: HexCodec, + { + let s = value.to_hex(); + serializer.serialize_str(&s) + } + + pub fn deserialize<'de, D, T>(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: HexCodec, + ::Error: core::fmt::Debug, + { + let s = String::deserialize(deserializer)?; + let value = T::from_hex(s).map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?; + Ok(value) + } +} + +impl FromStr for Bytes { + type Err = hex::FromHexError; + fn from_str(s: &str) -> Result { + let data = hex::decode(s.trim_start_matches("0x"))?; + Ok(Bytes(data)) + } +} + +macro_rules! impl_hex { + ($type:ident, $inner:ty, $default:expr) => { + #[derive(Encode, Decode, Eq, PartialEq, TypeInfo, Clone, Serialize, Deserialize)] + #[doc = concat!("`", stringify!($inner), "`", " wrapper type for encoding and decoding hex strings")] + pub struct $type(#[serde(with = "hex_serde")] pub $inner); + + impl Default for $type { + fn default() -> Self { + $type($default) + } + } + + impl From<$inner> for $type { + fn from(inner: $inner) -> Self { + $type(inner) + } + } + + impl Debug for $type { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, concat!(stringify!($type), "({})"), self.0.to_hex()) + } + } + + impl Display for $type { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "{}", self.0.to_hex()) + } + } + }; +} + +impl_hex!(Byte, u8, 0u8); +impl_hex!(Bytes, Vec, vec![]); +impl_hex!(Bytes8, [u8; 8], [0u8; 8]); +impl_hex!(Bytes256, [u8; 256], [0u8; 256]); + +#[test] +fn serialize_works() { + let a = Byte(42); + let s = serde_json::to_string(&a).unwrap(); + assert_eq!(s, "\"0x2a\""); + let b = serde_json::from_str::(&s).unwrap(); + assert_eq!(a, b); + + let a = Bytes(b"bello world".to_vec()); + let s = serde_json::to_string(&a).unwrap(); + assert_eq!(s, "\"0x62656c6c6f20776f726c64\""); + let b = serde_json::from_str::(&s).unwrap(); + assert_eq!(a, b); + + let a = Bytes256([42u8; 256]); + let s = serde_json::to_string(&a).unwrap(); + let b = serde_json::from_str::(&s).unwrap(); + assert_eq!(a, b); +} diff --git a/substrate/frame/revive/src/evm/api/rlp_codec.rs b/substrate/frame/revive/src/evm/api/rlp_codec.rs new file mode 100644 index 00000000000..e5f24c28a48 --- /dev/null +++ b/substrate/frame/revive/src/evm/api/rlp_codec.rs @@ -0,0 +1,219 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! RLP encoding and decoding for Ethereum transactions. +//! See for more information about RLP encoding. + +use super::*; +use alloc::vec::Vec; +use rlp::{Decodable, Encodable}; + +impl TransactionLegacyUnsigned { + /// Get the rlp encoded bytes of a signed transaction with a dummy 65 bytes signature. + pub fn dummy_signed_payload(&self) -> Vec { + let mut s = rlp::RlpStream::new(); + s.append(self); + const DUMMY_SIGNATURE: [u8; 65] = [0u8; 65]; + s.append_raw(&DUMMY_SIGNATURE.as_ref(), 1); + s.out().to_vec() + } +} + +/// See +impl Encodable for TransactionLegacyUnsigned { + fn rlp_append(&self, s: &mut rlp::RlpStream) { + if let Some(chain_id) = self.chain_id { + s.begin_list(9); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas); + match self.to { + Some(ref to) => s.append(to), + None => s.append_empty_data(), + }; + s.append(&self.value); + s.append(&self.input.0); + s.append(&chain_id); + s.append(&0_u8); + s.append(&0_u8); + } else { + s.begin_list(6); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas); + match self.to { + Some(ref to) => s.append(to), + None => s.append_empty_data(), + }; + s.append(&self.value); + s.append(&self.input.0); + } + } +} + +/// See +impl Decodable for TransactionLegacyUnsigned { + fn decode(rlp: &rlp::Rlp) -> Result { + Ok(TransactionLegacyUnsigned { + nonce: rlp.val_at(0)?, + gas_price: rlp.val_at(1)?, + gas: rlp.val_at(2)?, + to: { + let to = rlp.at(3)?; + if to.is_empty() { + None + } else { + Some(to.as_val()?) + } + }, + value: rlp.val_at(4)?, + input: Bytes(rlp.val_at(5)?), + chain_id: { + if let Ok(chain_id) = rlp.val_at(6) { + Some(chain_id) + } else { + None + } + }, + ..Default::default() + }) + } +} + +impl Encodable for TransactionLegacySigned { + fn rlp_append(&self, s: &mut rlp::RlpStream) { + s.begin_list(9); + s.append(&self.transaction_legacy_unsigned.nonce); + s.append(&self.transaction_legacy_unsigned.gas_price); + s.append(&self.transaction_legacy_unsigned.gas); + match self.transaction_legacy_unsigned.to { + Some(ref to) => s.append(to), + None => s.append_empty_data(), + }; + s.append(&self.transaction_legacy_unsigned.value); + s.append(&self.transaction_legacy_unsigned.input.0); + + s.append(&self.v); + s.append(&self.r); + s.append(&self.s); + } +} + +/// See +impl Decodable for TransactionLegacySigned { + fn decode(rlp: &rlp::Rlp) -> Result { + let v: U256 = rlp.val_at(6)?; + + let extract_chain_id = |v: U256| { + if v.ge(&35u32.into()) { + Some((v - 35) / 2) + } else { + None + } + }; + + Ok(TransactionLegacySigned { + transaction_legacy_unsigned: { + TransactionLegacyUnsigned { + nonce: rlp.val_at(0)?, + gas_price: rlp.val_at(1)?, + gas: rlp.val_at(2)?, + to: { + let to = rlp.at(3)?; + if to.is_empty() { + None + } else { + Some(to.as_val()?) + } + }, + value: rlp.val_at(4)?, + input: Bytes(rlp.val_at(5)?), + chain_id: extract_chain_id(v).map(|v| v.into()), + r#type: Type0 {}, + } + }, + v, + r: rlp.val_at(7)?, + s: rlp.val_at(8)?, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn encode_decode_legacy_transaction_works() { + let tx = TransactionLegacyUnsigned { + chain_id: Some(596.into()), + gas: U256::from(21000), + nonce: U256::from(1), + gas_price: U256::from("0x640000006a"), + to: Some(Account::from(subxt_signer::eth::dev::baltathar()).address()), + value: U256::from(123123), + input: Bytes(vec![]), + r#type: Type0, + }; + + let rlp_bytes = rlp::encode(&tx); + let decoded = rlp::decode::(&rlp_bytes).unwrap(); + assert_eq!(&tx, &decoded); + + let tx = Account::default().sign_transaction(tx); + let rlp_bytes = rlp::encode(&tx); + let decoded = rlp::decode::(&rlp_bytes).unwrap(); + assert_eq!(&tx, &decoded); + } + + #[test] + fn dummy_signed_payload_works() { + let tx = TransactionLegacyUnsigned { + chain_id: Some(596.into()), + gas: U256::from(21000), + nonce: U256::from(1), + gas_price: U256::from("0x640000006a"), + to: Some(Account::from(subxt_signer::eth::dev::baltathar()).address()), + value: U256::from(123123), + input: Bytes(vec![]), + r#type: Type0, + }; + + let signed_tx = Account::default().sign_transaction(tx.clone()); + let rlp_bytes = rlp::encode(&signed_tx); + assert_eq!(tx.dummy_signed_payload().len(), rlp_bytes.len()); + } + + #[test] + fn recover_address_works() { + let account = Account::default(); + + let unsigned_tx = TransactionLegacyUnsigned { + value: 200_000_000_000_000_000_000u128.into(), + gas_price: 100_000_000_200u64.into(), + gas: 100_107u32.into(), + nonce: 3.into(), + to: Some(Account::from(subxt_signer::eth::dev::baltathar()).address()), + chain_id: Some(596.into()), + ..Default::default() + }; + + let tx = account.sign_transaction(unsigned_tx.clone()); + let recovered_address = tx.recover_eth_address().unwrap(); + + assert_eq!(account.address(), recovered_address); + } +} diff --git a/substrate/frame/revive/src/evm/api/rpc_types.rs b/substrate/frame/revive/src/evm/api/rpc_types.rs new file mode 100644 index 00000000000..b15a0a53cd0 --- /dev/null +++ b/substrate/frame/revive/src/evm/api/rpc_types.rs @@ -0,0 +1,32 @@ +// 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. +//! Utility impl for the RPC types. +use super::{ReceiptInfo, TransactionInfo, TransactionSigned}; + +impl TransactionInfo { + /// Create a new [`TransactionInfo`] from a receipt and a signed transaction. + pub fn new(receipt: ReceiptInfo, transaction_signed: TransactionSigned) -> Self { + Self { + block_hash: receipt.block_hash, + block_number: receipt.block_number, + from: receipt.from, + hash: receipt.transaction_hash, + transaction_index: receipt.transaction_index, + transaction_signed, + } + } +} diff --git a/substrate/frame/revive/src/evm/api/rpc_types_gen.rs b/substrate/frame/revive/src/evm/api/rpc_types_gen.rs new file mode 100644 index 00000000000..e4663a82232 --- /dev/null +++ b/substrate/frame/revive/src/evm/api/rpc_types_gen.rs @@ -0,0 +1,682 @@ +// 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. +//! Generated JSON-RPC types. +#![allow(missing_docs)] + +use super::{byte::*, Type0, Type1, Type2}; +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use derive_more::{From, TryInto}; +pub use ethereum_types::*; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; + +/// Block object +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct Block { + /// Base fee per gas + #[serde(rename = "baseFeePerGas", skip_serializing_if = "Option::is_none")] + pub base_fee_per_gas: Option, + /// Blob gas used + #[serde(rename = "blobGasUsed", skip_serializing_if = "Option::is_none")] + pub blob_gas_used: Option, + /// Difficulty + #[serde(skip_serializing_if = "Option::is_none")] + pub difficulty: Option, + /// Excess blob gas + #[serde(rename = "excessBlobGas", skip_serializing_if = "Option::is_none")] + pub excess_blob_gas: Option, + /// Extra data + #[serde(rename = "extraData")] + pub extra_data: Bytes, + /// Gas limit + #[serde(rename = "gasLimit")] + pub gas_limit: U256, + /// Gas used + #[serde(rename = "gasUsed")] + pub gas_used: U256, + /// Hash + pub hash: H256, + /// Bloom filter + #[serde(rename = "logsBloom")] + pub logs_bloom: Bytes256, + /// Coinbase + pub miner: Address, + /// Mix hash + #[serde(rename = "mixHash")] + pub mix_hash: H256, + /// Nonce + pub nonce: Bytes8, + /// Number + pub number: U256, + /// Parent Beacon Block Root + #[serde(rename = "parentBeaconBlockRoot", skip_serializing_if = "Option::is_none")] + pub parent_beacon_block_root: Option, + /// Parent block hash + #[serde(rename = "parentHash")] + pub parent_hash: H256, + /// Receipts root + #[serde(rename = "receiptsRoot")] + pub receipts_root: H256, + /// Ommers hash + #[serde(rename = "sha3Uncles")] + pub sha_3_uncles: H256, + /// Block size + pub size: U256, + /// State root + #[serde(rename = "stateRoot")] + pub state_root: H256, + /// Timestamp + pub timestamp: U256, + /// Total difficulty + #[serde(rename = "totalDifficulty", skip_serializing_if = "Option::is_none")] + pub total_difficulty: Option, + pub transactions: H256OrTransactionInfo, + /// Transactions root + #[serde(rename = "transactionsRoot")] + pub transactions_root: H256, + /// Uncles + pub uncles: Vec, + /// Withdrawals + #[serde(skip_serializing_if = "Option::is_none")] + pub withdrawals: Option>, + /// Withdrawals root + #[serde(rename = "withdrawalsRoot", skip_serializing_if = "Option::is_none")] + pub withdrawals_root: Option, +} + +/// Block number or tag +#[derive( + Debug, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, From, TryInto, Eq, PartialEq, +)] +#[serde(untagged)] +pub enum BlockNumberOrTag { + /// Block number + U256(U256), + /// Block tag + BlockTag(BlockTag), +} +impl Default for BlockNumberOrTag { + fn default() -> Self { + BlockNumberOrTag::U256(Default::default()) + } +} + +/// Block number, tag, or block hash +#[derive( + Debug, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, From, TryInto, Eq, PartialEq, +)] +#[serde(untagged)] +pub enum BlockNumberOrTagOrHash { + /// Block number + U256(U256), + /// Block tag + BlockTag(BlockTag), + /// Block hash + H256(H256), +} +impl Default for BlockNumberOrTagOrHash { + fn default() -> Self { + BlockNumberOrTagOrHash::U256(Default::default()) + } +} + +/// Transaction object generic to all types +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct GenericTransaction { + /// accessList + /// EIP-2930 access list + #[serde(rename = "accessList", skip_serializing_if = "Option::is_none")] + pub access_list: Option, + /// blobVersionedHashes + /// List of versioned blob hashes associated with the transaction's EIP-4844 data blobs. + #[serde(rename = "blobVersionedHashes", skip_serializing_if = "Option::is_none")] + pub blob_versioned_hashes: Option>, + /// blobs + /// Raw blob data. + #[serde(skip_serializing_if = "Option::is_none")] + pub blobs: Option>, + /// chainId + /// Chain ID that this transaction is valid on. + #[serde(rename = "chainId", skip_serializing_if = "Option::is_none")] + pub chain_id: Option, + /// from address + #[serde(skip_serializing_if = "Option::is_none")] + pub from: Option
, + /// gas limit + #[serde(skip_serializing_if = "Option::is_none")] + pub gas: Option, + /// gas price + /// The gas price willing to be paid by the sender in wei + #[serde(rename = "gasPrice", skip_serializing_if = "Option::is_none")] + pub gas_price: Option, + /// input data + #[serde(alias = "data", skip_serializing_if = "Option::is_none")] + pub input: Option, + /// max fee per blob gas + /// The maximum total fee per gas the sender is willing to pay for blob gas in wei + #[serde(rename = "maxFeePerBlobGas", skip_serializing_if = "Option::is_none")] + pub max_fee_per_blob_gas: Option, + /// max fee per gas + /// The maximum total fee per gas the sender is willing to pay (includes the network / base fee + /// and miner / priority fee) in wei + #[serde(rename = "maxFeePerGas", skip_serializing_if = "Option::is_none")] + pub max_fee_per_gas: Option, + /// max priority fee per gas + /// Maximum fee per gas the sender is willing to pay to miners in wei + #[serde(rename = "maxPriorityFeePerGas", skip_serializing_if = "Option::is_none")] + pub max_priority_fee_per_gas: Option, + /// nonce + #[serde(skip_serializing_if = "Option::is_none")] + pub nonce: Option, + /// to address + pub to: Option
, + /// type + #[serde(skip_serializing_if = "Option::is_none")] + pub r#type: Option, + /// value + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, +} + +/// Receipt information +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct ReceiptInfo { + /// blob gas price + /// The actual value per gas deducted from the sender's account for blob gas. Only specified + /// for blob transactions as defined by EIP-4844. + #[serde(rename = "blobGasPrice", skip_serializing_if = "Option::is_none")] + pub blob_gas_price: Option, + /// blob gas used + /// The amount of blob gas used for this specific transaction. Only specified for blob + /// transactions as defined by EIP-4844. + #[serde(rename = "blobGasUsed", skip_serializing_if = "Option::is_none")] + pub blob_gas_used: Option, + /// block hash + #[serde(rename = "blockHash")] + pub block_hash: H256, + /// block number + #[serde(rename = "blockNumber")] + pub block_number: U256, + /// contract address + /// The contract address created, if the transaction was a contract creation, otherwise null. + #[serde(rename = "contractAddress")] + pub contract_address: Option
, + /// cumulative gas used + /// The sum of gas used by this transaction and all preceding transactions in the same block. + #[serde(rename = "cumulativeGasUsed")] + pub cumulative_gas_used: U256, + /// effective gas price + /// The actual value per gas deducted from the sender's account. Before EIP-1559, this is equal + /// to the transaction's gas price. After, it is equal to baseFeePerGas + min(maxFeePerGas - + /// baseFeePerGas, maxPriorityFeePerGas). + #[serde(rename = "effectiveGasPrice")] + pub effective_gas_price: U256, + /// from + pub from: Address, + /// gas used + /// The amount of gas used for this specific transaction alone. + #[serde(rename = "gasUsed")] + pub gas_used: U256, + /// logs + pub logs: Vec, + /// logs bloom + #[serde(rename = "logsBloom")] + pub logs_bloom: Bytes256, + /// state root + /// The post-transaction state root. Only specified for transactions included before the + /// Byzantium upgrade. + #[serde(skip_serializing_if = "Option::is_none")] + pub root: Option, + /// status + /// Either 1 (success) or 0 (failure). Only specified for transactions included after the + /// Byzantium upgrade. + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + /// to + /// Address of the receiver or null in a contract creation transaction. + pub to: Option
, + /// transaction hash + #[serde(rename = "transactionHash")] + pub transaction_hash: H256, + /// transaction index + #[serde(rename = "transactionIndex")] + pub transaction_index: U256, + /// type + #[serde(skip_serializing_if = "Option::is_none")] + pub r#type: Option, +} + +/// Syncing status +#[derive( + Debug, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, From, TryInto, Eq, PartialEq, +)] +#[serde(untagged)] +pub enum SyncingStatus { + /// Syncing progress + SyncingProgress(SyncingProgress), + /// Not syncing + /// Should always return false if not syncing. + Bool(bool), +} +impl Default for SyncingStatus { + fn default() -> Self { + SyncingStatus::SyncingProgress(Default::default()) + } +} + +/// Transaction information +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct TransactionInfo { + /// block hash + #[serde(rename = "blockHash")] + pub block_hash: H256, + /// block number + #[serde(rename = "blockNumber")] + pub block_number: U256, + /// from address + pub from: Address, + /// transaction hash + pub hash: H256, + /// transaction index + #[serde(rename = "transactionIndex")] + pub transaction_index: U256, + #[serde(flatten)] + pub transaction_signed: TransactionSigned, +} + +#[derive( + Debug, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, From, TryInto, Eq, PartialEq, +)] +#[serde(untagged)] +pub enum TransactionUnsigned { + Transaction4844Unsigned(Transaction4844Unsigned), + Transaction1559Unsigned(Transaction1559Unsigned), + Transaction2930Unsigned(Transaction2930Unsigned), + TransactionLegacyUnsigned(TransactionLegacyUnsigned), +} +impl Default for TransactionUnsigned { + fn default() -> Self { + TransactionUnsigned::Transaction4844Unsigned(Default::default()) + } +} + +/// Access list +pub type AccessList = Vec; + +/// Block tag +/// `earliest`: The lowest numbered block the client has available; `finalized`: The most recent +/// crypto-economically secure block, cannot be re-orged outside of manual intervention driven by +/// community coordination; `safe`: The most recent block that is safe from re-orgs under honest +/// majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical +/// chain observed by the client, this block may be re-orged out of the canonical chain even under +/// healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` +/// and containing the set of transactions usually taken from local mempool. Before the merge +/// transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to +/// with `-39001: Unknown block` error +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub enum BlockTag { + #[serde(rename = "earliest")] + #[default] + Earliest, + #[serde(rename = "finalized")] + Finalized, + #[serde(rename = "safe")] + Safe, + #[serde(rename = "latest")] + Latest, + #[serde(rename = "pending")] + Pending, +} + +#[derive( + Debug, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, From, TryInto, Eq, PartialEq, +)] +#[serde(untagged)] +pub enum H256OrTransactionInfo { + /// Transaction hashes + H256s(Vec), + /// Full transactions + TransactionInfos(Vec), +} +impl Default for H256OrTransactionInfo { + fn default() -> Self { + H256OrTransactionInfo::H256s(Default::default()) + } +} + +/// log +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct Log { + /// address + #[serde(skip_serializing_if = "Option::is_none")] + pub address: Option
, + /// block hash + #[serde(rename = "blockHash", skip_serializing_if = "Option::is_none")] + pub block_hash: Option, + /// block number + #[serde(rename = "blockNumber", skip_serializing_if = "Option::is_none")] + pub block_number: Option, + /// data + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + /// log index + #[serde(rename = "logIndex", skip_serializing_if = "Option::is_none")] + pub log_index: Option, + /// removed + #[serde(skip_serializing_if = "Option::is_none")] + pub removed: Option, + /// topics + #[serde(skip_serializing_if = "Option::is_none")] + pub topics: Option>, + /// transaction hash + #[serde(rename = "transactionHash")] + pub transaction_hash: H256, + /// transaction index + #[serde(rename = "transactionIndex", skip_serializing_if = "Option::is_none")] + pub transaction_index: Option, +} + +/// Syncing progress +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct SyncingProgress { + /// Current block + #[serde(rename = "currentBlock", skip_serializing_if = "Option::is_none")] + pub current_block: Option, + /// Highest block + #[serde(rename = "highestBlock", skip_serializing_if = "Option::is_none")] + pub highest_block: Option, + /// Starting block + #[serde(rename = "startingBlock", skip_serializing_if = "Option::is_none")] + pub starting_block: Option, +} + +/// EIP-1559 transaction. +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct Transaction1559Unsigned { + /// accessList + /// EIP-2930 access list + #[serde(rename = "accessList")] + pub access_list: AccessList, + /// chainId + /// Chain ID that this transaction is valid on. + #[serde(rename = "chainId")] + pub chain_id: U256, + /// gas limit + pub gas: U256, + /// gas price + /// The effective gas price paid by the sender in wei. For transactions not yet included in a + /// block, this value should be set equal to the max fee per gas. This field is DEPRECATED, + /// please transition to using effectiveGasPrice in the receipt object going forward. + #[serde(rename = "gasPrice")] + pub gas_price: U256, + /// input data + pub input: Bytes, + /// max fee per gas + /// The maximum total fee per gas the sender is willing to pay (includes the network / base fee + /// and miner / priority fee) in wei + #[serde(rename = "maxFeePerGas")] + pub max_fee_per_gas: U256, + /// max priority fee per gas + /// Maximum fee per gas the sender is willing to pay to miners in wei + #[serde(rename = "maxPriorityFeePerGas")] + pub max_priority_fee_per_gas: U256, + /// nonce + pub nonce: U256, + /// to address + pub to: Option
, + /// type + pub r#type: Type2, + /// value + pub value: U256, +} + +/// EIP-2930 transaction. +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct Transaction2930Unsigned { + /// accessList + /// EIP-2930 access list + #[serde(rename = "accessList")] + pub access_list: AccessList, + /// chainId + /// Chain ID that this transaction is valid on. + #[serde(rename = "chainId")] + pub chain_id: U256, + /// gas limit + pub gas: U256, + /// gas price + /// The gas price willing to be paid by the sender in wei + #[serde(rename = "gasPrice")] + pub gas_price: U256, + /// input data + pub input: Bytes, + /// nonce + pub nonce: U256, + /// to address + pub to: Option
, + /// type + pub r#type: Type1, + /// value + pub value: U256, +} + +/// EIP-4844 transaction. +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct Transaction4844Unsigned { + /// accessList + /// EIP-2930 access list + #[serde(rename = "accessList")] + pub access_list: AccessList, + /// blobVersionedHashes + /// List of versioned blob hashes associated with the transaction's EIP-4844 data blobs. + #[serde(rename = "blobVersionedHashes")] + pub blob_versioned_hashes: Vec, + /// chainId + /// Chain ID that this transaction is valid on. + #[serde(rename = "chainId")] + pub chain_id: U256, + /// gas limit + pub gas: U256, + /// input data + pub input: Bytes, + /// max fee per blob gas + /// The maximum total fee per gas the sender is willing to pay for blob gas in wei + #[serde(rename = "maxFeePerBlobGas")] + pub max_fee_per_blob_gas: U256, + /// max fee per gas + /// The maximum total fee per gas the sender is willing to pay (includes the network / base fee + /// and miner / priority fee) in wei + #[serde(rename = "maxFeePerGas")] + pub max_fee_per_gas: U256, + /// max priority fee per gas + /// Maximum fee per gas the sender is willing to pay to miners in wei + #[serde(rename = "maxPriorityFeePerGas")] + pub max_priority_fee_per_gas: U256, + /// nonce + pub nonce: U256, + /// to address + pub to: Address, + /// type + pub r#type: Byte, + /// value + pub value: U256, +} + +/// Legacy transaction. +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct TransactionLegacyUnsigned { + /// chainId + /// Chain ID that this transaction is valid on. + #[serde(rename = "chainId", skip_serializing_if = "Option::is_none")] + pub chain_id: Option, + /// gas limit + pub gas: U256, + /// gas price + /// The gas price willing to be paid by the sender in wei + #[serde(rename = "gasPrice")] + pub gas_price: U256, + /// input data + pub input: Bytes, + /// nonce + pub nonce: U256, + /// to address + pub to: Option
, + /// type + pub r#type: Type0, + /// value + pub value: U256, +} + +#[derive( + Debug, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, From, TryInto, Eq, PartialEq, +)] +#[serde(untagged)] +pub enum TransactionSigned { + Transaction4844Signed(Transaction4844Signed), + Transaction1559Signed(Transaction1559Signed), + Transaction2930Signed(Transaction2930Signed), + TransactionLegacySigned(TransactionLegacySigned), +} +impl Default for TransactionSigned { + fn default() -> Self { + TransactionSigned::Transaction4844Signed(Default::default()) + } +} + +/// Validator withdrawal +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct Withdrawal { + /// recipient address for withdrawal value + pub address: Address, + /// value contained in withdrawal + pub amount: U256, + /// index of withdrawal + pub index: U256, + /// index of validator that generated withdrawal + #[serde(rename = "validatorIndex")] + pub validator_index: U256, +} + +/// Access list entry +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct AccessListEntry { + pub address: Address, + #[serde(rename = "storageKeys")] + pub storage_keys: Vec, +} + +/// Signed 1559 Transaction +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct Transaction1559Signed { + #[serde(flatten)] + pub transaction_1559_unsigned: Transaction1559Unsigned, + /// r + pub r: U256, + /// s + pub s: U256, + /// v + /// For backwards compatibility, `v` is optionally provided as an alternative to `yParity`. + /// This field is DEPRECATED and all use of it should migrate to `yParity`. + #[serde(skip_serializing_if = "Option::is_none")] + pub v: Option, + /// yParity + /// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature. + #[serde(rename = "yParity", skip_serializing_if = "Option::is_none")] + pub y_parity: Option, +} + +/// Signed 2930 Transaction +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct Transaction2930Signed { + #[serde(flatten)] + pub transaction_2930_unsigned: Transaction2930Unsigned, + /// r + pub r: U256, + /// s + pub s: U256, + /// v + /// For backwards compatibility, `v` is optionally provided as an alternative to `yParity`. + /// This field is DEPRECATED and all use of it should migrate to `yParity`. + #[serde(skip_serializing_if = "Option::is_none")] + pub v: Option, + /// yParity + /// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature. + #[serde(rename = "yParity")] + pub y_parity: U256, +} + +/// Signed 4844 Transaction +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct Transaction4844Signed { + #[serde(flatten)] + pub transaction_4844_unsigned: Transaction4844Unsigned, + /// r + pub r: U256, + /// s + pub s: U256, + /// yParity + /// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature. + #[serde(rename = "yParity", skip_serializing_if = "Option::is_none")] + pub y_parity: Option, +} + +/// Signed Legacy Transaction +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct TransactionLegacySigned { + #[serde(flatten)] + pub transaction_legacy_unsigned: TransactionLegacyUnsigned, + /// r + pub r: U256, + /// s + pub s: U256, + /// v + pub v: U256, +} diff --git a/substrate/frame/revive/src/evm/api/signature.rs b/substrate/frame/revive/src/evm/api/signature.rs new file mode 100644 index 00000000000..957d50c8e32 --- /dev/null +++ b/substrate/frame/revive/src/evm/api/signature.rs @@ -0,0 +1,80 @@ +// 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. +//! Ethereum signature utilities +use super::{TransactionLegacySigned, TransactionLegacyUnsigned}; +use rlp::Encodable; +use sp_core::{H160, U256}; +use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256}; + +impl TransactionLegacyUnsigned { + /// Recover the Ethereum address, from an RLP encoded transaction and a 65 bytes signature. + pub fn recover_eth_address(rlp_encoded: &[u8], signature: &[u8; 65]) -> Result { + let hash = keccak_256(rlp_encoded); + let mut addr = H160::default(); + let pk = secp256k1_ecdsa_recover(&signature, &hash).map_err(|_| ())?; + addr.assign_from_slice(&keccak_256(&pk[..])[12..]); + + Ok(addr) + } +} + +impl TransactionLegacySigned { + /// Create a signed transaction from an [`TransactionLegacyUnsigned`] and a signature. + pub fn from( + transaction_legacy_unsigned: TransactionLegacyUnsigned, + signature: &[u8; 65], + ) -> TransactionLegacySigned { + let r = U256::from_big_endian(&signature[..32]); + let s = U256::from_big_endian(&signature[32..64]); + let recovery_id = signature[64] as u32; + let v = transaction_legacy_unsigned + .chain_id + .map(|chain_id| chain_id * 2 + 35 + recovery_id) + .unwrap_or_else(|| U256::from(27) + recovery_id); + + TransactionLegacySigned { transaction_legacy_unsigned, r, s, v } + } + + /// Get the raw 65 bytes signature from the signed transaction. + pub fn raw_signature(&self) -> Result<[u8; 65], ()> { + let mut s = [0u8; 65]; + self.r.write_as_big_endian(s[0..32].as_mut()); + self.s.write_as_big_endian(s[32..64].as_mut()); + s[64] = self.extract_recovery_id().ok_or(())?; + Ok(s) + } + + /// Get the recovery ID from the signed transaction. + /// See https://eips.ethereum.org/EIPS/eip-155 + fn extract_recovery_id(&self) -> Option { + if let Some(chain_id) = self.transaction_legacy_unsigned.chain_id { + // self.v - chain_id * 2 - 35 + let v: u64 = self.v.try_into().ok()?; + let chain_id: u64 = chain_id.try_into().ok()?; + let r = v.checked_sub(chain_id.checked_mul(2)?)?.checked_sub(35)?; + r.try_into().ok() + } else { + self.v.try_into().ok() + } + } + + /// Recover the Ethereum address from the signed transaction. + pub fn recover_eth_address(&self) -> Result { + let rlp_encoded = self.transaction_legacy_unsigned.rlp_bytes(); + TransactionLegacyUnsigned::recover_eth_address(&rlp_encoded, &self.raw_signature()?) + } +} diff --git a/substrate/frame/revive/src/evm/api/type_id.rs b/substrate/frame/revive/src/evm/api/type_id.rs new file mode 100644 index 00000000000..7d75d53500b --- /dev/null +++ b/substrate/frame/revive/src/evm/api/type_id.rs @@ -0,0 +1,95 @@ +// 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. +//! Ethereum Typed Transaction types +use super::Byte; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +/// A macro to generate Transaction type identifiers +/// See +macro_rules! transaction_type { + ($name:ident, $value:literal) => { + #[doc = concat!("Transaction type identifier: ", $value)] + #[derive(Clone, Default, Debug, Eq, PartialEq)] + pub struct $name; + + impl $name { + /// Convert to Byte + pub fn as_byte(&self) -> Byte { + Byte::from($value) + } + + /// Try to convert from Byte + pub fn try_from_byte(byte: Byte) -> Result { + if byte.0 == $value { + Ok(Self {}) + } else { + Err(byte) + } + } + } + + impl Encode for $name { + fn using_encoded R>(&self, f: F) -> R { + f(&[$value]) + } + } + impl Decode for $name { + fn decode(input: &mut I) -> Result { + if $value == input.read_byte()? { + Ok(Self {}) + } else { + Err(codec::Error::from(concat!("expected ", $value))) + } + } + } + + impl TypeInfo for $name { + type Identity = u8; + fn type_info() -> scale_info::Type { + ::type_info() + } + } + + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(concat!("0x", $value)) + } + } + impl<'de> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: &str = Deserialize::deserialize(deserializer)?; + if s == concat!("0x", $value) { + Ok($name {}) + } else { + Err(serde::de::Error::custom(concat!("expected ", $value))) + } + } + } + }; +} + +transaction_type!(Type0, 0); +transaction_type!(Type1, 1); +transaction_type!(Type2, 2); diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs new file mode 100644 index 00000000000..58110bcf186 --- /dev/null +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -0,0 +1,685 @@ +// 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. +//! Runtime types for integrating `pallet-revive` with the EVM. +use crate::{ + evm::api::{TransactionLegacySigned, TransactionLegacyUnsigned}, + AccountIdOf, AddressMapper, BalanceOf, MomentOf, Weight, LOG_TARGET, +}; +use codec::{Decode, Encode}; +use frame_support::{ + dispatch::{DispatchInfo, GetDispatchInfo}, + traits::{ExtrinsicCall, InherentBuilder, SignedTransactionBuilder}, +}; +use pallet_transaction_payment::OnChargeTransaction; +use scale_info::TypeInfo; +use sp_arithmetic::Percent; +use sp_core::{Get, U256}; +use sp_runtime::{ + generic::{self, CheckedExtrinsic, ExtrinsicFormat}, + traits::{ + self, Checkable, Dispatchable, ExtrinsicLike, ExtrinsicMetadata, IdentifyAccount, Member, + TransactionExtension, + }, + transaction_validity::{InvalidTransaction, TransactionValidityError}, + OpaqueExtrinsic, RuntimeDebug, Saturating, +}; + +use alloc::vec::Vec; + +type CallOf = ::RuntimeCall; + +/// The EVM gas price. +/// This constant is used by the proxy to advertise it via the eth_gas_price RPC. +/// +/// We use a fixed value for the gas price. +/// This let us calculate the gas estimate for a transaction with the formula: +/// `estimate_gas = substrate_fee / gas_price`. +pub const GAS_PRICE: u32 = 1_000u32; + +/// Wraps [`generic::UncheckedExtrinsic`] to support checking unsigned +/// [`crate::Call::eth_transact`] extrinsic. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(E))] +pub struct UncheckedExtrinsic( + pub generic::UncheckedExtrinsic, Signature, E::Extension>, +); + +impl + From, Signature, E::Extension>> + for UncheckedExtrinsic +{ + fn from( + utx: generic::UncheckedExtrinsic, Signature, E::Extension>, + ) -> Self { + Self(utx) + } +} + +impl ExtrinsicLike + for UncheckedExtrinsic +{ + fn is_bare(&self) -> bool { + ExtrinsicLike::is_bare(&self.0) + } +} + +impl ExtrinsicMetadata + for UncheckedExtrinsic +{ + const VERSION: u8 = + generic::UncheckedExtrinsic::, Signature, E::Extension>::VERSION; + type TransactionExtensions = E::Extension; +} + +impl ExtrinsicCall + for UncheckedExtrinsic +{ + type Call = CallOf; + + fn call(&self) -> &Self::Call { + self.0.call() + } +} + +use sp_runtime::traits::MaybeDisplay; +type OnChargeTransactionBalanceOf = <::OnChargeTransaction as OnChargeTransaction>::Balance; + +impl Checkable + for UncheckedExtrinsic +where + E: EthExtra, + Self: Encode, + ::Nonce: TryFrom, + ::RuntimeCall: Dispatchable, + OnChargeTransactionBalanceOf: Into>, + BalanceOf: Into + TryFrom, + MomentOf: Into, + CallOf: From> + TryInto>, + + // required by Checkable for `generic::UncheckedExtrinsic` + LookupSource: Member + MaybeDisplay, + CallOf: Encode + Member + Dispatchable, + Signature: Member + traits::Verify, + ::Signer: IdentifyAccount>, + E::Extension: Encode + TransactionExtension>, + Lookup: traits::Lookup>, +{ + type Checked = CheckedExtrinsic, CallOf, E::Extension>; + + fn check(self, lookup: &Lookup) -> Result { + if !self.0.is_signed() { + if let Ok(call) = self.0.function.clone().try_into() { + if let crate::Call::eth_transact { payload, gas_limit, storage_deposit_limit } = + call + { + let checked = E::try_into_checked_extrinsic( + payload, + gas_limit, + storage_deposit_limit, + self.encoded_size(), + )?; + return Ok(checked) + }; + } + } + self.0.check(lookup) + } + + #[cfg(feature = "try-runtime")] + fn unchecked_into_checked_i_know_what_i_am_doing( + self, + lookup: &Lookup, + ) -> Result { + self.0.unchecked_into_checked_i_know_what_i_am_doing(lookup) + } +} + +impl GetDispatchInfo for UncheckedExtrinsic +where + CallOf: GetDispatchInfo + Dispatchable, +{ + fn get_dispatch_info(&self) -> DispatchInfo { + self.0.get_dispatch_info() + } +} + +impl serde::Serialize + for UncheckedExtrinsic +{ + fn serialize(&self, seq: S) -> Result + where + S: ::serde::Serializer, + { + self.0.serialize(seq) + } +} + +impl<'a, Address: Decode, Signature: Decode, E: EthExtra> serde::Deserialize<'a> + for UncheckedExtrinsic +{ + fn deserialize(de: D) -> Result + where + D: serde::Deserializer<'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))) + } +} + +impl SignedTransactionBuilder + for UncheckedExtrinsic +where + Address: TypeInfo, + CallOf: TypeInfo, + Signature: TypeInfo, + E::Extension: TypeInfo, +{ + type Address = Address; + type Signature = Signature; + type Extension = E::Extension; + + fn new_signed_transaction( + call: Self::Call, + signed: Address, + signature: Signature, + tx_ext: E::Extension, + ) -> Self { + generic::UncheckedExtrinsic::new_signed(call, signed, signature, tx_ext).into() + } +} + +impl InherentBuilder for UncheckedExtrinsic +where + Address: TypeInfo, + CallOf: TypeInfo, + Signature: TypeInfo, + E::Extension: TypeInfo, +{ + fn new_inherent(call: Self::Call) -> Self { + generic::UncheckedExtrinsic::new_bare(call).into() + } +} + +impl From> + for OpaqueExtrinsic +where + Address: Encode, + Signature: Encode, + CallOf: Encode, + E::Extension: Encode, +{ + fn from(extrinsic: UncheckedExtrinsic) -> Self { + Self::from_bytes(extrinsic.encode().as_slice()).expect( + "both OpaqueExtrinsic and UncheckedExtrinsic have encoding that is compatible with \ + raw Vec encoding; qed", + ) + } +} + +/// EthExtra convert an unsigned [`crate::Call::eth_transact`] into a [`CheckedExtrinsic`]. +pub trait EthExtra { + /// The Runtime configuration. + type Config: crate::Config + pallet_transaction_payment::Config; + + /// The Runtime's transaction extension. + /// It should include at least: + /// - [`frame_system::CheckNonce`] to ensure that the nonce from the Ethereum transaction is + /// correct. + type Extension: TransactionExtension>; + + /// Get the transaction extension to apply to an unsigned [`crate::Call::eth_transact`] + /// extrinsic. + /// + /// # Parameters + /// - `nonce`: The nonce extracted from the Ethereum transaction. + /// - `tip`: The transaction tip calculated from the Ethereum transaction. + fn get_eth_extension( + nonce: ::Nonce, + tip: BalanceOf, + ) -> Self::Extension; + + /// Convert the unsigned [`crate::Call::eth_transact`] into a [`CheckedExtrinsic`]. + /// and ensure that the fees from the Ethereum transaction correspond to the fees computed from + /// the encoded_len, the injected gas_limit and storage_deposit_limit. + /// + /// # Parameters + /// - `payload`: The RLP-encoded Ethereum transaction. + /// - `gas_limit`: The gas limit for the extrinsic + /// - `storage_deposit_limit`: The storage deposit limit for the extrinsic, + /// - `encoded_len`: The encoded length of the extrinsic. + fn try_into_checked_extrinsic( + payload: Vec, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + encoded_len: usize, + ) -> Result< + CheckedExtrinsic, CallOf, Self::Extension>, + InvalidTransaction, + > + where + ::Nonce: TryFrom, + BalanceOf: Into + TryFrom, + MomentOf: Into, + ::RuntimeCall: Dispatchable, + OnChargeTransactionBalanceOf: Into>, + CallOf: From>, + { + let tx = rlp::decode::(&payload).map_err(|err| { + log::debug!(target: LOG_TARGET, "Failed to decode transaction: {err:?}"); + InvalidTransaction::Call + })?; + + let signer = tx.recover_eth_address().map_err(|err| { + log::debug!(target: LOG_TARGET, "Failed to recover signer: {err:?}"); + InvalidTransaction::BadProof + })?; + + let signer = + ::AddressMapper::to_account_id_contract(&signer); + let TransactionLegacyUnsigned { nonce, chain_id, to, value, input, gas, gas_price, .. } = + tx.transaction_legacy_unsigned; + + if chain_id.unwrap_or_default() != ::ChainId::get().into() { + log::debug!(target: LOG_TARGET, "Invalid chain_id {chain_id:?}"); + return Err(InvalidTransaction::Call); + } + + let call = if let Some(dest) = to { + crate::Call::call:: { + dest, + value: value.try_into().map_err(|_| InvalidTransaction::Call)?, + gas_limit, + storage_deposit_limit, + data: input.0, + } + } else { + let blob = match polkavm::ProgramBlob::blob_length(&input.0) { + Some(blob_len) => blob_len + .try_into() + .ok() + .and_then(|blob_len| (input.0.split_at_checked(blob_len))), + _ => None, + }; + + let Some((code, data)) = blob else { + log::debug!(target: LOG_TARGET, "Failed to extract polkavm code & data"); + return Err(InvalidTransaction::Call); + }; + + crate::Call::instantiate_with_code:: { + value: value.try_into().map_err(|_| InvalidTransaction::Call)?, + gas_limit, + storage_deposit_limit, + code: code.to_vec(), + data: data.to_vec(), + salt: None, + } + }; + + let nonce = nonce.try_into().map_err(|_| InvalidTransaction::Call)?; + + // Fees calculated with the fixed `GAS_PRICE` that should be used to estimate the gas. + let eth_fee_no_tip = U256::from(GAS_PRICE) + .saturating_mul(gas) + .try_into() + .map_err(|_| InvalidTransaction::Call)?; + + // Fees with the actual gas_price from the transaction. + let eth_fee: BalanceOf = U256::from(gas_price) + .saturating_mul(gas) + .try_into() + .map_err(|_| InvalidTransaction::Call)?; + + let info = call.get_dispatch_info(); + let function: CallOf = call.into(); + + // Fees calculated from the extrinsic, without the tip. + let actual_fee: BalanceOf = + pallet_transaction_payment::Pallet::::compute_fee( + encoded_len as u32, + &info, + Default::default(), + ) + .into(); + + log::debug!(target: LOG_TARGET, "Checking Ethereum transaction fees: + dispatch_info: {info:?} + encoded_len: {encoded_len:?} + fees: {actual_fee:?} + "); + + if eth_fee < actual_fee { + log::debug!(target: LOG_TARGET, "fees {eth_fee:?} too low for the extrinsic {actual_fee:?}"); + return Err(InvalidTransaction::Payment.into()) + } + + let min = actual_fee.min(eth_fee_no_tip); + 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:?}"); + 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:?}"); + } + + let tip = eth_fee.saturating_sub(eth_fee_no_tip); + log::debug!(target: LOG_TARGET, "Created checked Ethereum transaction with nonce {nonce:?} and tip: {tip:?}"); + Ok(CheckedExtrinsic { + format: ExtrinsicFormat::Signed(signer.into(), Self::get_eth_extension(nonce, tip)), + function, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + evm::*, + test_utils::*, + tests::{ExtBuilder, RuntimeCall, RuntimeOrigin, Test}, + }; + use frame_support::{error::LookupError, traits::fungible::Mutate}; + use pallet_revive_fixtures::compile_module; + use rlp::Encodable; + use sp_runtime::{ + traits::{Checkable, DispatchTransaction}, + MultiAddress, MultiSignature, + }; + type AccountIdOf = ::AccountId; + + /// A simple account that can sign transactions + pub struct Account(subxt_signer::eth::Keypair); + + impl Default for Account { + fn default() -> Self { + Self(subxt_signer::eth::dev::alith()) + } + } + + impl From for Account { + fn from(kp: subxt_signer::eth::Keypair) -> Self { + Self(kp) + } + } + + impl Account { + /// Get the [`AccountId`] of the account. + pub fn account_id(&self) -> AccountIdOf { + let address = self.address(); + ::AddressMapper::to_account_id_contract(&address) + } + + /// Get the [`H160`] address of the account. + pub fn address(&self) -> H160 { + H160::from_slice(&self.0.account_id().as_ref()) + } + + /// Sign a transaction. + pub fn sign_transaction(&self, tx: TransactionLegacyUnsigned) -> TransactionLegacySigned { + let rlp_encoded = tx.rlp_bytes(); + let signature = self.0.sign(&rlp_encoded); + TransactionLegacySigned::from(tx, signature.as_ref()) + } + } + + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct Extra; + type SignedExtra = (frame_system::CheckNonce, ChargeTransactionPayment); + + use pallet_transaction_payment::ChargeTransactionPayment; + impl EthExtra for Extra { + type Config = Test; + type Extension = SignedExtra; + + fn get_eth_extension(nonce: u32, tip: BalanceOf) -> Self::Extension { + (frame_system::CheckNonce::from(nonce), ChargeTransactionPayment::from(tip)) + } + } + + type Ex = UncheckedExtrinsic, MultiSignature, Extra>; + struct TestContext; + + impl traits::Lookup for TestContext { + type Source = MultiAddress; + type Target = AccountIdOf; + fn lookup(&self, s: Self::Source) -> Result { + match s { + MultiAddress::Id(id) => Ok(id), + _ => Err(LookupError), + } + } + } + + /// A builder for creating an unchecked extrinsic, and test that the check function works. + #[derive(Clone)] + struct UncheckedExtrinsicBuilder { + tx: TransactionLegacyUnsigned, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + } + + impl UncheckedExtrinsicBuilder { + /// Create a new builder with default values. + fn new() -> Self { + Self { + tx: TransactionLegacyUnsigned { + chain_id: Some(::ChainId::get().into()), + gas_price: U256::from(GAS_PRICE), + ..Default::default() + }, + gas_limit: Weight::zero(), + storage_deposit_limit: 0, + } + } + + /// Create a new builder with a call to the given address. + fn call_with(dest: H160) -> Self { + let mut builder = Self::new(); + builder.tx.to = Some(dest); + builder.tx.gas = U256::from(516_708u128); + builder + } + + /// Create a new builder with an instantiate call. + fn instantiate_with(code: Vec, data: Vec) -> Self { + let mut builder = Self::new(); + builder.tx.input = Bytes(code.into_iter().chain(data.into_iter()).collect()); + builder.tx.gas = U256::from(1_035_070u128); + builder + } + + /// Update the transaction with the given function. + fn update(mut self, f: impl FnOnce(&mut TransactionLegacyUnsigned) -> ()) -> Self { + f(&mut self.tx); + self + } + + /// Call `check` on the unchecked extrinsic, and `pre_dispatch` on the signed extension. + fn check(&self) -> Result<(RuntimeCall, SignedExtra), TransactionValidityError> { + let UncheckedExtrinsicBuilder { tx, gas_limit, storage_deposit_limit } = self.clone(); + + // Fund the account. + let account = Account::default(); + let _ = ::Currency::set_balance( + &account.account_id(), + 100_000_000_000_000, + ); + + let payload = account.sign_transaction(tx).rlp_bytes().to_vec(); + let call = RuntimeCall::Contracts(crate::Call::eth_transact { + payload, + gas_limit, + storage_deposit_limit, + }); + + let encoded_len = call.encoded_size(); + let uxt: Ex = generic::UncheckedExtrinsic::new_bare(call).into(); + let result: CheckedExtrinsic<_, _, _> = uxt.check(&TestContext {})?; + let (account_id, extra): (AccountId32, SignedExtra) = match result.format { + ExtrinsicFormat::Signed(signer, extra) => (signer, extra), + _ => unreachable!(), + }; + + extra.clone().validate_and_prepare( + RuntimeOrigin::signed(account_id), + &result.function, + &result.function.get_dispatch_info(), + encoded_len, + )?; + + Ok((result.function, extra)) + } + } + + #[test] + fn check_eth_transact_call_works() { + ExtBuilder::default().build().execute_with(|| { + let builder = UncheckedExtrinsicBuilder::call_with(H160::from([1u8; 20])); + assert_eq!( + builder.check().unwrap().0, + crate::Call::call:: { + dest: builder.tx.to.unwrap(), + value: builder.tx.value.as_u64(), + gas_limit: builder.gas_limit, + storage_deposit_limit: builder.storage_deposit_limit, + data: builder.tx.input.0 + } + .into() + ); + }); + } + + #[test] + fn check_eth_transact_instantiate_works() { + ExtBuilder::default().build().execute_with(|| { + let (code, _) = compile_module("dummy").unwrap(); + let data = vec![]; + let builder = UncheckedExtrinsicBuilder::instantiate_with(code.clone(), data.clone()); + + assert_eq!( + builder.check().unwrap().0, + crate::Call::instantiate_with_code:: { + value: builder.tx.value.as_u64(), + gas_limit: builder.gas_limit, + storage_deposit_limit: builder.storage_deposit_limit, + code, + data, + salt: None + } + .into() + ); + }); + } + + #[test] + fn check_eth_transact_nonce_works() { + ExtBuilder::default().build().execute_with(|| { + let builder = UncheckedExtrinsicBuilder::call_with(H160::from([1u8; 20])) + .update(|tx| tx.nonce = 1u32.into()); + + assert_eq!( + builder.check(), + Err(TransactionValidityError::Invalid(InvalidTransaction::Future)) + ); + + >::inc_account_nonce(Account::default().account_id()); + + let builder = UncheckedExtrinsicBuilder::call_with(H160::from([1u8; 20])); + assert_eq!( + builder.check(), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)) + ); + }); + } + + #[test] + fn check_eth_transact_chain_id_works() { + ExtBuilder::default().build().execute_with(|| { + let builder = UncheckedExtrinsicBuilder::call_with(H160::from([1u8; 20])) + .update(|tx| tx.chain_id = Some(42.into())); + + assert_eq!( + builder.check(), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + ); + }); + } + + #[test] + fn check_instantiate_data() { + ExtBuilder::default().build().execute_with(|| { + let code = b"invalid code".to_vec(); + let data = vec![1]; + let builder = UncheckedExtrinsicBuilder::instantiate_with(code.clone(), data.clone()); + + // Fail because the tx input fail to get the blob length + assert_eq!( + builder.clone().update(|tx| tx.input = Bytes(vec![1, 2, 3])).check(), + Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) + ); + }); + } + + #[test] + fn check_transaction_fees() { + ExtBuilder::default().build().execute_with(|| { + let scenarios: [(_, Box, _); 5] = [ + ("Eth fees too low", Box::new(|tx| tx.gas_price /= 2), InvalidTransaction::Payment), + ("Gas fees too high", Box::new(|tx| tx.gas *= 2), InvalidTransaction::Call), + ("Gas fees too low", Box::new(|tx| tx.gas *= 2), InvalidTransaction::Call), + ( + "Diff > 10%", + Box::new(|tx| tx.gas = tx.gas * 111 / 100), + InvalidTransaction::Call, + ), + ( + "Diff < 10%", + Box::new(|tx| { + tx.gas_price *= 2; + tx.gas = tx.gas * 89 / 100 + }), + InvalidTransaction::Call, + ), + ]; + + for (msg, update_tx, err) in scenarios { + let builder = + UncheckedExtrinsicBuilder::call_with(H160::from([1u8; 20])).update(update_tx); + + assert_eq!(builder.check(), Err(TransactionValidityError::Invalid(err)), "{}", msg); + } + }); + } + + #[test] + fn check_transaction_tip() { + ExtBuilder::default().build().execute_with(|| { + let (code, _) = compile_module("dummy").unwrap(); + let data = vec![]; + let builder = UncheckedExtrinsicBuilder::instantiate_with(code.clone(), data.clone()) + .update(|tx| tx.gas_price = tx.gas_price * 103 / 100); + + let tx = &builder.tx; + let expected_tip = tx.gas_price * tx.gas - U256::from(GAS_PRICE) * tx.gas; + let (_, extra) = builder.check().unwrap(); + assert_eq!(U256::from(extra.1.tip()), expected_tip); + }); + } +} diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 07dbd096339..759fba9f1c6 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -841,6 +841,7 @@ where storage_meter, BalanceOf::::zero(), false, + true, )? else { return Ok(None); @@ -874,6 +875,7 @@ where storage_meter: &mut storage::meter::GenericMeter, deposit_limit: BalanceOf, read_only: bool, + origin_is_caller: bool, ) -> Result, E)>, ExecError> { let (account_id, contract_info, executable, delegate_caller, entry_point) = match frame_args { @@ -905,7 +907,17 @@ where let address = if let Some(salt) = salt { address::create2(&deployer, executable.code(), input_data, salt) } else { - address::create1(&deployer, account_nonce.saturated_into()) + use sp_runtime::Saturating; + address::create1( + &deployer, + // the Nonce from the origin has been incremented pre-dispatch, so we need + // to subtract 1 to get the nonce at the time of the call. + if origin_is_caller { + account_nonce.saturating_sub(1u32.into()).saturated_into() + } else { + account_nonce.saturated_into() + }, + ) }; let contract = ContractInfo::new( &address, @@ -976,6 +988,7 @@ where nested_storage, deposit_limit, read_only, + false, )? { self.frames.try_push(frame).map_err(|_| Error::::MaxCallDepthReached)?; Ok(Some(executable)) diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 9986da472c9..9b0bbb2d6bc 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -26,25 +26,23 @@ mod benchmarking; mod benchmarking_dummy; mod exec; mod gas; -mod primitives; -use crate::exec::MomentOf; -use frame_support::traits::IsType; -pub use primitives::*; -use sp_core::U256; - mod limits; +mod primitives; mod storage; mod transient_storage; mod wasm; +#[cfg(test)] +mod tests; + pub mod chain_extension; pub mod debug; +pub mod evm; pub mod test_utils; pub mod weights; -#[cfg(test)] -mod tests; use crate::{ + evm::{runtime::GAS_PRICE, TransactionLegacyUnsigned}, exec::{AccountIdOf, ExecError, Executable, Ext, Key, Origin, Stack as ExecStack}, gas::GasMeter, storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, @@ -58,9 +56,10 @@ use frame_support::{ PostDispatchInfo, RawOrigin, }, ensure, + pallet_prelude::DispatchClass, traits::{ fungible::{Inspect, Mutate, MutateHold}, - ConstU32, ConstU64, Contains, EnsureOrigin, Get, Time, + ConstU32, ConstU64, Contains, EnsureOrigin, Get, IsType, OriginTrait, Time, }, weights::{Weight, WeightMeter}, BoundedVec, RuntimeDebugNoBound, @@ -70,18 +69,21 @@ use frame_system::{ pallet_prelude::{BlockNumberFor, OriginFor}, EventRecord, Pallet as System, }; +use pallet_transaction_payment::OnChargeTransaction; use scale_info::TypeInfo; -use sp_core::{H160, H256}; +use sp_core::{H160, H256, U256}; use sp_runtime::{ traits::{BadOrigin, Convert, Dispatchable, Saturating}, DispatchError, }; pub use crate::{ - address::{AddressMapper, DefaultAddressMapper}, + address::{create1, create2, AddressMapper, DefaultAddressMapper}, debug::Tracing, + exec::MomentOf, pallet::*, }; +pub use primitives::*; pub use weights::WeightInfo; #[cfg(doc)] @@ -90,6 +92,7 @@ pub use crate::wasm::SyscallDoc; type TrieId = BoundedVec>; type BalanceOf = <::Currency as Inspect<::AccountId>>::Balance; +type OnChargeTransactionBalanceOf = <::OnChargeTransaction as OnChargeTransaction>::Balance; type CodeVec = BoundedVec>; type EventRecordOf = EventRecord<::RuntimeEvent, ::Hash>; @@ -134,7 +137,7 @@ pub mod pallet { use sp_runtime::Perbill; /// The in-code storage version. - pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -160,6 +163,7 @@ pub mod pallet { type RuntimeCall: Dispatchable + GetDispatchInfo + codec::Decode + + core::fmt::Debug + IsType<::RuntimeCall>; /// Overarching hold reason. @@ -738,6 +742,33 @@ pub mod pallet { BalanceOf: Into + TryFrom, MomentOf: Into, { + /// A raw EVM transaction, typically dispatched by an Ethereum JSON-RPC server. + /// + /// # Parameters + /// + /// * `payload`: The RLP-encoded [`crate::evm::TransactionLegacySigned`]. + /// * `gas_limit`: The gas limit enforced during contract execution. + /// * `storage_deposit_limit`: The maximum balance that can be charged to the caller for + /// storage usage. + /// + /// # Note + /// + /// This call cannot be dispatched directly; attempting to do so will result in a failed + /// transaction. It serves as a wrapper for an Ethereum transaction. When submitted, the + /// runtime converts it into a [`sp_runtime::generic::CheckedExtrinsic`] by recovering the + /// signer and validating the transaction. + #[allow(unused_variables)] + #[pallet::call_index(0)] + #[pallet::weight(Weight::MAX)] + pub fn eth_transact( + origin: OriginFor, + payload: Vec, + gas_limit: Weight, + #[pallet::compact] storage_deposit_limit: BalanceOf, + ) -> DispatchResultWithPostInfo { + Err(frame_system::Error::CallFiltered::.into()) + } + /// Makes a call to an account, optionally transferring some balance. /// /// # Parameters @@ -754,7 +785,7 @@ pub mod pallet { /// * If the account is a regular account, any value will be transferred. /// * If no account exists and the call value is not less than `existential_deposit`, /// a regular account will be created and any value will be transferred. - #[pallet::call_index(0)] + #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))] pub fn call( origin: OriginFor, @@ -764,6 +795,7 @@ pub mod pallet { #[pallet::compact] storage_deposit_limit: BalanceOf, data: Vec, ) -> DispatchResultWithPostInfo { + log::info!(target: LOG_TARGET, "Call: {:?} {:?} {:?}", dest, value, data); let mut output = Self::bare_call( origin, dest, @@ -787,7 +819,7 @@ pub mod pallet { /// This function is identical to [`Self::instantiate_with_code`] but without the /// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary /// must be supplied. - #[pallet::call_index(1)] + #[pallet::call_index(2)] #[pallet::weight( T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit) )] @@ -851,7 +883,7 @@ pub mod pallet { /// - The smart-contract account is created at the computed address. /// - The `value` is transferred to the new account. /// - The `deploy` function is executed in the context of the newly-created account. - #[pallet::call_index(2)] + #[pallet::call_index(3)] #[pallet::weight( T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32) .saturating_add(*gas_limit) @@ -902,7 +934,7 @@ pub mod pallet { /// To avoid this situation a constructor could employ access control so that it can /// only be instantiated by permissioned entities. The same is true when uploading /// through [`Self::instantiate_with_code`]. - #[pallet::call_index(3)] + #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))] pub fn upload_code( origin: OriginFor, @@ -916,7 +948,7 @@ pub mod pallet { /// /// A code can only be removed by its original uploader (its owner) and only if it is /// not used by any contract. - #[pallet::call_index(4)] + #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::remove_code())] pub fn remove_code( origin: OriginFor, @@ -938,7 +970,7 @@ pub mod pallet { /// This does **not** change the address of the contract in question. This means /// that the contract address is no longer derived from its code hash after calling /// this dispatchable. - #[pallet::call_index(5)] + #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::set_code())] pub fn set_code( origin: OriginFor, @@ -1002,7 +1034,7 @@ where data: Vec, debug: DebugInfo, collect_events: CollectEvents, - ) -> ContractExecResult, EventRecordOf> { + ) -> ContractResult, EventRecordOf> { let mut gas_meter = GasMeter::new(gas_limit); let mut storage_deposit = Default::default(); let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) { @@ -1031,7 +1063,7 @@ where } else { None }; - ContractExecResult { + ContractResult { result: result.map_err(|r| r.error), gas_consumed: gas_meter.gas_consumed(), gas_required: gas_meter.gas_required(), @@ -1057,7 +1089,7 @@ where salt: Option<[u8; 32]>, debug: DebugInfo, collect_events: CollectEvents, - ) -> ContractInstantiateResult, EventRecordOf> { + ) -> ContractResult, EventRecordOf> { let mut gas_meter = GasMeter::new(gas_limit); let mut storage_deposit = Default::default(); let mut debug_message = @@ -1099,7 +1131,7 @@ where } else { None }; - ContractInstantiateResult { + ContractResult { result: output .map(|(addr, result)| InstantiateReturnValue { result, addr }) .map_err(|e| e.error), @@ -1111,6 +1143,184 @@ where } } + /// A version of [`Self::eth_transact`] used to dry-run Ethereum calls. + /// + /// # Parameters + /// + /// - `origin`: The origin of the call. + /// - `dest`: The destination address of the call. + /// - `value`: The value to transfer. + /// - `input`: The input data. + /// - `gas_limit`: The gas limit enforced during contract execution. + /// - `storage_deposit_limit`: The maximum balance that can be charged to the caller for storage + /// usage. + /// - `utx_encoded_size`: A function that takes a call and returns the encoded size of the + /// unchecked extrinsic. + /// - `debug`: Debugging configuration. + /// - `collect_events`: Event collection configuration. + pub fn bare_eth_transact( + origin: T::AccountId, + dest: Option, + value: BalanceOf, + input: Vec, + gas_limit: Weight, + storage_deposit_limit: BalanceOf, + utx_encoded_size: impl Fn(Call) -> u32, + debug: DebugInfo, + collect_events: CollectEvents, + ) -> EthContractResult> + where + T: pallet_transaction_payment::Config, + ::RuntimeCall: + Dispatchable, + ::RuntimeCall: From>, + ::RuntimeCall: Encode, + OnChargeTransactionBalanceOf: Into>, + T::Nonce: Into, + { + // Get the nonce to encode in the tx. + let nonce: T::Nonce = >::account_nonce(&origin); + + // Use a big enough gas price to ensure that the encoded size is large enough. + let max_gas_fee: BalanceOf = + (pallet_transaction_payment::Pallet::::weight_to_fee(Weight::MAX) / + GAS_PRICE.into()) + .into(); + + // A contract call. + if let Some(dest) = dest { + // Dry run the call. + let result = crate::Pallet::::bare_call( + T::RuntimeOrigin::signed(origin), + dest, + value, + gas_limit, + storage_deposit_limit, + input.clone(), + debug, + collect_events, + ); + + // Get the encoded size of the transaction. + let tx = TransactionLegacyUnsigned { + value: value.into(), + input: input.into(), + nonce: nonce.into(), + chain_id: Some(T::ChainId::get().into()), + gas_price: GAS_PRICE.into(), + gas: max_gas_fee.into(), + to: Some(dest), + ..Default::default() + }; + let eth_dispatch_call = crate::Call::::eth_transact { + payload: tx.dummy_signed_payload(), + gas_limit: result.gas_required, + storage_deposit_limit: result.storage_deposit.charge_or_zero(), + }; + let encoded_len = utx_encoded_size(eth_dispatch_call); + + // Get the dispatch info of the call. + let dispatch_call: ::RuntimeCall = crate::Call::::call { + dest, + value, + gas_limit: result.gas_required, + storage_deposit_limit: result.storage_deposit.charge_or_zero(), + data: tx.input.0, + } + .into(); + let dispatch_info = dispatch_call.get_dispatch_info(); + + // Compute the fee. + let fee = pallet_transaction_payment::Pallet::::compute_fee( + encoded_len, + &dispatch_info, + 0u32.into(), + ) + .into(); + + log::debug!(target: LOG_TARGET, "Call dry run Result: dispatch_info: {dispatch_info:?} len: {encoded_len:?} fee: {fee:?}"); + EthContractResult { + gas_required: result.gas_required, + storage_deposit: result.storage_deposit.charge_or_zero(), + result: result.result.map(|v| v.data), + fee, + } + // A contract deployment + } else { + // Extract code and data from the input. + let (code, data) = match polkavm::ProgramBlob::blob_length(&input) { + Some(blob_len) => blob_len + .try_into() + .ok() + .and_then(|blob_len| (input.split_at_checked(blob_len))) + .unwrap_or_else(|| (&input[..], &[][..])), + _ => { + log::debug!(target: LOG_TARGET, "Failed to extract polkavm blob length"); + (&input[..], &[][..]) + }, + }; + + // Dry run the call. + let result = crate::Pallet::::bare_instantiate( + T::RuntimeOrigin::signed(origin), + value, + gas_limit, + storage_deposit_limit, + Code::Upload(code.to_vec()), + data.to_vec(), + None, + debug, + collect_events, + ); + + // Get the encoded size of the transaction. + let tx = TransactionLegacyUnsigned { + gas: max_gas_fee.into(), + nonce: nonce.into(), + value: value.into(), + input: input.clone().into(), + gas_price: GAS_PRICE.into(), + chain_id: Some(T::ChainId::get().into()), + ..Default::default() + }; + let eth_dispatch_call = crate::Call::::eth_transact { + payload: tx.dummy_signed_payload(), + gas_limit: result.gas_required, + storage_deposit_limit: result.storage_deposit.charge_or_zero(), + }; + let encoded_len = utx_encoded_size(eth_dispatch_call); + + // Get the dispatch info of the call. + let dispatch_call: ::RuntimeCall = + crate::Call::::instantiate_with_code { + value, + gas_limit: result.gas_required, + storage_deposit_limit: result.storage_deposit.charge_or_zero(), + code: code.to_vec(), + data: data.to_vec(), + salt: None, + } + .into(); + let dispatch_info = dispatch_call.get_dispatch_info(); + + // Compute the fee. + let fee = pallet_transaction_payment::Pallet::::compute_fee( + encoded_len, + &dispatch_info, + 0u32.into(), + ) + .into(); + + log::debug!(target: LOG_TARGET, "Call dry run Result: dispatch_info: {dispatch_info:?} len: {encoded_len:?} fee: {fee:?}"); + EthContractResult { + gas_required: result.gas_required, + storage_deposit: result.storage_deposit.charge_or_zero(), + result: result.result.map(|v| v.result.data), + fee, + } + } + } + /// A generalized version of [`Self::upload_code`]. /// /// It is identical to [`Self::upload_code`] and only differs in the information it returns. @@ -1199,7 +1409,7 @@ sp_api::decl_runtime_apis! { gas_limit: Option, storage_deposit_limit: Option, input_data: Vec, - ) -> ContractExecResult; + ) -> ContractResult; /// Instantiate a new contract. /// @@ -1212,7 +1422,20 @@ sp_api::decl_runtime_apis! { code: Code, data: Vec, salt: Option<[u8; 32]>, - ) -> ContractInstantiateResult; + ) -> ContractResult; + + + /// Perform an Ethereum call. + /// + /// See [`crate::Pallet::bare_eth_transact`] + fn eth_transact( + origin: H160, + dest: Option, + value: Balance, + input: Vec, + gas_limit: Option, + storage_deposit_limit: Option, + ) -> EthContractResult; /// Upload new code without instantiating a contract from it. /// diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs index 67bc144c3dd..af0100d59cb 100644 --- a/substrate/frame/revive/src/primitives.rs +++ b/substrate/frame/revive/src/primitives.rs @@ -76,19 +76,24 @@ pub struct ContractResult { /// RPC calls. pub debug_message: Vec, /// The execution result of the wasm code. - pub result: R, + pub result: Result, /// The events that were emitted during execution. It is an option as event collection is /// optional. pub events: Option>, } -/// Result type of a `bare_call` call as well as `ContractsApi::call`. -pub type ContractExecResult = - ContractResult, Balance, EventRecord>; - -/// Result type of a `bare_instantiate` call as well as `ContractsApi::instantiate`. -pub type ContractInstantiateResult = - ContractResult, Balance, EventRecord>; +/// The result of the execution of a `eth_transact` call. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct EthContractResult { + /// The fee charged for the execution. + pub fee: Balance, + /// The amount of gas that was necessary to execute the transaction. + pub gas_required: Weight, + /// Storage deposit charged. + pub storage_deposit: Balance, + /// The execution result. + pub result: Result, DispatchError>, +} /// Result type of a `bare_code_upload` call. pub type CodeUploadResult = Result, DispatchError>; diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs index d361590df95..e64f5889443 100644 --- a/substrate/frame/revive/src/test_utils/builder.rs +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -17,9 +17,8 @@ use super::{deposit_limit, GAS_LIMIT}; use crate::{ - address::AddressMapper, AccountIdOf, BalanceOf, Code, CollectEvents, Config, - ContractExecResult, ContractInstantiateResult, DebugInfo, EventRecordOf, ExecReturnValue, - InstantiateReturnValue, OriginFor, Pallet, Weight, + address::AddressMapper, AccountIdOf, BalanceOf, Code, CollectEvents, Config, ContractResult, + DebugInfo, EventRecordOf, ExecReturnValue, InstantiateReturnValue, OriginFor, Pallet, Weight, }; use frame_support::pallet_prelude::DispatchResultWithPostInfo; use paste::paste; @@ -140,7 +139,7 @@ builder!( salt: Option<[u8; 32]>, debug: DebugInfo, collect_events: CollectEvents, - ) -> ContractInstantiateResult, EventRecordOf>; + ) -> ContractResult, EventRecordOf>; /// Build the instantiate call and unwrap the result. pub fn build_and_unwrap_result(self) -> InstantiateReturnValue { @@ -203,7 +202,7 @@ builder!( data: Vec, debug: DebugInfo, collect_events: CollectEvents, - ) -> ContractExecResult, EventRecordOf>; + ) -> ContractResult, EventRecordOf>; /// Build the call and unwrap the result. pub fn build_and_unwrap_result(self) -> ExecReturnValue { diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index e637c5f991c..94af7dbd04d 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -58,16 +58,17 @@ use frame_support::{ tokens::Preservation, ConstU32, ConstU64, Contains, OnIdle, OnInitialize, StorageVersion, }, - weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight, WeightMeter}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, FixedFee, IdentityFee, Weight, WeightMeter}, }; use frame_system::{EventRecord, Phase}; use pallet_revive_fixtures::{bench::dummy_unique, compile_module}; use pallet_revive_uapi::ReturnErrorCode as RuntimeReturnCode; +use pallet_transaction_payment::{ConstFeeMultiplier, Multiplier}; use sp_io::hashing::blake2_256; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ testing::H256, - traits::{BlakeTwo256, Convert, IdentityLookup}, + traits::{BlakeTwo256, Convert, IdentityLookup, One}, AccountId32, BuildStorage, DispatchError, Perbill, TokenError, }; @@ -82,6 +83,7 @@ frame_support::construct_runtime!( Utility: pallet_utility, Contracts: pallet_revive, Proxy: pallet_proxy, + TransactionPayment: pallet_transaction_payment, Dummy: pallet_dummy } ); @@ -415,6 +417,18 @@ impl pallet_proxy::Config for Test { type AnnouncementDepositFactor = ConstU64<1>; } +parameter_types! { + pub FeeMultiplier: Multiplier = Multiplier::one(); +} + +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] +impl pallet_transaction_payment::Config for Test { + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + type WeightToFee = IdentityFee<::Balance>; + type LengthToFee = FixedFee<100, ::Balance>; + type FeeMultiplierUpdate = ConstFeeMultiplier; +} + impl pallet_dummy::Config for Test {} parameter_types! { @@ -509,6 +523,17 @@ impl Config for Test { type ChainId = ChainId; } +impl TryFrom for crate::Call { + type Error = (); + + fn try_from(value: RuntimeCall) -> Result { + match value { + RuntimeCall::Contracts(call) => Ok(call), + _ => Err(()), + } + } +} + pub struct ExtBuilder { existential_deposit: u64, storage_version: Option, @@ -727,15 +752,16 @@ mod run_tests { )); assert_eq!(System::account_nonce(&ALICE), 0); + System::inc_account_nonce(&ALICE); - for nonce in 0..3 { + for nonce in 1..3 { let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash)) .salt(None) .build_and_unwrap_contract(); assert!(ContractInfoOf::::contains_key(&addr)); assert_eq!( addr, - create1(&::AddressMapper::to_address(&ALICE), nonce) + create1(&::AddressMapper::to_address(&ALICE), nonce - 1) ); } assert_eq!(System::account_nonce(&ALICE), 3); @@ -747,7 +773,7 @@ mod run_tests { assert!(ContractInfoOf::::contains_key(&addr)); assert_eq!( addr, - create1(&::AddressMapper::to_address(&ALICE), nonce) + create1(&::AddressMapper::to_address(&ALICE), nonce - 1) ); } assert_eq!(System::account_nonce(&ALICE), 6); diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs index e2256d7dcea..2b802290384 100644 --- a/substrate/frame/revive/src/wasm/mod.rs +++ b/substrate/frame/revive/src/wasm/mod.rs @@ -200,7 +200,10 @@ where &self.code_info.owner, deposit, ) - .map_err(|_| >::StorageDepositNotEnoughFunds)?; + .map_err(|err| { + log::debug!(target: LOG_TARGET, "failed to store code for owner: {:?}: {err:?}", self.code_info.owner); + >::StorageDepositNotEnoughFunds + })?; self.code_info.refcount = 0; >::insert(code_hash, &self.code); diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 78c8b192965..00be26aeaf8 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -44,11 +44,6 @@ type CallOf = ::RuntimeCall; /// The maximum nesting depth a contract can use when encoding types. const MAX_DECODE_NESTING: u32 = 256; -/// Encode a `U256` into a 32 byte buffer. -fn as_bytes(u: U256) -> [u8; 32] { - u.to_little_endian() -} - #[derive(Clone, Copy)] pub enum ApiVersion { /// Expose all APIs even unversioned ones. Only used for testing and benchmarking. @@ -1545,7 +1540,7 @@ pub mod env { Ok(self.write_fixed_sandbox_output( memory, out_ptr, - &as_bytes(self.ext.balance()), + &self.ext.balance().to_little_endian(), false, already_charged, )?) @@ -1566,7 +1561,7 @@ pub mod env { Ok(self.write_fixed_sandbox_output( memory, out_ptr, - &as_bytes(self.ext.balance_of(&address)), + &self.ext.balance_of(&address).to_little_endian(), false, already_charged, )?) @@ -1579,7 +1574,7 @@ pub mod env { Ok(self.write_fixed_sandbox_output( memory, out_ptr, - &as_bytes(U256::from(::ChainId::get())), + &U256::from(::ChainId::get()).to_little_endian(), false, |_| Some(RuntimeCosts::CopyToContract(32)), )?) @@ -1593,7 +1588,7 @@ pub mod env { Ok(self.write_fixed_sandbox_output( memory, out_ptr, - &as_bytes(self.ext.value_transferred()), + &self.ext.value_transferred().to_little_endian(), false, already_charged, )?) @@ -1607,7 +1602,7 @@ pub mod env { Ok(self.write_fixed_sandbox_output( memory, out_ptr, - &as_bytes(self.ext.now()), + &self.ext.now().to_little_endian(), false, already_charged, )?) @@ -1621,7 +1616,7 @@ pub mod env { Ok(self.write_fixed_sandbox_output( memory, out_ptr, - &as_bytes(self.ext.minimum_balance()), + &self.ext.minimum_balance().to_little_endian(), false, already_charged, )?) @@ -1675,7 +1670,7 @@ pub mod env { Ok(self.write_fixed_sandbox_output( memory, out_ptr, - &as_bytes(self.ext.block_number()), + &self.ext.block_number().to_little_endian(), false, already_charged, )?) @@ -2033,7 +2028,7 @@ pub mod env { Ok(self.write_fixed_sandbox_output( memory, out_ptr, - &as_bytes(U256::from(self.ext.last_frame_output().data.len())), + &U256::from(self.ext.last_frame_output().data.len()).to_little_endian(), false, |len| Some(RuntimeCosts::CopyToContract(len)), )?) diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml index 8705781db00..9eaa1b68ca8 100644 --- a/substrate/frame/revive/uapi/Cargo.toml +++ b/substrate/frame/revive/uapi/Cargo.toml @@ -21,7 +21,7 @@ codec = { features = [ ], optional = true, workspace = true } [target.'cfg(target_arch = "riscv32")'.dependencies] -polkavm-derive = { version = "0.12.0" } +polkavm-derive = { version = "0.13.0" } [package.metadata.docs.rs] default-target = ["wasm32-unknown-unknown"] diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 7147a11fb9c..370673622b9 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -442,6 +442,7 @@ try-runtime = [ "pallet-recovery?/try-runtime", "pallet-referenda?/try-runtime", "pallet-remark?/try-runtime", + "pallet-revive-mock-network?/try-runtime", "pallet-revive?/try-runtime", "pallet-root-offences?/try-runtime", "pallet-root-testing?/try-runtime", @@ -497,7 +498,6 @@ serde = [ "pallet-parameters?/serde", "pallet-referenda?/serde", "pallet-remark?/serde", - "pallet-revive?/serde", "pallet-state-trie-migration?/serde", "pallet-tips?/serde", "pallet-transaction-payment?/serde", -- GitLab From aeebf2f383390f2f86527d70212162d5dbea8b93 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Tue, 22 Oct 2024 18:02:01 +0200 Subject: [PATCH 29/50] [pallet-revive] fix fixture build path (#6174) Co-authored-by: GitHub Action Co-authored-by: Cyrill Leutwiler --- prdoc/pr_6174.prdoc | 9 ++++++++ substrate/frame/revive/fixtures/build.rs | 25 ++++++++++++++-------- substrate/frame/revive/fixtures/src/lib.rs | 23 ++++++++++---------- substrate/frame/revive/src/evm/runtime.rs | 1 + 4 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 prdoc/pr_6174.prdoc diff --git a/prdoc/pr_6174.prdoc b/prdoc/pr_6174.prdoc new file mode 100644 index 00000000000..8aa1c25012b --- /dev/null +++ b/prdoc/pr_6174.prdoc @@ -0,0 +1,9 @@ +title: '[pallet-revive] fix fixture build path' +doc: +- audience: Runtime Dev + description: "Fix fixture build path" +crates: +- name: pallet-revive-fixtures + bump: patch +- name: pallet-revive + bump: patch diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index cb4b7640814..ee7db4203cc 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -182,18 +182,14 @@ mod build { pub fn run() -> Result<()> { let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); let contracts_dir = fixtures_dir.join("contracts"); - let uapi_dir = fixtures_dir.parent().expect("uapi dir exits; qed").join("uapi"); - let ws_dir: PathBuf = env::var("CARGO_WORKSPACE_ROOT_DIR")?.into(); - let out_dir: PathBuf = ws_dir.join("target").join("pallet-revive-fixtures"); - - // create out_dir if it does not exist - if !out_dir.exists() { - fs::create_dir_all(&out_dir)?; - } + let out_dir: PathBuf = env::var("OUT_DIR")?.into(); // the fixtures have a dependency on the uapi crate println!("cargo::rerun-if-changed={}", fixtures_dir.display()); - println!("cargo::rerun-if-changed={}", uapi_dir.display()); + let uapi_dir = fixtures_dir.parent().expect("parent dir exits; qed").join("uapi"); + if uapi_dir.exists() { + println!("cargo::rerun-if-changed={}", uapi_dir.display()); + } let entries = collect_entries(&contracts_dir); if entries.is_empty() { @@ -207,6 +203,17 @@ mod build { invoke_build(tmp_dir_path)?; write_output(tmp_dir_path, &out_dir, entries)?; + + #[cfg(unix)] + if let Ok(symlink_dir) = env::var("CARGO_WORKSPACE_ROOT_DIR") { + let symlink_dir: PathBuf = symlink_dir.into(); + let symlink_dir: PathBuf = symlink_dir.join("target").join("pallet-revive-fixtures"); + if symlink_dir.is_symlink() { + fs::remove_file(&symlink_dir)? + } + std::os::unix::fs::symlink(&out_dir, &symlink_dir)?; + } + Ok(()) } } diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs index eacd63b97e5..5548dca66d0 100644 --- a/substrate/frame/revive/fixtures/src/lib.rs +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -22,11 +22,8 @@ extern crate alloc; /// Load a given wasm module and returns a wasm binary contents along with it's hash. #[cfg(feature = "std")] pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, sp_core::H256)> { - let ws_dir: std::path::PathBuf = env!("CARGO_WORKSPACE_ROOT_DIR").into(); - let fixture_path = ws_dir - .join("target") - .join("pallet-revive-fixtures") - .join(format!("{fixture_name}.polkavm")); + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + let fixture_path = out_dir.join(format!("{fixture_name}.polkavm")); log::debug!("Loading fixture from {fixture_path:?}"); let binary = std::fs::read(fixture_path)?; let code_hash = sp_io::hashing::keccak_256(&binary); @@ -43,12 +40,7 @@ pub mod bench { #[cfg(feature = "riscv")] macro_rules! fixture { ($name: literal) => { - include_bytes!(concat!( - env!("CARGO_WORKSPACE_ROOT_DIR"), - "/target/pallet-revive-fixtures/", - $name, - ".polkavm" - )) + include_bytes!(concat!(env!("OUT_DIR"), "/", $name, ".polkavm")) }; } #[cfg(not(feature = "riscv"))] @@ -71,3 +63,12 @@ pub mod bench { dummy } } + +#[cfg(test)] +mod test { + #[test] + fn out_dir_should_have_compiled_mocks() { + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + assert!(out_dir.join("dummy.polkavm").exists()); + } +} diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index 58110bcf186..e4340b27a18 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -387,6 +387,7 @@ pub trait EthExtra { } } +#[cfg(feature = "riscv")] #[cfg(test)] mod test { use super::*; -- GitLab From 77836cfb10d92fe797aac19695d2bf5c5448592c Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 22 Oct 2024 20:35:15 +0200 Subject: [PATCH 30/50] `fatxpool`: `LocalTransactionPool` implemented (#6104) [`LocalTransactionPool` trait](https://github.com/paritytech/polkadot-sdk/blob/d5b96e9e7f24adc1799f8e426c5cb69b4f2dbf8a/substrate/client/transaction-pool/api/src/lib.rs#L408-L426) is now implemented for `ForkAwareTransactionPool`. Closes #5493 --- prdoc/pr_6104.prdoc | 10 ++++ .../client/transaction-pool/benches/basics.rs | 9 ++++ .../client/transaction-pool/src/common/api.rs | 37 +++++-------- .../transaction-pool/src/common/tests.rs | 9 ++++ .../fork_aware_txpool/fork_aware_txpool.rs | 20 +++---- .../src/fork_aware_txpool/tx_mem_pool.rs | 18 +++---- .../src/fork_aware_txpool/view.rs | 53 +++++++++++++++++-- .../src/fork_aware_txpool/view_store.rs | 40 +++++++++++--- .../client/transaction-pool/src/graph/pool.rs | 13 ++++- .../runtime/transaction-pool/src/lib.rs | 9 ++++ 10 files changed, 162 insertions(+), 56 deletions(-) create mode 100644 prdoc/pr_6104.prdoc diff --git a/prdoc/pr_6104.prdoc b/prdoc/pr_6104.prdoc new file mode 100644 index 00000000000..2b62a68c9f0 --- /dev/null +++ b/prdoc/pr_6104.prdoc @@ -0,0 +1,10 @@ +title: "LocalTransactionPool implemented for fork aware transaction pool" + +doc: + - audience: Node Dev + description: | + LocalTransactionPool trait is implemented for fork aware transaction pool. + +crates: + - name: sc-transaction-pool + bump: minor diff --git a/substrate/client/transaction-pool/benches/basics.rs b/substrate/client/transaction-pool/benches/basics.rs index 2db34bc3f32..0d8c1cbba9b 100644 --- a/substrate/client/transaction-pool/benches/basics.rs +++ b/substrate/client/transaction-pool/benches/basics.rs @@ -91,6 +91,15 @@ impl ChainApi for TestApi { }))) } + fn validate_transaction_blocking( + &self, + _at: ::Hash, + _source: TransactionSource, + _uxt: Arc<::Extrinsic>, + ) -> sc_transaction_pool_api::error::Result { + unimplemented!(); + } + fn block_id_to_number( &self, at: &BlockId, diff --git a/substrate/client/transaction-pool/src/common/api.rs b/substrate/client/transaction-pool/src/common/api.rs index a5185ba606e..e16c0f2efa5 100644 --- a/substrate/client/transaction-pool/src/common/api.rs +++ b/substrate/client/transaction-pool/src/common/api.rs @@ -162,6 +162,18 @@ where .boxed() } + /// Validates a transaction by calling into the runtime. + /// + /// Same as `validate_transaction` but blocks the current thread when performing validation. + fn validate_transaction_blocking( + &self, + at: Block::Hash, + source: TransactionSource, + uxt: graph::ExtrinsicFor, + ) -> error::Result { + validate_transaction_blocking(&*self.client, at, source, uxt) + } + fn block_id_to_number( &self, at: &BlockId, @@ -272,28 +284,3 @@ where result } - -impl FullChainApi -where - Block: BlockT, - Client: ProvideRuntimeApi - + BlockBackend - + BlockIdTo - + HeaderBackend - + HeaderMetadata, - Client: Send + Sync + 'static, - Client::Api: TaggedTransactionQueue, -{ - /// Validates a transaction by calling into the runtime, same as - /// `validate_transaction` but blocks the current thread when performing - /// validation. Only implemented for `FullChainApi` since we can call into - /// the runtime locally. - pub fn validate_transaction_blocking( - &self, - at: Block::Hash, - source: TransactionSource, - uxt: graph::ExtrinsicFor, - ) -> error::Result { - validate_transaction_blocking(&*self.client, at, source, uxt) - } -} diff --git a/substrate/client/transaction-pool/src/common/tests.rs b/substrate/client/transaction-pool/src/common/tests.rs index 1cbabf8b5fd..b00cf5fbfed 100644 --- a/substrate/client/transaction-pool/src/common/tests.rs +++ b/substrate/client/transaction-pool/src/common/tests.rs @@ -156,6 +156,15 @@ impl ChainApi for TestApi { futures::future::ready(Ok(res)) } + fn validate_transaction_blocking( + &self, + _at: ::Hash, + _source: TransactionSource, + _uxt: Arc<::Extrinsic>, + ) -> error::Result { + unimplemented!(); + } + /// Returns a block number given the block id. fn block_id_to_number( &self, diff --git a/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs b/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs index 11e30bef7ea..7e72b44adf3 100644 --- a/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs +++ b/substrate/client/transaction-pool/src/fork_aware_txpool/fork_aware_txpool.rs @@ -599,7 +599,7 @@ where log::debug!(target: LOG_TARGET, "fatp::submit_at count:{} views:{}", xts.len(), self.active_views_count()); log_xt_trace!(target: LOG_TARGET, xts.iter().map(|xt| self.tx_hash(xt)), "[{:?}] fatp::submit_at"); let xts = xts.into_iter().map(Arc::from).collect::>(); - let mempool_result = self.mempool.extend_unwatched(source, xts.clone()); + let mempool_result = self.mempool.extend_unwatched(source, &xts); if view_store.is_empty() { return future::ready(Ok(mempool_result)).boxed() @@ -838,16 +838,16 @@ where fn submit_local( &self, _at: Block::Hash, - _xt: sc_transaction_pool_api::LocalTransactionFor, + xt: sc_transaction_pool_api::LocalTransactionFor, ) -> Result { - //todo [#5493] - //looks like view_store / view needs non async submit_local method ?. - let e = Err(sc_transaction_pool_api::error::Error::Unactionable.into()); - log::warn!( - target: LOG_TARGET, - "LocalTransactionPool::submit_local is not implemented for ForkAwareTxPool, returning error: {e:?}", - ); - e + log::debug!(target: LOG_TARGET, "fatp::submit_local views:{}", self.active_views_count()); + let xt = Arc::from(xt); + let result = self + .mempool + .extend_unwatched(TransactionSource::Local, &[xt.clone()]) + .remove(0)?; + + self.view_store.submit_local(xt).or_else(|_| Ok(result)) } } diff --git a/substrate/client/transaction-pool/src/fork_aware_txpool/tx_mem_pool.rs b/substrate/client/transaction-pool/src/fork_aware_txpool/tx_mem_pool.rs index 86ea27dcf45..989c7e8ef35 100644 --- a/substrate/client/transaction-pool/src/fork_aware_txpool/tx_mem_pool.rs +++ b/substrate/client/transaction-pool/src/fork_aware_txpool/tx_mem_pool.rs @@ -237,11 +237,11 @@ where pub(super) fn extend_unwatched( &self, source: TransactionSource, - xts: Vec>, + xts: &[ExtrinsicFor], ) -> Vec, ChainApi::Error>> { let mut transactions = self.transactions.write(); let result = xts - .into_iter() + .iter() .map(|xt| { let hash = self.api.hash_and_length(&xt).0; self.try_insert( @@ -437,7 +437,7 @@ mod tx_mem_pool_tests { let xts = (0..max + 1).map(|x| Arc::from(uxt(x as _))).collect::>(); - let results = mempool.extend_unwatched(TransactionSource::External, xts); + let results = mempool.extend_unwatched(TransactionSource::External, &xts); assert!(results.iter().take(max).all(Result::is_ok)); assert!(matches!( results.into_iter().last().unwrap().unwrap_err(), @@ -455,7 +455,7 @@ mod tx_mem_pool_tests { let mut xts = (0..max - 1).map(|x| Arc::from(uxt(x as _))).collect::>(); xts.push(xts.iter().last().unwrap().clone()); - let results = mempool.extend_unwatched(TransactionSource::External, xts); + let results = mempool.extend_unwatched(TransactionSource::External, &xts); assert!(results.iter().take(max - 1).all(Result::is_ok)); assert!(matches!( results.into_iter().last().unwrap().unwrap_err(), @@ -471,7 +471,7 @@ mod tx_mem_pool_tests { let xts = (0..max).map(|x| Arc::from(uxt(x as _))).collect::>(); - let results = mempool.extend_unwatched(TransactionSource::External, xts); + let results = mempool.extend_unwatched(TransactionSource::External, &xts); assert!(results.iter().all(Result::is_ok)); let xt = Arc::from(uxt(98)); @@ -481,7 +481,7 @@ mod tx_mem_pool_tests { sc_transaction_pool_api::error::Error::ImmediatelyDropped )); let xt = Arc::from(uxt(99)); - let mut result = mempool.extend_unwatched(TransactionSource::External, vec![xt]); + let mut result = mempool.extend_unwatched(TransactionSource::External, &[xt]); assert!(matches!( result.pop().unwrap().unwrap_err(), sc_transaction_pool_api::error::Error::ImmediatelyDropped @@ -498,7 +498,7 @@ mod tx_mem_pool_tests { let xt0 = xts.iter().last().unwrap().clone(); let xt1 = xts.iter().next().unwrap().clone(); - let results = mempool.extend_unwatched(TransactionSource::External, xts); + let results = mempool.extend_unwatched(TransactionSource::External, &xts); assert!(results.iter().all(Result::is_ok)); let result = mempool.push_watched(TransactionSource::External, xt0); @@ -506,7 +506,7 @@ mod tx_mem_pool_tests { result.unwrap_err(), sc_transaction_pool_api::error::Error::AlreadyImported(_) )); - let mut result = mempool.extend_unwatched(TransactionSource::External, vec![xt1]); + let mut result = mempool.extend_unwatched(TransactionSource::External, &[xt1]); assert!(matches!( result.pop().unwrap().unwrap_err(), sc_transaction_pool_api::error::Error::AlreadyImported(_) @@ -521,7 +521,7 @@ mod tx_mem_pool_tests { let xts0 = (0..10).map(|x| Arc::from(uxt(x as _))).collect::>(); - let results = mempool.extend_unwatched(TransactionSource::External, xts0); + let results = mempool.extend_unwatched(TransactionSource::External, &xts0); assert!(results.iter().all(Result::is_ok)); let xts1 = (0..5).map(|x| Arc::from(uxt(2 * x))).collect::>(); diff --git a/substrate/client/transaction-pool/src/fork_aware_txpool/view.rs b/substrate/client/transaction-pool/src/fork_aware_txpool/view.rs index fd5bfa8312c..99095d88cb0 100644 --- a/substrate/client/transaction-pool/src/fork_aware_txpool/view.rs +++ b/substrate/client/transaction-pool/src/fork_aware_txpool/view.rs @@ -33,10 +33,11 @@ use crate::{ LOG_TARGET, }; use parking_lot::Mutex; -use sc_transaction_pool_api::{PoolStatus, TransactionSource}; +use sc_transaction_pool_api::{error::Error as TxPoolError, PoolStatus, TransactionSource}; use sp_blockchain::HashAndNumber; use sp_runtime::{ - traits::Block as BlockT, transaction_validity::TransactionValidityError, SaturatedConversion, + generic::BlockId, traits::Block as BlockT, transaction_validity::TransactionValidityError, + SaturatedConversion, }; use std::{collections::HashMap, sync::Arc, time::Instant}; @@ -178,6 +179,50 @@ where self.pool.submit_and_watch(&self.at, source, xt).await } + /// Synchronously imports single unvalidated extrinsics into the view. + pub(super) fn submit_local( + &self, + xt: ExtrinsicFor, + ) -> Result, ChainApi::Error> { + let (hash, length) = self.pool.validated_pool().api().hash_and_length(&xt); + log::trace!(target: LOG_TARGET, "[{:?}] view::submit_local at:{}", hash, self.at.hash); + + let validity = self + .pool + .validated_pool() + .api() + .validate_transaction_blocking( + self.at.hash, + TransactionSource::Local, + Arc::from(xt.clone()), + )? + .map_err(|e| { + match e { + TransactionValidityError::Invalid(i) => TxPoolError::InvalidTransaction(i), + TransactionValidityError::Unknown(u) => TxPoolError::UnknownTransaction(u), + } + .into() + })?; + + let block_number = self + .pool + .validated_pool() + .api() + .block_id_to_number(&BlockId::hash(self.at.hash))? + .ok_or_else(|| TxPoolError::InvalidBlockId(format!("{:?}", self.at.hash)))?; + + let validated = ValidatedTransaction::valid_at( + block_number.saturated_into::(), + hash, + TransactionSource::Local, + Arc::from(xt), + length, + validity, + ); + + self.pool.validated_pool().submit(vec![validated]).remove(0) + } + /// Status of the pool associated with the view. pub(super) fn status(&self) -> PoolStatus { self.pool.validated_pool().status() @@ -243,9 +288,7 @@ where let validation_result = (api.validate_transaction(self.at.hash, tx.source, tx.data.clone()).await, tx.hash, tx); validation_results.push(validation_result); } else { - { - self.revalidation_worker_channels.lock().as_mut().map(|ch| ch.remove_sender()); - } + self.revalidation_worker_channels.lock().as_mut().map(|ch| ch.remove_sender()); should_break = true; } } => {} diff --git a/substrate/client/transaction-pool/src/fork_aware_txpool/view_store.rs b/substrate/client/transaction-pool/src/fork_aware_txpool/view_store.rs index 953d6d86033..413fca22324 100644 --- a/substrate/client/transaction-pool/src/fork_aware_txpool/view_store.rs +++ b/substrate/client/transaction-pool/src/fork_aware_txpool/view_store.rs @@ -29,6 +29,7 @@ use crate::{ ReadyIteratorFor, LOG_TARGET, }; use futures::prelude::*; +use itertools::Itertools; use parking_lot::RwLock; use sc_transaction_pool_api::{error::Error as PoolError, PoolStatus, TransactionSource}; use sp_blockchain::TreeRoute; @@ -110,6 +111,37 @@ where HashMap::<_, _>::from_iter(results.into_iter()) } + /// Synchronously imports single unverified extrinsics into every active view. + pub(super) fn submit_local( + &self, + xt: ExtrinsicFor, + ) -> Result, ChainApi::Error> { + let active_views = self + .active_views + .read() + .iter() + .map(|(_, view)| view.clone()) + .collect::>(); + + let tx_hash = self.api.hash_and_length(&xt).0; + + let result = active_views + .iter() + .map(|view| { + self.dropped_stream_controller + .add_initial_views(std::iter::once(tx_hash), view.at.hash); + view.submit_local(xt.clone()) + }) + .find_or_first(Result::is_ok); + + if let Some(Err(err)) = result { + log::trace!(target: LOG_TARGET, "[{:?}] submit_local: err: {}", tx_hash, err); + return Err(err) + }; + + Ok(tx_hash) + } + /// Import a single extrinsic and starts to watch its progress in the pool. /// /// The extrinsic is imported to every view, and the individual streams providing the progress @@ -155,12 +187,8 @@ where let maybe_error = futures::future::join_all(submit_and_watch_futures) .await .into_iter() - .reduce(|mut r, v| { - if r.is_err() && v.is_ok() { - r = v; - } - r - }); + .find_or_first(Result::is_ok); + if let Some(Err(err)) = maybe_error { log::trace!(target: LOG_TARGET, "[{:?}] submit_and_watch: err: {}", tx_hash, err); return Err((err, Some(external_watcher))); diff --git a/substrate/client/transaction-pool/src/graph/pool.rs b/substrate/client/transaction-pool/src/graph/pool.rs index 6d08a0f0b93..2dd8de352c6 100644 --- a/substrate/client/transaction-pool/src/graph/pool.rs +++ b/substrate/client/transaction-pool/src/graph/pool.rs @@ -73,7 +73,7 @@ pub trait ChainApi: Send + Sync { + Send + 'static; - /// Verify extrinsic at given block. + /// Asynchronously verify extrinsic at given block. fn validate_transaction( &self, at: ::Hash, @@ -81,6 +81,17 @@ pub trait ChainApi: Send + Sync { uxt: ExtrinsicFor, ) -> Self::ValidationFuture; + /// Synchronously verify given extrinsic at given block. + /// + /// Validates a transaction by calling into the runtime. Same as `validate_transaction` but + /// blocks the current thread when performing validation. + fn validate_transaction_blocking( + &self, + at: ::Hash, + source: TransactionSource, + uxt: ExtrinsicFor, + ) -> Result; + /// Returns a block number given the block id. fn block_id_to_number( &self, diff --git a/substrate/test-utils/runtime/transaction-pool/src/lib.rs b/substrate/test-utils/runtime/transaction-pool/src/lib.rs index 2d19dbfb6d4..6a4f38f63e8 100644 --- a/substrate/test-utils/runtime/transaction-pool/src/lib.rs +++ b/substrate/test-utils/runtime/transaction-pool/src/lib.rs @@ -450,6 +450,15 @@ impl ChainApi for TestApi { ready(Ok(Ok(validity))) } + fn validate_transaction_blocking( + &self, + _at: ::Hash, + _source: TransactionSource, + _uxt: Arc<::Extrinsic>, + ) -> Result { + unimplemented!(); + } + fn block_id_to_number( &self, at: &BlockId, -- GitLab From 6418131a5de10a80f0d9957500d36d6592194cab Mon Sep 17 00:00:00 2001 From: tmpolaczyk <44604217+tmpolaczyk@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:23:29 +0200 Subject: [PATCH 31/50] Use bool::then instead of then_some with function calls (#6156) I noticed that hardware benchmarks are being run even though we pass the --no-hardware-benchmarks cli flag. After some debugging, the cause is an incorrect usage of the `then_some` method. From [std docs](https://doc.rust-lang.org/std/primitive.bool.html#method.then_some): > Arguments passed to then_some are eagerly evaluated; if you are passing the result of a function call, it is recommended to use [then](https://doc.rust-lang.org/std/primitive.bool.html#method.then), which is lazily evaluated. ```rust let mut a = 0; let mut function_with_side_effects = || { a += 1; }; true.then_some(function_with_side_effects()); false.then_some(function_with_side_effects()); // `a` is incremented twice because the value passed to `then_some` is // evaluated eagerly. assert_eq!(a, 2); ``` This PR fixes all the similar usages of the `then_some` method across the codebase. polkadot address: 138eUqXvUYT3o4GdbnWQfGRzM8yDWh5Q2eFrFULL7RAXzdWD --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Shawn Tabrizi Co-authored-by: Oliver Tale-Yazdi --- cumulus/polkadot-omni-node/lib/src/command.rs | 16 +++++++------ polkadot/cli/src/command.rs | 10 ++++---- .../src/collator_side/validators_buffer.rs | 2 +- prdoc/pr_6156.prdoc | 23 +++++++++++++++++++ substrate/bin/node/cli/src/service.rs | 10 ++++---- substrate/client/network/src/discovery.rs | 2 +- .../client/network/sync/src/strategy/state.rs | 2 +- .../client/network/sync/src/strategy/warp.rs | 2 +- .../frame/contracts/proc-macro/src/lib.rs | 2 +- .../src/pallet/expand/pallet_struct.rs | 2 +- substrate/frame/support/src/migrations.rs | 4 ++-- templates/parachain/node/src/command.rs | 16 +++++++------ 12 files changed, 61 insertions(+), 30 deletions(-) create mode 100644 prdoc/pr_6156.prdoc diff --git a/cumulus/polkadot-omni-node/lib/src/command.rs b/cumulus/polkadot-omni-node/lib/src/command.rs index 350dcfee1cd..fe935a03cc9 100644 --- a/cumulus/polkadot-omni-node/lib/src/command.rs +++ b/cumulus/polkadot-omni-node/lib/src/command.rs @@ -266,13 +266,15 @@ pub fn run(cmd_config: RunConfig) -> Result<() } let hwbench = (!cli.no_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench( - Some(database_path), - &SUBSTRATE_REFERENCE_HARDWARE, - ) - })) + .then(|| { + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench( + Some(database_path), + &SUBSTRATE_REFERENCE_HARDWARE, + ) + }) + }) .flatten(); let parachain_account = diff --git a/polkadot/cli/src/command.rs b/polkadot/cli/src/command.rs index d124c8fb7eb..7c904e6658e 100644 --- a/polkadot/cli/src/command.rs +++ b/polkadot/cli/src/command.rs @@ -203,10 +203,12 @@ where runner.run_node_until_exit(move |config| async move { let hwbench = (!cli.run.no_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) - })) + .then(|| { + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(&database_path); + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) + }) + }) .flatten(); let database_source = config.database.clone(); diff --git a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs index fbb3ff4328a..35202fc9629 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/validators_buffer.rs @@ -110,7 +110,7 @@ impl ValidatorGroupsBuffer { .validators .iter() .enumerate() - .filter_map(|(idx, authority_id)| bits[idx].then_some(authority_id.clone())) + .filter_map(|(idx, authority_id)| bits[idx].then(|| authority_id.clone())) .collect(); if let Some(last_group) = self.group_infos.iter().last() { diff --git a/prdoc/pr_6156.prdoc b/prdoc/pr_6156.prdoc new file mode 100644 index 00000000000..d20324a83a2 --- /dev/null +++ b/prdoc/pr_6156.prdoc @@ -0,0 +1,23 @@ +title: "Use bool::then instead of then_some with function calls" +doc: +- audience: Node Dev + description: |- + Fix misusage of `bool::then_some`. + +crates: +- name: polkadot-omni-node-lib + bump: patch +- name: polkadot-cli + bump: patch +- name: polkadot-collator-protocol + bump: patch +- name: sc-network + bump: patch +- name: sc-network-sync + bump: patch +- name: pallet-contracts-proc-macro + bump: patch +- name: frame-support-procedural + bump: patch +- name: frame-support + bump: patch diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index d71f1304caf..7b166f94bcc 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -420,10 +420,12 @@ pub fn new_full_base::Hash>>( let enable_offchain_worker = config.offchain_worker.enabled; let hwbench = (!disable_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) - })) + .then(|| { + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(&database_path); + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) + }) + }) .flatten(); let sc_service::PartialComponents { diff --git a/substrate/client/network/src/discovery.rs b/substrate/client/network/src/discovery.rs index 86c66c22701..49e0797c126 100644 --- a/substrate/client/network/src/discovery.rs +++ b/substrate/client/network/src/discovery.rs @@ -648,7 +648,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { let mut list: LinkedHashSet<_> = self .permanent_addresses .iter() - .filter_map(|(p, a)| (*p == peer_id).then_some(a.clone())) + .filter_map(|(p, a)| (*p == peer_id).then(|| a.clone())) .collect(); if let Some(ephemeral_addresses) = self.ephemeral_addresses.get(&peer_id) { diff --git a/substrate/client/network/sync/src/strategy/state.rs b/substrate/client/network/sync/src/strategy/state.rs index a04ab8be4fe..d69ab3e2d53 100644 --- a/substrate/client/network/sync/src/strategy/state.rs +++ b/substrate/client/network/sync/src/strategy/state.rs @@ -173,7 +173,7 @@ impl StateStrategy { peer_id: PeerId, announce: &BlockAnnounce, ) -> Option<(B::Hash, NumberFor)> { - is_best.then_some({ + is_best.then(|| { let best_number = *announce.header.number(); let best_hash = announce.header.hash(); if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { diff --git a/substrate/client/network/sync/src/strategy/warp.rs b/substrate/client/network/sync/src/strategy/warp.rs index cce6a93caf4..0c71dd3c6ae 100644 --- a/substrate/client/network/sync/src/strategy/warp.rs +++ b/substrate/client/network/sync/src/strategy/warp.rs @@ -301,7 +301,7 @@ where peer_id: PeerId, announce: &BlockAnnounce, ) -> Option<(B::Hash, NumberFor)> { - is_best.then_some({ + is_best.then(|| { let best_number = *announce.header.number(); let best_hash = announce.header.hash(); if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { diff --git a/substrate/frame/contracts/proc-macro/src/lib.rs b/substrate/frame/contracts/proc-macro/src/lib.rs index 84ea7de00a2..4aba1d24dbd 100644 --- a/substrate/frame/contracts/proc-macro/src/lib.rs +++ b/substrate/frame/contracts/proc-macro/src/lib.rs @@ -522,7 +522,7 @@ fn expand_docs(def: &EnvDef) -> TokenStream2 { /// `expand_impls()`). fn expand_env(def: &EnvDef, docs: bool) -> TokenStream2 { let impls = expand_impls(def); - let docs = docs.then_some(expand_docs(def)).unwrap_or(TokenStream2::new()); + let docs = docs.then(|| expand_docs(def)).unwrap_or(TokenStream2::new()); let stable_api_count = def.host_funcs.iter().filter(|f| f.is_stable).count(); quote! { diff --git a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs index c6166ff45b1..79bf33a828e 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -171,7 +171,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { let whitelisted_storage_idents: Vec = def .storages .iter() - .filter_map(|s| s.whitelisted.then_some(s.ident.clone())) + .filter_map(|s| s.whitelisted.then(|| s.ident.clone())) .collect(); let whitelisted_storage_keys_impl = quote::quote![ diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs index 905d6143e4f..3fdf8d6edc9 100644 --- a/substrate/frame/support/src/migrations.rs +++ b/substrate/frame/support/src/migrations.rs @@ -825,14 +825,14 @@ impl SteppedMigrations for T { fn nth_id(n: u32) -> Option> { n.is_zero() - .then_some(T::id().encode()) + .then(|| T::id().encode()) .defensive_proof("nth_id should only be called with n==0") } fn nth_max_steps(n: u32) -> Option> { // It should be generally fine to call with n>0, but the code should not attempt to. n.is_zero() - .then_some(T::max_steps()) + .then(|| T::max_steps()) .defensive_proof("nth_max_steps should only be called with n==0") } diff --git a/templates/parachain/node/src/command.rs b/templates/parachain/node/src/command.rs index 938bda837e0..5d9308aed15 100644 --- a/templates/parachain/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -220,13 +220,15 @@ pub fn run() -> Result<()> { runner.run_node_until_exit(|config| async move { let hwbench = (!cli.no_hardware_benchmarks) - .then_some(config.database.path().map(|database_path| { - let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench( - Some(database_path), - &SUBSTRATE_REFERENCE_HARDWARE, - ) - })) + .then(|| { + config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(database_path); + sc_sysinfo::gather_hwbench( + Some(database_path), + &SUBSTRATE_REFERENCE_HARDWARE, + ) + }) + }) .flatten(); let para_id = chain_spec::Extensions::try_get(&*config.chain_spec) -- GitLab From b4732add46910370443d092a3f479986060f6df5 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 22 Oct 2024 18:38:38 -0300 Subject: [PATCH 32/50] Assets in pool with native can be used in `query_weight_to_asset_fee` (#6080) A follow-up to https://github.com/paritytech/polkadot-sdk/pull/5599. Assets in a pool with the native one are returned from `query_acceptable_payment_assets`. Now those assets can be used in `query_weight_to_asset_fee` to get the correct amount that needs to be paid. --------- Co-authored-by: command-bot <> --- .../emulated/common/src/macros.rs | 68 +++++++++++++++++++ .../tests/assets/asset-hub-rococo/src/lib.rs | 2 +- .../assets/asset-hub-rococo/src/tests/swap.rs | 5 ++ .../tests/assets/asset-hub-westend/src/lib.rs | 2 +- .../asset-hub-westend/src/tests/swap.rs | 5 ++ .../assets/asset-hub-rococo/src/lib.rs | 44 +++++++----- .../assets/asset-hub-westend/src/lib.rs | 47 ++++++++----- .../runtimes/assets/common/src/lib.rs | 33 +++++++++ prdoc/pr_6080.prdoc | 22 ++++++ 9 files changed, 191 insertions(+), 37 deletions(-) create mode 100644 prdoc/pr_6080.prdoc diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index 68926b04bfe..3ff5ed388a3 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -451,3 +451,71 @@ macro_rules! test_dry_run_transfer_across_pk_bridge { } }; } + +#[macro_export] +macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub { + ( $asset_hub:ty ) => { + $crate::macros::paste::paste! { + use emulated_integration_tests_common::USDT_ID; + use xcm_runtime_apis::fees::{Error as XcmPaymentApiError, runtime_decl_for_xcm_payment_api::XcmPaymentApiV1}; + + $asset_hub::execute_with(|| { + // Setup a pool between USDT and WND. + type RuntimeOrigin = <$asset_hub as Chain>::RuntimeOrigin; + type Assets = <$asset_hub as [<$asset_hub Pallet>]>::Assets; + type AssetConversion = <$asset_hub as [<$asset_hub Pallet>]>::AssetConversion; + let wnd = Location::new(1, []); + let usdt = Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]); + let sender = [<$asset_hub Sender>]::get(); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(sender.clone()), + Box::new(wnd.clone()), + Box::new(usdt.clone()), + )); + + type Runtime = <$asset_hub as Chain>::Runtime; + let acceptable_payment_assets = Runtime::query_acceptable_payment_assets(4).unwrap(); + assert_eq!(acceptable_payment_assets, vec![ + VersionedAssetId::from(AssetId(wnd.clone())), + VersionedAssetId::from(AssetId(usdt.clone())), + ]); + + let program = Xcm::<()>::builder() + .withdraw_asset((Parent, 100u128)) + .buy_execution((Parent, 10u128), Unlimited) + .deposit_asset(All, [0u8; 32]) + .build(); + let weight = Runtime::query_xcm_weight(VersionedXcm::from(program)).unwrap(); + let fee_in_wnd = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(wnd.clone()))).unwrap(); + // Assets not in a pool don't work. + assert!(Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1)])))).is_err()); + let fee_in_usdt_fail = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt.clone()))); + // Weight to asset fee fails because there's not enough asset in the pool. + // We just created it, there's none. + assert_eq!(fee_in_usdt_fail, Err(XcmPaymentApiError::AssetNotFound)); + // We add some. + assert_ok!(Assets::mint( + RuntimeOrigin::signed(sender.clone()), + USDT_ID.into(), + sender.clone().into(), + 5_000_000_000_000 + )); + // We make 1 WND = 4 USDT. + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(sender.clone()), + Box::new(wnd), + Box::new(usdt.clone()), + 1_000_000_000_000, + 4_000_000_000_000, + 0, + 0, + sender.into() + )); + // Now it works. + let fee_in_usdt = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::from(AssetId(usdt))); + assert_ok!(fee_in_usdt); + assert!(fee_in_usdt.unwrap() > fee_in_wnd); + }); + } + }; +} 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 12f440fdefe..1184b5fd7c4 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 @@ -37,7 +37,7 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::DUMMY_EMPTY, test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, - test_relay_is_trusted_teleporter, + test_relay_is_trusted_teleporter, test_xcm_fee_querying_apis_work_for_asset_hub, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs index ac0c90ba198..d9b32eaa357 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs @@ -386,3 +386,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { ); }); } + +#[test] +fn xcm_fee_querying_apis_work() { + test_xcm_fee_querying_apis_work_for_asset_hub!(AssetHubRococo); +} 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 906768b19b7..179a44e14aa 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 @@ -34,7 +34,7 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::DUMMY_EMPTY, test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, - test_relay_is_trusted_teleporter, + test_relay_is_trusted_teleporter, test_xcm_fee_querying_apis_work_for_asset_hub, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, Test, TestArgs, TestContext, TestExt, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs index 1a282145215..4535fd43199 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs @@ -389,3 +389,8 @@ fn pay_xcm_fee_with_some_asset_swapped_for_native() { ); }); } + +#[test] +fn xcm_fee_querying_apis_work() { + test_xcm_fee_querying_apis_work_for_asset_hub!(AssetHubWestend); +} 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 ae5d2102ff6..f768f803aea 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -1412,31 +1412,41 @@ impl_runtime_apis! { // We accept the native token to pay fees. let mut acceptable_assets = vec![AssetId(native_token.clone())]; // We also accept all assets in a pool with the native token. - acceptable_assets.extend( - pallet_asset_conversion::Pools::::iter_keys().filter_map( - |(asset_1, asset_2)| { - if asset_1 == native_token { - Some(asset_2.clone().into()) - } else if asset_2 == native_token { - Some(asset_1.clone().into()) - } else { - None - } - }, - ), - ); + let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::< + Runtime, + xcm::v4::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) } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let native_asset = xcm_config::TokenLocation::get(); + let fee_in_native = WeightToFee::weight_to_fee(&weight); match asset.try_as::() { - Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => { + Ok(asset_id) if asset_id.0 == native_asset => { // for native token - Ok(WeightToFee::weight_to_fee(&weight)) + Ok(fee_in_native) }, Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) + let assets_in_pool_with_this_asset: Vec<_> = assets_common::get_assets_in_pool_with::< + Runtime, + xcm::v4::Location + >(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?; + if assets_in_pool_with_this_asset + .into_iter() + .map(|asset_id| asset_id.0) + .any(|location| location == native_asset) { + pallet_asset_conversion::Pallet::::quote_price_tokens_for_exact_tokens( + asset_id.clone().0, + native_asset, + fee_in_native, + true, // We include the fee. + ).ok_or(XcmPaymentApiError::AssetNotFound) + } else { + log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + } }, Err(_) => { log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); 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 0da80098b28..63234bfb6e5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1450,31 +1450,42 @@ impl_runtime_apis! { // We accept the native token to pay fees. let mut acceptable_assets = vec![AssetId(native_token.clone())]; // We also accept all assets in a pool with the native token. - acceptable_assets.extend( - pallet_asset_conversion::Pools::::iter_keys().filter_map( - |(asset_1, asset_2)| { - if asset_1 == native_token { - Some(asset_2.clone().into()) - } else if asset_2 == native_token { - Some(asset_1.clone().into()) - } else { - None - } - }, - ), - ); + let assets_in_pool_with_native = assets_common::get_assets_in_pool_with::< + Runtime, + xcm::v4::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) } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let native_asset = xcm_config::WestendLocation::get(); + let fee_in_native = WeightToFee::weight_to_fee(&weight); match asset.try_as::() { - Ok(asset_id) if asset_id.0 == xcm_config::WestendLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) + Ok(asset_id) if asset_id.0 == native_asset => { + // for native asset + Ok(fee_in_native) }, Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) + // 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 + >(&asset_id.0).map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?; + if assets_in_pool_with_this_asset + .into_iter() + .map(|asset_id| asset_id.0) + .any(|location| location == native_asset) { + pallet_asset_conversion::Pallet::::quote_price_tokens_for_exact_tokens( + asset_id.clone().0, + native_asset, + fee_in_native, + true, // We include the fee. + ).ok_or(XcmPaymentApiError::AssetNotFound) + } else { + log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + } }, Err(_) => { log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs index deda5fa4ab9..26046e5974b 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -26,6 +26,9 @@ pub mod runtime_api; extern crate alloc; use crate::matching::{LocalLocationPattern, ParentLocation}; +use alloc::vec::Vec; +use codec::{Decode, EncodeLike}; +use core::cmp::PartialEq; use frame_support::traits::{Equals, EverythingBut}; use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId}; use sp_runtime::traits::TryConvertInto; @@ -134,6 +137,36 @@ pub type PoolAssetsConvertedConcreteId = TryConvertInto, >; +/// Returns an iterator of all assets in a pool with `asset`. +/// +/// Should only be used in runtime APIs since it iterates over the whole +/// `pallet_asset_conversion::Pools` map. +/// +/// It takes in any version of an XCM Location but always returns the latest one. +/// This is to allow some margin of migrating the pools when updating the XCM version. +/// +/// An error of type `()` is returned if the version conversion fails for XCM locations. +/// This error should be mapped by the caller to a more descriptive one. +pub fn get_assets_in_pool_with< + Runtime: pallet_asset_conversion::Config, + L: TryInto + Clone + Decode + EncodeLike + PartialEq, +>( + asset: &L, +) -> Result, ()> { + pallet_asset_conversion::Pools::::iter_keys() + .filter_map(|(asset_1, asset_2)| { + if asset_1 == *asset { + Some(asset_2) + } else if asset_2 == *asset { + Some(asset_1) + } else { + None + } + }) + .map(|location| location.try_into().map_err(|_| ()).map(AssetId)) + .collect::, _>>() +} + #[cfg(test)] mod tests { use super::*; diff --git a/prdoc/pr_6080.prdoc b/prdoc/pr_6080.prdoc new file mode 100644 index 00000000000..52ecd58dddd --- /dev/null +++ b/prdoc/pr_6080.prdoc @@ -0,0 +1,22 @@ +# 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: Assets in pool with native can be used in query_weight_to_asset_fee in Asset Hubs + +doc: + - audience: Runtime User + description: | + `query_weight_to_asset_fee` now works with assets in a pool with the native asset in both + Westend and Rococo asset hubs. + This means all the information you get from `query_acceptable_payment_assets` can be used + directly in `query_weight_to_asset_fee` to get the correct fees that need to be paid. + +crates: + - name: assets-common + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: emulated-integration-tests-common + bump: minor -- GitLab From ed231828fbe662cdb78b24d54a98fc13a9a64fdd Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Wed, 23 Oct 2024 13:22:48 +0200 Subject: [PATCH 33/50] [pallet-revive] Add pallet to AH westend (#5502) Add pallet-revive to Westend runtime, and configure the runtime to accept Ethereum signed transaction --- Cargo.lock | 9 +- .../assets/asset-hub-westend/Cargo.toml | 4 + .../assets/asset-hub-westend/src/lib.rs | 183 +++++++++++++++++- prdoc/pr_5502.prdoc | 7 + 4 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 prdoc/pr_5502.prdoc diff --git a/Cargo.lock b/Cargo.lock index a42f5baa476..ce19fb039cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1022,6 +1022,7 @@ dependencies = [ "pallet-nfts", "pallet-nfts-runtime-api", "pallet-proxy", + "pallet-revive", "pallet-session", "pallet-state-trie-migration", "pallet-timestamp", @@ -16835,8 +16836,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.10.5", + "heck 0.5.0", + "itertools 0.12.1", "log", "multimap", "once_cell", @@ -16869,7 +16870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", @@ -16882,7 +16883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 5fa48381b67..d5eaa43ab83 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -44,6 +44,7 @@ pallet-timestamp = { workspace = true } pallet-transaction-payment = { workspace = true } pallet-transaction-payment-rpc-runtime-api = { workspace = true } pallet-uniques = { workspace = true } +pallet-revive = { workspace = true } pallet-utility = { workspace = true } sp-api = { workspace = true } sp-block-builder = { workspace = true } @@ -129,6 +130,7 @@ runtime-benchmarks = [ "pallet-nft-fractionalization/runtime-benchmarks", "pallet-nfts/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", + "pallet-revive/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-transaction-payment/runtime-benchmarks", @@ -169,6 +171,7 @@ try-runtime = [ "pallet-nft-fractionalization/try-runtime", "pallet-nfts/try-runtime", "pallet-proxy/try-runtime", + "pallet-revive/try-runtime", "pallet-session/try-runtime", "pallet-state-trie-migration/try-runtime", "pallet-timestamp/try-runtime", @@ -221,6 +224,7 @@ std = [ "pallet-nfts-runtime-api/std", "pallet-nfts/std", "pallet-proxy/std", + "pallet-revive/std", "pallet-session/std", "pallet-state-trie-migration/std", "pallet-timestamp/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 63234bfb6e5..9a60de77a58 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -47,7 +47,7 @@ use frame_support::{ fungible, fungibles, tokens::{imbalance::ResolveAssetTo, nonfungibles_v2::Inspect}, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, InstanceFilter, - TransformOrigin, + Nothing, TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -58,13 +58,14 @@ use frame_system::{ }; use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_nfts::{DestroyWitness, PalletFeatures}; +use pallet_revive::evm::runtime::EthExtra; use parachains_common::{ impls::DealWithFees, message_queue::*, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, BlockNumber, CollectionId, Hash, Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, }; use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdConversion, BlakeTwo256, Block as BlockT, Saturating, Verify}, @@ -934,6 +935,52 @@ impl pallet_xcm_bridge_hub_router::Config for Runtime type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; } +parameter_types! { + pub const DepositPerItem: Balance = deposit(1, 0); + pub const DepositPerByte: Balance = deposit(0, 1); + pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(30); +} + +type EventRecord = frame_system::EventRecord< + ::RuntimeEvent, + ::Hash, +>; + +impl pallet_revive::Config for Runtime { + type Time = Timestamp; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type CallFilter = Nothing; + type DepositPerItem = DepositPerItem; + type DepositPerByte = DepositPerByte; + type WeightPrice = pallet_transaction_payment::Pallet; + type WeightInfo = pallet_revive::weights::SubstrateWeight; + type ChainExtension = (); + type AddressMapper = pallet_revive::DefaultAddressMapper; + type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; + type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; + type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; + type RuntimeHoldReason = RuntimeHoldReason; + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type Debug = (); + type Xcm = pallet_xcm::Pallet; + type ChainId = ConstU64<420_420_421>; +} + +impl TryFrom for pallet_revive::Call { + type Error = (); + + fn try_from(value: RuntimeCall) -> Result { + match value { + RuntimeCall::Revive(call) => Ok(call), + _ => Err(()), + } + } +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -982,6 +1029,7 @@ construct_runtime!( AssetsFreezer: pallet_assets_freezer:: = 57, ForeignAssetsFreezer: pallet_assets_freezer:: = 58, PoolAssetsFreezer: pallet_assets_freezer:: = 59, + Revive: pallet_revive = 60, StateTrieMigration: pallet_state_trie_migration = 70, @@ -1012,9 +1060,34 @@ pub type TxExtension = ( cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, frame_metadata_hash_extension::CheckMetadataHash, ); + +/// Default extensions applied to Ethereum transactions. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct EthExtraImpl; + +impl EthExtra for EthExtraImpl { + type Config = Runtime; + type Extension = TxExtension; + + fn get_eth_extension(nonce: u32, tip: Balance) -> Self::Extension { + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::from(generic::Era::Immortal), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::::from(tip, None), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::::new(), + frame_metadata_hash_extension::CheckMetadataHash::::new(false), + ) + } +} + /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; + pallet_revive::evm::runtime::UncheckedExtrinsic; /// Migrations to apply on runtime upgrade. pub type Migrations = ( @@ -1249,6 +1322,7 @@ mod benches { [cumulus_pallet_xcmp_queue, XcmpQueue] [pallet_xcm_bridge_hub_router, ToRococo] [pallet_asset_conversion_ops, AssetConversionMigration] + [pallet_revive, Revive] // XCM [pallet_xcm, PalletXcmExtrinsicsBenchmark::] // NOTE: Make sure you point to the individual modules below. @@ -1994,6 +2068,109 @@ impl_runtime_apis! { PolkadotXcm::is_trusted_teleporter(asset, location) } } + + impl pallet_revive::ReviveApi for Runtime + { + fn eth_transact( + from: H160, + dest: Option, + value: Balance, + input: Vec, + gas_limit: Option, + storage_deposit_limit: Option, + ) -> pallet_revive::EthContractResult + { + use pallet_revive::AddressMapper; + let blockweights = ::BlockWeights::get(); + let origin = ::AddressMapper::to_account_id(&from); + + let encoded_size = |pallet_call| { + let call = RuntimeCall::Revive(pallet_call); + let uxt: UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic::new_bare(call).into(); + uxt.encoded_size() as u32 + }; + + Revive::bare_eth_transact( + origin, + dest, + value, + input, + gas_limit.unwrap_or(blockweights.max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + encoded_size, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn call( + origin: AccountId, + dest: H160, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + input_data: Vec, + ) -> pallet_revive::ContractResult { + let blockweights= ::BlockWeights::get(); + Revive::bare_call( + RuntimeOrigin::signed(origin), + dest, + value, + gas_limit.unwrap_or(blockweights.max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + input_data, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn instantiate( + origin: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + code: pallet_revive::Code, + data: Vec, + salt: Option<[u8; 32]>, + ) -> pallet_revive::ContractResult + { + let blockweights= ::BlockWeights::get(); + Revive::bare_instantiate( + RuntimeOrigin::signed(origin), + value, + gas_limit.unwrap_or(blockweights.max_block), + storage_deposit_limit.unwrap_or(u128::MAX), + code, + data, + salt, + pallet_revive::DebugInfo::UnsafeDebug, + pallet_revive::CollectEvents::UnsafeCollect, + ) + } + + fn upload_code( + origin: AccountId, + code: Vec, + storage_deposit_limit: Option, + ) -> pallet_revive::CodeUploadResult + { + Revive::bare_upload_code( + RuntimeOrigin::signed(origin), + code, + storage_deposit_limit.unwrap_or(u128::MAX), + ) + } + + fn get_storage( + address: H160, + key: [u8; 32], + ) -> pallet_revive::GetStorageResult { + Revive::get_storage( + address, + key + ) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/prdoc/pr_5502.prdoc b/prdoc/pr_5502.prdoc new file mode 100644 index 00000000000..ea9972f0187 --- /dev/null +++ b/prdoc/pr_5502.prdoc @@ -0,0 +1,7 @@ +title: '[pallet-revive] Add pallet to AH westend' +doc: + - audience: Runtime Dev + description: 'Add pallet-revive to Westend runtime, and configure the runtime to accept Ethereum signed transaction' +crates: +- name: asset-hub-westend-runtime + bump: major -- GitLab From fc486e55d5f8c97c652c977f8a2295f6cb9c7231 Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:58:58 +0100 Subject: [PATCH 34/50] Polkadot OmniNode Docs (#6094) provides low-level documentation on how the omni-node is meant to work. This is meant to act as reusable material for other teams (e.g. Papermoon and W3F) to use and integrate into the high level Polkadot documentation. Broadly speaking, for omni-node to have great rust-docs, we need to focus on the following crates, all of which got a bit of love in this PR: 1. `sp-genesis-builder` 2. `polkadot-omni-node` 3. `polkadot-omni-node-lib` 4. `frame-omni-bencher` On top of this, we have now: * `polkadot_sdk_docs::guides` contains two new steps demonstrating the most basic version of composing your pallet, putting it into a runtime, and putting that runtime into omni-node * `polkadot_sdk_docs::reference_docs::omni_node` to explain in more detail how omni-node differs from the old-school node. * `polkadot_sdk_docs::reference_docs::frame_weight_benchmarking` to finally have a minimal reference about weights and benchmarking. * It provides tests for some of the steps in https://github.com/paritytech/polkadot-sdk/issues/5568 closes https://github.com/paritytech/polkadot-sdk/issues/5568 closes https://github.com/paritytech/polkadot-sdk/issues/4781 Next steps - [x] Ensure the README of the parachain template is up-to-date. @iulianbarbu - [ ] Readme for `polkadot-omni-node` and similar is updated. For now, use `cargo-readme` and copy over the rust-docs. To build the branch locally and run this: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/meta_contributing/index.html#how-to-develop-locally --------- Co-authored-by: Iulian Barbu <14218860+iulianbarbu@users.noreply.github.com> Co-authored-by: Sebastian Kunert Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> --- .gitignore | 1 + Cargo.lock | 48 +- Cargo.toml | 4 + cumulus/polkadot-omni-node/lib/src/cli.rs | 6 + cumulus/polkadot-omni-node/lib/src/lib.rs | 13 +- .../lib/src/nodes/manual_seal.rs | 6 +- cumulus/polkadot-omni-node/src/main.rs | 7 +- docs/mermaid/IA.mmd | 1 - docs/sdk/Cargo.toml | 24 +- docs/sdk/assets/theme.css | 35 ++ .../packages/guides/first-pallet/Cargo.toml | 26 + .../packages/guides/first-pallet/src/lib.rs | 480 ++++++++++++++++++ .../packages/guides/first-runtime/Cargo.toml | 60 +++ .../packages/guides/first-runtime/build.rs | 27 + .../packages/guides/first-runtime/src/lib.rs | 301 +++++++++++ .../src/guides/enable_elastic_scaling_mvp.rs | 9 +- docs/sdk/src/guides/mod.rs | 39 +- docs/sdk/src/guides/your_first_node.rs | 313 ++++++++++++ docs/sdk/src/guides/your_first_pallet/mod.rs | 46 +- .../guides/your_first_pallet/with_event.rs | 101 ---- docs/sdk/src/guides/your_first_runtime.rs | 169 +++++- docs/sdk/src/lib.rs | 3 +- docs/sdk/src/meta_contributing.rs | 10 +- docs/sdk/src/polkadot_sdk/mod.rs | 23 + docs/sdk/src/polkadot_sdk/substrate.rs | 16 - .../src/reference_docs/chain_spec_genesis.rs | 3 + .../frame_benchmarking_weight.rs | 221 +++++++- docs/sdk/src/reference_docs/mod.rs | 4 +- docs/sdk/src/reference_docs/omni_node.rs | 185 +++++++ docs/sdk/src/reference_docs/umbrella_crate.rs | 1 + prdoc/pr_6094.prdoc | 21 + substrate/bin/node/cli/src/cli.rs | 1 + substrate/frame/Cargo.toml | 6 + substrate/frame/aura/src/lib.rs | 4 +- substrate/frame/src/lib.rs | 10 + .../primitives/genesis-builder/src/lib.rs | 72 ++- templates/minimal/runtime/src/lib.rs | 2 +- .../runtime/src/genesis_config_presets.rs | 2 + 38 files changed, 2071 insertions(+), 229 deletions(-) create mode 100644 docs/sdk/packages/guides/first-pallet/Cargo.toml create mode 100644 docs/sdk/packages/guides/first-pallet/src/lib.rs create mode 100644 docs/sdk/packages/guides/first-runtime/Cargo.toml create mode 100644 docs/sdk/packages/guides/first-runtime/build.rs create mode 100644 docs/sdk/packages/guides/first-runtime/src/lib.rs delete mode 100644 docs/sdk/src/guides/your_first_pallet/with_event.rs create mode 100644 docs/sdk/src/reference_docs/omni_node.rs create mode 100644 prdoc/pr_6094.prdoc diff --git a/.gitignore b/.gitignore index 28c28cc8ab0..afa9ed33f4a 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ runtime/wasm/target/ substrate.code-workspace target/ *.scale +justfile diff --git a/Cargo.lock b/Cargo.lock index ce19fb039cb..602891892a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15633,6 +15633,7 @@ dependencies = [ name = "polkadot-sdk-docs" version = "0.0.1" dependencies = [ + "assert_cmd", "chain-spec-guide-runtime", "cumulus-client-service", "cumulus-pallet-aura-ext", @@ -15640,6 +15641,7 @@ dependencies = [ "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "docify", + "frame-benchmarking", "frame-executive", "frame-metadata-hash-extension", "frame-support", @@ -15663,6 +15665,7 @@ dependencies = [ "pallet-example-offchain-worker", "pallet-example-single-block-migrations", "pallet-examples", + "pallet-grandpa", "pallet-multisig", "pallet-nfts", "pallet-preimage", @@ -15677,8 +15680,12 @@ dependencies = [ "pallet-xcm", "parachain-template-runtime", "parity-scale-codec", + "polkadot-omni-node-lib", "polkadot-sdk", + "polkadot-sdk-docs-first-pallet", + "polkadot-sdk-docs-first-runtime", "polkadot-sdk-frame", + "rand", "sc-chain-spec", "sc-cli", "sc-client-db", @@ -15694,6 +15701,7 @@ dependencies = [ "sc-rpc-api", "sc-service", "scale-info", + "serde_json", "simple-mermaid 0.1.1", "solochain-template-runtime", "sp-api 26.0.0", @@ -15708,6 +15716,7 @@ dependencies = [ "sp-std 14.0.0", "sp-tracing 16.0.0", "sp-version 29.0.0", + "sp-weights 27.0.0", "staging-chain-spec-builder", "staging-node-cli", "staging-parachain-info", @@ -15720,6 +15729,35 @@ dependencies = [ "xcm-simulator", ] +[[package]] +name = "polkadot-sdk-docs-first-pallet" +version = "0.0.0" +dependencies = [ + "docify", + "parity-scale-codec", + "polkadot-sdk-frame", + "scale-info", +] + +[[package]] +name = "polkadot-sdk-docs-first-runtime" +version = "0.0.0" +dependencies = [ + "docify", + "pallet-balances", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "polkadot-sdk-docs-first-pallet", + "polkadot-sdk-frame", + "scale-info", + "serde_json", + "sp-keyring", + "substrate-wasm-builder", +] + [[package]] name = "polkadot-sdk-frame" version = "0.1.0" @@ -15742,8 +15780,10 @@ dependencies = [ "sp-consensus-aura", "sp-consensus-grandpa", "sp-core 28.0.0", + "sp-genesis-builder", "sp-inherents", "sp-io 30.0.0", + "sp-keyring", "sp-offchain", "sp-runtime 31.0.1", "sp-session", @@ -16836,8 +16876,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", - "heck 0.5.0", - "itertools 0.12.1", + "heck 0.4.1", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -16870,7 +16910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", @@ -16883,7 +16923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2 1.0.86", "quote 1.0.37", "syn 2.0.82", diff --git a/Cargo.toml b/Cargo.toml index 049de32b54c..6ba91de3c09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,6 +149,8 @@ members = [ "cumulus/test/service", "cumulus/xcm/xcm-emulator", "docs/sdk", + "docs/sdk/packages/guides/first-pallet", + "docs/sdk/packages/guides/first-runtime", "docs/sdk/src/reference_docs/chain_spec_runtime", "polkadot", "polkadot/cli", @@ -806,6 +808,8 @@ hyper = { version = "1.3.1", default-features = false } hyper-rustls = { version = "0.24.2" } hyper-util = { version = "0.1.5", default-features = false } # TODO: remove hyper v0.14 https://github.com/paritytech/polkadot-sdk/issues/4896 +first-pallet = { package = "polkadot-sdk-docs-first-pallet", path = "docs/sdk/packages/guides/first-pallet", default-features = false } +first-runtime = { package = "polkadot-sdk-docs-first-runtime", path = "docs/sdk/packages/guides/first-runtime", default-features = false } hyperv14 = { package = "hyper", version = "0.14.29", default-features = false } impl-serde = { version = "0.5.0", default-features = false } impl-trait-for-tuples = { version = "0.2.2" } diff --git a/cumulus/polkadot-omni-node/lib/src/cli.rs b/cumulus/polkadot-omni-node/lib/src/cli.rs index 6ca328912bb..dc59c185d90 100644 --- a/cumulus/polkadot-omni-node/lib/src/cli.rs +++ b/cumulus/polkadot-omni-node/lib/src/cli.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +//! CLI options of the omni-node. See [`Command`]. + use crate::{ chain_spec::DiskChainSpecLoader, common::{ @@ -103,6 +105,7 @@ pub enum Subcommand { Benchmark(frame_benchmarking_cli::BenchmarkCmd), } +/// CLI Options shipped with `polkadot-omni-node`. #[derive(clap::Parser)] #[command( propagate_version = true, @@ -113,9 +116,11 @@ pub struct Cli { #[arg(skip)] pub(crate) chain_spec_loader: Option>, + /// Possible subcommands. See [`Subcommand`]. #[command(subcommand)] pub subcommand: Option, + /// The shared parameters with all cumulus-based parachain nodes. #[command(flatten)] pub run: cumulus_client_cli::RunCmd, @@ -200,6 +205,7 @@ impl SubstrateCli for Cli { } } +/// The relay chain CLI flags. These are passed in after a `--` at the end. #[derive(Debug)] pub struct RelayChainCli { /// The actual relay chain cli object. diff --git a/cumulus/polkadot-omni-node/lib/src/lib.rs b/cumulus/polkadot-omni-node/lib/src/lib.rs index a293ab225c6..3f01f421114 100644 --- a/cumulus/polkadot-omni-node/lib/src/lib.rs +++ b/cumulus/polkadot-omni-node/lib/src/lib.rs @@ -14,6 +14,8 @@ // 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 @@ -37,11 +39,18 @@ //! //! ## Examples //! -//! For an example, see the `polkadot-parachain-bin` crate. +//! 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 ` #![deny(missing_docs)] -mod cli; +pub mod cli; mod command; mod common; mod fake_runtime_api; 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 d00d7adf27e..e8043bd7b2a 100644 --- a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs +++ b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs @@ -28,7 +28,6 @@ use sc_network::NetworkBackend; use sc_service::{build_polkadot_syncing_strategy, Configuration, PartialComponents, TaskManager}; use sc_telemetry::TelemetryHandle; use sp_runtime::traits::Header; -use sp_timestamp::Timestamp; use std::{marker::PhantomData, sync::Arc}; pub struct ManualSealNode(PhantomData); @@ -182,7 +181,10 @@ impl ManualSealNode { additional_key_values: None, }; Ok(( - sp_timestamp::InherentDataProvider::new(Timestamp::new(0)), + // This is intentional, as the runtime that we expect to run against this + // will never receive the aura-related inherents/digests, and providing + // real timestamps would cause aura <> timestamp checking to fail. + sp_timestamp::InherentDataProvider::new(sp_timestamp::Timestamp::new(0)), mocked_parachain, )) } diff --git a/cumulus/polkadot-omni-node/src/main.rs b/cumulus/polkadot-omni-node/src/main.rs index a86ec6f6fde..5bca81e2e78 100644 --- a/cumulus/polkadot-omni-node/src/main.rs +++ b/cumulus/polkadot-omni-node/src/main.rs @@ -14,12 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -//! Basic polkadot omni-node. +//! White labeled polkadot omni-node. //! -//! 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 [chain_spec.json]` +//! For documentation, see [`polkadot_omni_node_lib`]. #![warn(missing_docs)] #![warn(unused_extern_crates)] diff --git a/docs/mermaid/IA.mmd b/docs/mermaid/IA.mmd index 0f14e200df9..dcf9806dcb6 100644 --- a/docs/mermaid/IA.mmd +++ b/docs/mermaid/IA.mmd @@ -8,6 +8,5 @@ flowchart polkadot_sdk --> substrate polkadot_sdk --> frame - polkadot_sdk --> polkadot[polkadot node] polkadot_sdk --> xcm polkadot_sdk --> templates diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index b86ce986820..0c39367eeed 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -29,6 +29,7 @@ pallet-example-offchain-worker = { workspace = true, default-features = true } # How we build docs in rust-docs simple-mermaid = "0.1.1" docify = { workspace = true } +serde_json = { workspace = true } # Polkadot SDK deps, typically all should only be in scope such that we can link to their doc item. polkadot-sdk = { features = ["runtime-full"], workspace = true, default-features = true } @@ -39,6 +40,7 @@ subkey = { workspace = true, default-features = true } frame-system = { workspace = true } frame-support = { workspace = true } frame-executive = { workspace = true } +frame-benchmarking = { workspace = true } pallet-example-authorization-tx-extension = { workspace = true, default-features = true } pallet-example-single-block-migrations = { workspace = true, default-features = true } frame-metadata-hash-extension = { workspace = true, default-features = true } @@ -70,6 +72,9 @@ cumulus-primitives-proof-size-hostfunction = { workspace = true, default-feature cumulus-client-service = { workspace = true, default-features = true } cumulus-primitives-storage-weight-reclaim = { workspace = true, default-features = true } +# Omni Node +polkadot-omni-node-lib = { workspace = true, default-features = true } + # Pallets and FRAME internals pallet-aura = { workspace = true, default-features = true } pallet-timestamp = { workspace = true, default-features = true } @@ -92,6 +97,7 @@ pallet-scheduler = { workspace = true, default-features = true } pallet-referenda = { workspace = true, default-features = true } pallet-broker = { workspace = true, default-features = true } pallet-babe = { workspace = true, default-features = true } +pallet-grandpa = { workspace = true, default-features = true } # Primitives sp-io = { workspace = true, default-features = true } @@ -106,6 +112,7 @@ sp-arithmetic = { workspace = true, default-features = true } sp-genesis-builder = { workspace = true, default-features = true } sp-offchain = { workspace = true, default-features = true } sp-version = { workspace = true, default-features = true } +sp-weights = { workspace = true, default-features = true } # XCM @@ -117,9 +124,18 @@ xcm-simulator = { workspace = true } pallet-xcm = { workspace = true } # runtime guides -chain-spec-guide-runtime = { workspace = true } + +chain-spec-guide-runtime = { workspace = true, default-features = true } # Templates -minimal-template-runtime = { workspace = true } -solochain-template-runtime = { workspace = true } -parachain-template-runtime = { workspace = true } +minimal-template-runtime = { workspace = true, default-features = true } +solochain-template-runtime = { workspace = true, default-features = true } +parachain-template-runtime = { workspace = true, default-features = true } + +# local packages +first-runtime = { workspace = true, default-features = true } +first-pallet = { workspace = true, default-features = true } + +[dev-dependencies] +assert_cmd = "2.0.14" +rand = "0.8" diff --git a/docs/sdk/assets/theme.css b/docs/sdk/assets/theme.css index 1f47a8ef5b0..f9aa4760275 100644 --- a/docs/sdk/assets/theme.css +++ b/docs/sdk/assets/theme.css @@ -6,6 +6,27 @@ --polkadot-purple: #552BBF; } +/* Light theme */ +html[data-theme="light"] { + --quote-background: #f9f9f9; + --quote-border: #ccc; + --quote-text: #333; +} + +/* Dark theme */ +html[data-theme="dark"] { + --quote-background: #333; + --quote-border: #555; + --quote-text: #f9f9f9; +} + +/* Ayu theme */ +html[data-theme="ayu"] { + --quote-background: #272822; + --quote-border: #383830; + --quote-text: #f8f8f2; +} + body.sdk-docs { nav.sidebar>div.sidebar-crate>a>img { width: 190px; @@ -20,3 +41,17 @@ body.sdk-docs { html[data-theme="light"] .sidebar-crate > .logo-container > img { content: url("https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_Black.png"); } + +/* Custom styles for blockquotes */ +blockquote { + background-color: var(--quote-background); + border-left: 5px solid var(--quote-border); + color: var(--quote-text); + margin: 1em 0; + padding: 1em 1.5em; + /* font-style: italic; */ +} + +blockquote p { + margin: 0; +} diff --git a/docs/sdk/packages/guides/first-pallet/Cargo.toml b/docs/sdk/packages/guides/first-pallet/Cargo.toml new file mode 100644 index 00000000000..dad5b886349 --- /dev/null +++ b/docs/sdk/packages/guides/first-pallet/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "polkadot-sdk-docs-first-pallet" +description = "A simple pallet created for the polkadot-sdk-docs guides" +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true } +scale-info = { workspace = true } +frame = { workspace = true, features = ["experimental", "runtime"] } +docify = { workspace = true } + +[features] +default = ["std"] +std = ["codec/std", "frame/std", "scale-info/std"] diff --git a/docs/sdk/packages/guides/first-pallet/src/lib.rs b/docs/sdk/packages/guides/first-pallet/src/lib.rs new file mode 100644 index 00000000000..168b7ca44ab --- /dev/null +++ b/docs/sdk/packages/guides/first-pallet/src/lib.rs @@ -0,0 +1,480 @@ +// 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. + +//! Pallets used in the `your_first_pallet` guide. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod shell_pallet { + use frame::prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); +} + +#[frame::pallet(dev_mode)] +pub mod pallet { + use frame::prelude::*; + + #[docify::export] + pub type Balance = u128; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[docify::export] + /// Single storage item, of type `Balance`. + #[pallet::storage] + pub type TotalIssuance = StorageValue<_, Balance>; + + #[docify::export] + /// A mapping from `T::AccountId` to `Balance` + #[pallet::storage] + pub type Balances = StorageMap<_, _, T::AccountId, Balance>; + + #[docify::export(impl_pallet)] + #[pallet::call] + impl Pallet { + /// An unsafe mint that can be called by anyone. Not a great idea. + pub fn mint_unsafe( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + // ensure that this is a signed account, but we don't really check `_anyone`. + let _anyone = ensure_signed(origin)?; + + // update the balances map. Notice how all `` remains as ``. + Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); + // update total issuance. + TotalIssuance::::mutate(|t| *t = Some(t.unwrap_or(0) + amount)); + + Ok(()) + } + + /// Transfer `amount` from `origin` to `dest`. + pub fn transfer( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + + // ensure sender has enough balance, and if so, calculate what is left after `amount`. + let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; + if sender_balance < amount { + return Err("InsufficientBalance".into()) + } + let remainder = sender_balance - amount; + + // update sender and dest balances. + Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); + Balances::::insert(&sender, remainder); + + Ok(()) + } + } + + #[allow(unused)] + impl Pallet { + #[docify::export] + pub fn transfer_better( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + + let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; + ensure!(sender_balance >= amount, "InsufficientBalance"); + let remainder = sender_balance - amount; + + // .. snip + Ok(()) + } + + #[docify::export] + /// Transfer `amount` from `origin` to `dest`. + pub fn transfer_better_checked( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + + let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; + let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?; + + // .. snip + Ok(()) + } + } + + #[cfg(any(test, doc))] + pub(crate) mod tests { + use crate::pallet::*; + + #[docify::export(testing_prelude)] + use frame::testing_prelude::*; + + pub(crate) const ALICE: u64 = 1; + pub(crate) const BOB: u64 = 2; + pub(crate) const CHARLIE: u64 = 3; + + #[docify::export] + // This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod + // tests { .. }` + mod runtime { + use super::*; + // we need to reference our `mod pallet` as an identifier to pass to + // `construct_runtime`. + // YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE + use crate::pallet as pallet_currency; + + construct_runtime!( + pub enum Runtime { + // ---^^^^^^ This is where `enum Runtime` is defined. + System: frame_system, + Currency: pallet_currency, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + // within pallet we just said `::AccountId`, now we + // finally specified it. + type AccountId = u64; + } + + // our simple pallet has nothing to be configured. + impl pallet_currency::Config for Runtime {} + } + + pub(crate) use runtime::*; + + #[allow(unused)] + #[docify::export] + fn new_test_state_basic() -> TestState { + let mut state = TestState::new_empty(); + let accounts = vec![(ALICE, 100), (BOB, 100)]; + state.execute_with(|| { + for (who, amount) in &accounts { + Balances::::insert(who, amount); + TotalIssuance::::mutate(|b| *b = Some(b.unwrap_or(0) + amount)); + } + }); + + state + } + + #[docify::export] + pub(crate) struct StateBuilder { + balances: Vec<(::AccountId, Balance)>, + } + + #[docify::export(default_state_builder)] + impl Default for StateBuilder { + fn default() -> Self { + Self { balances: vec![(ALICE, 100), (BOB, 100)] } + } + } + + #[docify::export(impl_state_builder_add)] + impl StateBuilder { + fn add_balance( + mut self, + who: ::AccountId, + amount: Balance, + ) -> Self { + self.balances.push((who, amount)); + self + } + } + + #[docify::export(impl_state_builder_build)] + impl StateBuilder { + pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) { + let mut ext = TestState::new_empty(); + ext.execute_with(|| { + for (who, amount) in &self.balances { + Balances::::insert(who, amount); + TotalIssuance::::mutate(|b| *b = Some(b.unwrap_or(0) + amount)); + } + }); + + ext.execute_with(test); + + // assertions that must always hold + ext.execute_with(|| { + assert_eq!( + Balances::::iter().map(|(_, x)| x).sum::(), + TotalIssuance::::get().unwrap_or_default() + ); + }) + } + } + + #[docify::export] + #[test] + fn first_test() { + TestState::new_empty().execute_with(|| { + // We expect Alice's account to have no funds. + assert_eq!(Balances::::get(&ALICE), None); + assert_eq!(TotalIssuance::::get(), None); + + // mint some funds into Alice's account. + assert_ok!(Pallet::::mint_unsafe( + RuntimeOrigin::signed(ALICE), + ALICE, + 100 + )); + + // re-check the above + assert_eq!(Balances::::get(&ALICE), Some(100)); + assert_eq!(TotalIssuance::::get(), Some(100)); + }) + } + + #[docify::export] + #[test] + fn state_builder_works() { + StateBuilder::default().build_and_execute(|| { + assert_eq!(Balances::::get(&ALICE), Some(100)); + assert_eq!(Balances::::get(&BOB), Some(100)); + assert_eq!(Balances::::get(&CHARLIE), None); + assert_eq!(TotalIssuance::::get(), Some(200)); + }); + } + + #[docify::export] + #[test] + fn state_builder_add_balance() { + StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| { + assert_eq!(Balances::::get(&CHARLIE), Some(42)); + assert_eq!(TotalIssuance::::get(), Some(242)); + }) + } + + #[test] + #[should_panic] + fn state_builder_duplicate_genesis_fails() { + StateBuilder::default() + .add_balance(CHARLIE, 42) + .add_balance(CHARLIE, 43) + .build_and_execute(|| { + assert_eq!(Balances::::get(&CHARLIE), None); + assert_eq!(TotalIssuance::::get(), Some(242)); + }) + } + + #[docify::export] + #[test] + fn mint_works() { + StateBuilder::default().build_and_execute(|| { + // given the initial state, when: + assert_ok!(Pallet::::mint_unsafe(RuntimeOrigin::signed(ALICE), BOB, 100)); + + // then: + assert_eq!(Balances::::get(&BOB), Some(200)); + assert_eq!(TotalIssuance::::get(), Some(300)); + + // given: + assert_ok!(Pallet::::mint_unsafe( + RuntimeOrigin::signed(ALICE), + CHARLIE, + 100 + )); + + // then: + assert_eq!(Balances::::get(&CHARLIE), Some(100)); + assert_eq!(TotalIssuance::::get(), Some(400)); + }); + } + + #[docify::export] + #[test] + fn transfer_works() { + StateBuilder::default().build_and_execute(|| { + // given the initial state, when: + assert_ok!(Pallet::::transfer(RuntimeOrigin::signed(ALICE), BOB, 50)); + + // then: + assert_eq!(Balances::::get(&ALICE), Some(50)); + assert_eq!(Balances::::get(&BOB), Some(150)); + assert_eq!(TotalIssuance::::get(), Some(200)); + + // when: + assert_ok!(Pallet::::transfer(RuntimeOrigin::signed(BOB), ALICE, 50)); + + // then: + assert_eq!(Balances::::get(&ALICE), Some(100)); + assert_eq!(Balances::::get(&BOB), Some(100)); + assert_eq!(TotalIssuance::::get(), Some(200)); + }); + } + + #[docify::export] + #[test] + fn transfer_from_non_existent_fails() { + StateBuilder::default().build_and_execute(|| { + // given the initial state, when: + assert_err!( + Pallet::::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10), + "NonExistentAccount" + ); + + // then nothing has changed. + assert_eq!(Balances::::get(&ALICE), Some(100)); + assert_eq!(Balances::::get(&BOB), Some(100)); + assert_eq!(Balances::::get(&CHARLIE), None); + assert_eq!(TotalIssuance::::get(), Some(200)); + }); + } + } +} + +#[frame::pallet(dev_mode)] +pub mod pallet_v2 { + use super::pallet::Balance; + use frame::prelude::*; + + #[docify::export(config_v2)] + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type of the runtime. + type RuntimeEvent: From> + + IsType<::RuntimeEvent> + + TryInto>; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type Balances = StorageMap<_, _, T::AccountId, Balance>; + + #[pallet::storage] + pub type TotalIssuance = StorageValue<_, Balance>; + + #[docify::export] + #[pallet::error] + pub enum Error { + /// Account does not exist. + NonExistentAccount, + /// Account does not have enough balance. + InsufficientBalance, + } + + #[docify::export] + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A transfer succeeded. + Transferred { from: T::AccountId, to: T::AccountId, amount: Balance }, + } + + #[pallet::call] + impl Pallet { + #[docify::export(transfer_v2)] + pub fn transfer( + origin: T::RuntimeOrigin, + dest: T::AccountId, + amount: Balance, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + + // ensure sender has enough balance, and if so, calculate what is left after `amount`. + let sender_balance = + Balances::::get(&sender).ok_or(Error::::NonExistentAccount)?; + let remainder = + sender_balance.checked_sub(amount).ok_or(Error::::InsufficientBalance)?; + + Balances::::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount)); + Balances::::insert(&sender, remainder); + + Self::deposit_event(Event::::Transferred { from: sender, to: dest, amount }); + + Ok(()) + } + } + + #[cfg(any(test, doc))] + pub mod tests { + use super::{super::pallet::tests::StateBuilder, *}; + use frame::testing_prelude::*; + const ALICE: u64 = 1; + const BOB: u64 = 2; + + #[docify::export] + pub mod runtime_v2 { + use super::*; + use crate::pallet_v2 as pallet_currency; + + construct_runtime!( + pub enum Runtime { + System: frame_system, + Currency: pallet_currency, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = MockBlock; + type AccountId = u64; + } + + impl pallet_currency::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + } + } + + pub(crate) use runtime_v2::*; + + #[docify::export(transfer_works_v2)] + #[test] + fn transfer_works() { + StateBuilder::default().build_and_execute(|| { + // skip the genesis block, as events are not deposited there and we need them for + // the final assertion. + System::set_block_number(ALICE); + + // given the initial state, when: + assert_ok!(Pallet::::transfer(RuntimeOrigin::signed(ALICE), BOB, 50)); + + // then: + assert_eq!(Balances::::get(&ALICE), Some(50)); + assert_eq!(Balances::::get(&BOB), Some(150)); + assert_eq!(TotalIssuance::::get(), Some(200)); + + // now we can also check that an event has been deposited: + assert_eq!( + System::read_events_for_pallet::>(), + vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }] + ); + }); + } + } +} diff --git a/docs/sdk/packages/guides/first-runtime/Cargo.toml b/docs/sdk/packages/guides/first-runtime/Cargo.toml new file mode 100644 index 00000000000..303d5c5e7f5 --- /dev/null +++ b/docs/sdk/packages/guides/first-runtime/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "polkadot-sdk-docs-first-runtime" +description = "A simple runtime created for the polkadot-sdk-docs guides" +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +codec = { workspace = true } +scale-info = { workspace = true } +serde_json = { workspace = true } + +# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. +frame = { workspace = true, features = ["experimental", "runtime"] } + +# pallets that we want to use +pallet-balances = { workspace = true } +pallet-sudo = { workspace = true } +pallet-timestamp = { workspace = true } +pallet-transaction-payment = { workspace = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true } + +# other polkadot-sdk-deps +sp-keyring = { workspace = true } + +# local pallet templates +first-pallet = { workspace = true } + +docify = { workspace = true } + +[build-dependencies] +substrate-wasm-builder = { workspace = true, optional = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "serde_json/std", + + "frame/std", + + "pallet-balances/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + + "first-pallet/std", + "sp-keyring/std", + + "substrate-wasm-builder", +] diff --git a/docs/sdk/packages/guides/first-runtime/build.rs b/docs/sdk/packages/guides/first-runtime/build.rs new file mode 100644 index 00000000000..b7676a70dfe --- /dev/null +++ b/docs/sdk/packages/guides/first-runtime/build.rs @@ -0,0 +1,27 @@ +// 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. + +fn main() { + #[cfg(feature = "std")] + { + substrate_wasm_builder::WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build(); + } +} diff --git a/docs/sdk/packages/guides/first-runtime/src/lib.rs b/docs/sdk/packages/guides/first-runtime/src/lib.rs new file mode 100644 index 00000000000..92d51962b6f --- /dev/null +++ b/docs/sdk/packages/guides/first-runtime/src/lib.rs @@ -0,0 +1,301 @@ +// 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. + +//! Runtime used in `your_first_runtime`. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; +use alloc::{vec, vec::Vec}; +use first_pallet::pallet_v2 as our_first_pallet; +use frame::{ + prelude::*, + runtime::{apis, prelude::*}, +}; +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"), + authoring_version: 1, + spec_version: 0, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + system_version: 1, +}; + +#[docify::export(cr)] +construct_runtime!( + pub struct Runtime { + // Mandatory for all runtimes + System: frame_system, + + // A number of other pallets from FRAME. + Timestamp: pallet_timestamp, + Balances: pallet_balances, + Sudo: pallet_sudo, + TransactionPayment: pallet_transaction_payment, + + // Our local pallet + FirstPallet: our_first_pallet, + } +); + +#[docify::export_content] +mod runtime_types { + use super::*; + pub(super) type SignedExtra = ( + // `frame` already provides all the signed extensions from `frame-system`. We just add the + // one related to tx-payment here. + frame::runtime::types_common::SystemTransactionExtensionsOf, + pallet_transaction_payment::ChargeTransactionPayment, + ); + + pub(super) type Block = frame::runtime::types_common::BlockOf; + pub(super) type Header = HeaderFor; + + pub(super) type RuntimeExecutive = Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + >; +} +use runtime_types::*; + +#[docify::export_content] +mod config_impls { + use super::*; + + parameter_types! { + pub const Version: RuntimeVersion = VERSION; + } + + #[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] + impl frame_system::Config for Runtime { + type Block = Block; + type Version = Version; + type AccountData = + pallet_balances::AccountData<::Balance>; + } + + #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] + impl pallet_balances::Config for Runtime { + type AccountStore = System; + } + + #[derive_impl(pallet_sudo::config_preludes::TestDefaultConfig)] + impl pallet_sudo::Config for Runtime {} + + #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] + impl pallet_timestamp::Config for Runtime {} + + #[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] + impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + // We specify a fixed length to fee here, which essentially means all transactions charge + // exactly 1 unit of fee. + type LengthToFee = FixedFee<1, ::Balance>; + type WeightToFee = NoFee<::Balance>; + } +} + +#[docify::export(our_config_impl)] +impl our_first_pallet::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +/// Provides getters for genesis configuration presets. +pub mod genesis_config_presets { + use super::*; + use crate::{ + interface::{Balance, MinimumBalance}, + BalancesConfig, RuntimeGenesisConfig, SudoConfig, + }; + 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 { + 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(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) + } + + /// List of supported presets. + #[docify::export] + pub fn preset_names() -> Vec { + vec![PresetId::from(DEV_RUNTIME_PRESET)] + } +} + +impl_runtime_apis! { + impl apis::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + RuntimeExecutive::execute_block(block) + } + + fn initialize_block(header: &Header) -> ExtrinsicInclusionMode { + RuntimeExecutive::initialize_block(header) + } + } + + impl apis::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> Vec { + Runtime::metadata_versions() + } + } + + impl apis::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ExtrinsicFor) -> ApplyExtrinsicResult { + RuntimeExecutive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> HeaderFor { + RuntimeExecutive::finalize_block() + } + + fn inherent_extrinsics(data: InherentData) -> Vec> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: InherentData, + ) -> CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl apis::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ExtrinsicFor, + block_hash: ::Hash, + ) -> TransactionValidity { + RuntimeExecutive::validate_transaction(source, tx, block_hash) + } + } + + impl apis::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &HeaderFor) { + RuntimeExecutive::offchain_worker(header) + } + } + + impl apis::SessionKeys for Runtime { + fn generate_session_keys(_seed: Option>) -> Vec { + Default::default() + } + + fn decode_session_keys( + _encoded: Vec, + ) -> Option, apis::KeyTypeId)>> { + Default::default() + } + } + + impl apis::AccountNonceApi for Runtime { + fn account_nonce(account: interface::AccountId) -> interface::Nonce { + System::account_nonce(account) + } + } + + impl apis::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> GenesisBuilderResult { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, self::genesis_config_presets::get_preset) + } + + fn preset_names() -> Vec { + crate::genesis_config_presets::preset_names() + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< + Block, + interface::Balance, + > for Runtime { + fn query_info(uxt: ExtrinsicFor, len: u32) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details(uxt: ExtrinsicFor, len: u32) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> interface::Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> interface::Balance { + TransactionPayment::length_to_fee(length) + } + } +} + +/// Just a handy re-definition of some types based on what is already provided to the pallet +/// configs. +pub mod interface { + use super::Runtime; + use frame::prelude::frame_system; + + pub type AccountId = ::AccountId; + pub type Nonce = ::Nonce; + pub type Hash = ::Hash; + pub type Balance = ::Balance; + pub type MinimumBalance = ::ExistentialDeposit; +} diff --git a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs index 812e674d163..2339088abed 100644 --- a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs +++ b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs @@ -70,10 +70,11 @@ //! - Ensure enough coretime is assigned to the parachain. For maximum throughput the upper bound is //! 3 cores. //! -//!
Phase 1 is not needed if using the polkadot-parachain binary -//! built from the latest polkadot-sdk release! Simply pass the -//! --experimental-use-slot-based parameter to the command line and jump to Phase -//! 2.
+//!
Phase 1 is NOT needed if using the polkadot-parachain or +//! polkadot-omni-node binary, or polkadot-omni-node-lib built from the +//! latest polkadot-sdk release! Simply pass the --experimental-use-slot-based +//! ([`polkadot_omni_node_lib::cli::Cli::experimental_use_slot_based`]) parameter to the command +//! line and jump to Phase 2.
//! //! The following steps assume using the cumulus parachain template. //! diff --git a/docs/sdk/src/guides/mod.rs b/docs/sdk/src/guides/mod.rs index a7fd146ccdf..747128a728d 100644 --- a/docs/sdk/src/guides/mod.rs +++ b/docs/sdk/src/guides/mod.rs @@ -1,14 +1,22 @@ //! # Polkadot SDK Docs Guides //! -//! This crate contains a collection of guides that are foundational to the developers of Polkadot -//! SDK. They are common user-journeys that are traversed in the Polkadot ecosystem. +//! This crate contains a collection of guides that are foundational to the developers of +//! Polkadot SDK. They are common user-journeys that are traversed in the Polkadot ecosystem. //! -//! 1. [`crate::guides::your_first_pallet`] is your starting point with Polkadot SDK. It contains -//! the basics of -//! building a simple crypto currency with FRAME. -//! 2. [`crate::guides::your_first_runtime`] is the next step in your journey. It contains the -//! basics of building a runtime that contains this pallet, plus a few common pallets from FRAME. +//! The main user-journey covered by these guides is: //! +//! * [`your_first_pallet`], where you learn what a FRAME pallet is, and write your first +//! application logic. +//! * [`your_first_runtime`], where you learn how to compile your pallets into a WASM runtime. +//! * [`your_first_node`], where you learn how to run the said runtime in a node. +//! +//! > By this step, you have already launched a full Polkadot-SDK-based blockchain! +//! +//! Once done, feel free to step up into one of our templates: [`crate::polkadot_sdk::templates`]. +//! +//! [`your_first_pallet`]: crate::guides::your_first_pallet +//! [`your_first_runtime`]: crate::guides::your_first_runtime +//! [`your_first_node`]: crate::guides::your_first_node //! //! Other guides are related to other miscellaneous topics and are listed as modules below. @@ -19,19 +27,12 @@ pub mod your_first_pallet; /// compiling it to [WASM](crate::polkadot_sdk::substrate#wasm-build). pub mod your_first_runtime; -// /// Running the given runtime with a node. No specific consensus mechanism is used at this stage. -// TODO -// pub mod your_first_node; - -// /// How to enhance a given runtime and node to be cumulus-enabled, run it as a parachain -// /// and connect it to a relay-chain. -// TODO -// pub mod cumulus_enabled_parachain; +/// Running the given runtime with a node. No specific consensus mechanism is used at this stage. +pub mod your_first_node; -// /// How to make a given runtime XCM-enabled, capable of sending messages (`Transact`) between -// /// itself and the relay chain to which it is connected. -// TODO -// pub mod xcm_enabled_parachain; +/// How to enhance a given runtime and node to be cumulus-enabled, run it as a parachain +/// and connect it to a relay-chain. +// pub mod your_first_parachain; /// How to enable storage weight reclaiming in a parachain node and runtime. pub mod enable_pov_reclaim; diff --git a/docs/sdk/src/guides/your_first_node.rs b/docs/sdk/src/guides/your_first_node.rs index d12349c9906..da37c11c206 100644 --- a/docs/sdk/src/guides/your_first_node.rs +++ b/docs/sdk/src/guides/your_first_node.rs @@ -1 +1,314 @@ //! # Your first Node +//! +//! In this guide, you will learn how to run a runtime, such as the one created in +//! [`your_first_runtime`], in a node. Within the context of this guide, we will focus on running +//! the runtime with an [`omni-node`]. Please first read this page to learn about the OmniNode, and +//! other options when it comes to running a node. +//! +//! [`your_first_runtime`] is a runtime with no consensus related code, and therefore can only be +//! executed with a node that also expects no consensus ([`sc_consensus_manual_seal`]). +//! `polkadot-omni-node`'s [`--dev-block-time`] precisely does this. +//! +//! > All of the following steps are coded as unit tests of this module. Please see `Source` of the +//! > page for more information. +//! +//! ## Running The Omni Node +//! +//! ### Installs +//! +//! The `polkadot-omni-node` can either be downloaded from the latest [Release](https://github.com/paritytech/polkadot-sdk/releases/) of `polkadot-sdk`, +//! or installed using `cargo`: +//! +//! ```text +//! cargo install polkadot-omni-node +//! ``` +//! +//! Next, we need to install the [`chain-spec-builder`]. This is the tool that allows us to build +//! chain-specifications, through interacting with the genesis related APIs of the runtime, as +//! described in [`crate::guides::your_first_runtime#genesis-configuration`]. +//! +//! ```text +//! cargo install staging-chain-spec-builder +//! ``` +//! +//! > The name of the crate is prefixed with `staging` as the crate name `chain-spec-builder` on +//! > crates.io is already taken and is not controlled by `polkadot-sdk` developers. +//! +//! ### Building Runtime +//! +//! Next, we need to build the corresponding runtime that we wish to interact with. +//! +//! ```text +//! cargo build --release -p path-to-runtime +//! ``` +//! Equivalent code in tests: +#![doc = docify::embed!("./src/guides/your_first_node.rs", build_runtime)] +//! +//! This creates the wasm file under `./target/{release}/wbuild/release` directory. +//! +//! ### Building Chain Spec +//! +//! Next, we can generate the corresponding chain-spec file. For this example, we will use the +//! `development` (`sp_genesis_config::DEVELOPMENT`) preset. +//! +//! Note that we intend to run this chain-spec with `polkadot-omni-node`, which is tailored for +//! running parachains. This requires the chain-spec to always contain the `para_id` and a +//! `relay_chain` fields, which are provided below as CLI arguments. +//! +//! ```text +//! chain-spec-builder \ +//! -c \ +//! create \ +//! --para-id 42 \ +//! --relay-chain dontcare \ +//! --runtime polkadot_sdk_docs_first_runtime.wasm \ +//! named-preset development +//! ``` +//! +//! Equivalent code in tests: +#![doc = docify::embed!("./src/guides/your_first_node.rs", csb)] +//! +//! +//! ### Running `polkadot-omni-node` +//! +//! Finally, we can run the node with the generated chain-spec file. We can also specify the block +//! time using the `--dev-block-time` flag. +//! +//! ```text +//! polkadot-omni-node \ +//! --tmp \ +//! --dev-block-time 1000 \ +//! --chain .json +//! ``` +//! +//! > Note that we always prefer to use `--tmp` for testing, as it will save the chain state to a +//! > temporary folder, allowing the chain-to be easily restarted without `purge-chain`. See +//! > [`sc_cli::commands::PurgeChainCmd`] and [`sc_cli::commands::RunCmd::tmp`] for more info. +//! +//! This will start the node and import the blocks. Note while using `--dev-block-time`, the node +//! will use the testing-specific manual-seal consensus. This is an efficient way to test the +//! application logic of your runtime, without needing to yet care about consensus, block +//! production, relay-chain and so on. +//! +//! ### Next Steps +//! +//! * See the rest of the steps in [`crate::reference_docs::omni_node#user-journey`]. +//! +//! [`runtime`]: crate::reference_docs::glossary#runtime +//! [`node`]: crate::reference_docs::glossary#node +//! [`build_config`]: first_runtime::Runtime#method.build_config +//! [`omni-node`]: crate::reference_docs::omni_node +//! [`--dev-block-time`]: (polkadot_omni_node_lib::cli::Cli::dev_block_time) + +#[cfg(test)] +mod tests { + use assert_cmd::Command; + use rand::Rng; + use sc_chain_spec::{DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET}; + use sp_genesis_builder::PresetId; + use std::path::PathBuf; + + const PARA_RUNTIME: &'static str = "parachain-template-runtime"; + const FIRST_RUNTIME: &'static str = "polkadot-sdk-docs-first-runtime"; + const MINIMAL_RUNTIME: &'static str = "minimal-template-runtime"; + + const CHAIN_SPEC_BUILDER: &'static str = "chain-spec-builder"; + const OMNI_NODE: &'static str = "polkadot-omni-node"; + + fn cargo() -> Command { + Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())) + } + + fn get_target_directory() -> Option { + let output = cargo().arg("metadata").arg("--format-version=1").output().ok()?; + + if !output.status.success() { + return None; + } + + let metadata: serde_json::Value = serde_json::from_slice(&output.stdout).ok()?; + let target_directory = metadata["target_directory"].as_str()?; + + Some(PathBuf::from(target_directory)) + } + + fn find_release_binary(name: &str) -> Option { + let target_dir = get_target_directory()?; + let release_path = target_dir.join("release").join(name); + + if release_path.exists() { + Some(release_path) + } else { + None + } + } + + fn find_wasm(runtime_name: &str) -> Option { + let target_dir = get_target_directory()?; + let wasm_path = target_dir + .join("release") + .join("wbuild") + .join(runtime_name) + .join(format!("{}.wasm", runtime_name.replace('-', "_"))); + + if wasm_path.exists() { + Some(wasm_path) + } else { + None + } + } + + fn maybe_build_runtimes() { + if find_wasm(&PARA_RUNTIME).is_none() { + println!("Building parachain-template-runtime..."); + Command::new("cargo") + .arg("build") + .arg("--release") + .arg("-p") + .arg(PARA_RUNTIME) + .assert() + .success(); + } + if find_wasm(&FIRST_RUNTIME).is_none() { + println!("Building polkadot-sdk-docs-first-runtime..."); + #[docify::export_content] + fn build_runtime() { + Command::new("cargo") + .arg("build") + .arg("--release") + .arg("-p") + .arg(FIRST_RUNTIME) + .assert() + .success(); + } + build_runtime() + } + + assert!(find_wasm(PARA_RUNTIME).is_some()); + assert!(find_wasm(FIRST_RUNTIME).is_some()); + } + + fn maybe_build_chain_spec_builder() { + if find_release_binary(CHAIN_SPEC_BUILDER).is_none() { + println!("Building chain-spec-builder..."); + Command::new("cargo") + .arg("build") + .arg("--release") + .arg("-p") + .arg("staging-chain-spec-builder") + .assert() + .success(); + } + assert!(find_release_binary(CHAIN_SPEC_BUILDER).is_some()); + } + + fn maybe_build_omni_node() { + if find_release_binary(OMNI_NODE).is_none() { + println!("Building polkadot-omni-node..."); + Command::new("cargo") + .arg("build") + .arg("--release") + .arg("-p") + .arg("polkadot-omni-node") + .assert() + .success(); + } + } + + fn test_runtime_preset(runtime: &'static str, block_time: u64, maybe_preset: Option) { + sp_tracing::try_init_simple(); + maybe_build_runtimes(); + maybe_build_chain_spec_builder(); + maybe_build_omni_node(); + + let chain_spec_builder = + find_release_binary(&CHAIN_SPEC_BUILDER).expect("we built it above; qed"); + let omni_node = find_release_binary(OMNI_NODE).expect("we built it above; qed"); + let runtime_path = find_wasm(runtime).expect("we built it above; qed"); + + let random_seed: u32 = rand::thread_rng().gen(); + let chain_spec_file = std::env::current_dir() + .unwrap() + .join(format!("{}_{}_{}.json", runtime, block_time, random_seed)); + + Command::new(chain_spec_builder) + .args(["-c", chain_spec_file.to_str().unwrap()]) + .arg("create") + .args(["--para-id", "1000", "--relay-chain", "dontcare"]) + .args(["-r", runtime_path.to_str().unwrap()]) + .args(match maybe_preset { + Some(preset) => vec!["named-preset".to_string(), preset.to_string()], + None => vec!["default".to_string()], + }) + .assert() + .success(); + + let output = Command::new(omni_node) + .arg("--tmp") + .args(["--chain", chain_spec_file.to_str().unwrap()]) + .args(["--dev-block-time", block_time.to_string().as_str()]) + .timeout(std::time::Duration::from_secs(10)) + .output() + .unwrap(); + + std::fs::remove_file(chain_spec_file).unwrap(); + + // uncomment for debugging. + // println!("output: {:?}", output); + + let expected_blocks = (10_000 / block_time).saturating_div(2); + assert!(expected_blocks > 0, "test configuration is bad, should give it more time"); + assert!(String::from_utf8(output.stderr) + .unwrap() + .contains(format!("Imported #{}", expected_blocks).to_string().as_str())); + } + + #[test] + fn works_with_different_block_times() { + test_runtime_preset(PARA_RUNTIME, 100, Some(DEV_RUNTIME_PRESET.into())); + test_runtime_preset(PARA_RUNTIME, 3000, Some(DEV_RUNTIME_PRESET.into())); + + // we need this snippet just for docs + #[docify::export_content(csb)] + fn build_para_chain_spec_works() { + let chain_spec_builder = find_release_binary(&CHAIN_SPEC_BUILDER).unwrap(); + let runtime_path = find_wasm(PARA_RUNTIME).unwrap(); + let output = "/tmp/demo-chain-spec.json"; + Command::new(chain_spec_builder) + .args(["-c", output]) + .arg("create") + .args(["--para-id", "1000", "--relay-chain", "dontcare"]) + .args(["-r", runtime_path.to_str().unwrap()]) + .args(["named-preset", "development"]) + .assert() + .success(); + std::fs::remove_file(output).unwrap(); + } + build_para_chain_spec_works(); + } + + #[test] + fn parachain_runtime_works() { + // TODO: None doesn't work. But maybe it should? it would be misleading as many users might + // use it. + [Some(DEV_RUNTIME_PRESET.into()), Some(LOCAL_TESTNET_RUNTIME_PRESET.into())] + .into_iter() + .for_each(|preset| { + test_runtime_preset(PARA_RUNTIME, 1000, preset); + }); + } + + #[test] + fn minimal_runtime_works() { + [None, Some(DEV_RUNTIME_PRESET.into())].into_iter().for_each(|preset| { + test_runtime_preset(MINIMAL_RUNTIME, 1000, preset); + }); + } + + #[test] + fn guide_first_runtime_works() { + [Some(DEV_RUNTIME_PRESET.into())].into_iter().for_each(|preset| { + test_runtime_preset(FIRST_RUNTIME, 1000, preset); + }); + } +} diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs index fcfaab00e55..aef8981b4d4 100644 --- a/docs/sdk/src/guides/your_first_pallet/mod.rs +++ b/docs/sdk/src/guides/your_first_pallet/mod.rs @@ -47,7 +47,7 @@ //! //! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any //! pallet. Refer to the documentation of each to get an overview of what they do. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", shell_pallet)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", shell_pallet)] //! //! All of the code that follows in this guide should live inside of the `mod pallet`. //! @@ -61,17 +61,17 @@ //! > For the rest of this guide, we will opt for a balance type of `u128`. For the sake of //! > simplicity, we are hardcoding this type. In a real pallet is best practice to define it as a //! > generic bounded type in the `Config` trait, and then specify it in the implementation. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Balance)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balance)] //! //! The definition of these two storage items, based on [`pallet::storage`] details, is as follows: -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", TotalIssuance)] -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Balances)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", TotalIssuance)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balances)] //! //! ### Dispatchables //! //! Next, we will define the dispatchable functions. As per [`pallet::call`], these will be defined //! as normal `fn`s attached to `struct Pallet`. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", impl_pallet)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_pallet)] //! //! The logic of these functions is self-explanatory. Instead, we will focus on the FRAME-related //! details: @@ -108,14 +108,14 @@ //! How we handle error in the above snippets is fairly rudimentary. Let's look at how this can be //! improved. First, we can use [`frame::prelude::ensure`] to express the error slightly better. //! This macro will call `.into()` under the hood. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_better)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better)] //! //! Moreover, you will learn in the [Defensive Programming //! section](crate::reference_docs::defensive_programming) that it is always recommended to use //! safe arithmetic operations in your runtime. By using [`frame::traits::CheckedSub`], we can not //! only take a step in that direction, but also improve the error handing and make it slightly more //! ergonomic. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_better_checked)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better_checked)] //! //! This is more or less all the logic that there is in this basic currency pallet! //! @@ -145,7 +145,7 @@ //! through [`frame::runtime::prelude::construct_runtime`]. All runtimes also have to include //! [`frame::prelude::frame_system`]. So we expect to see a runtime with two pallet, `frame_system` //! and the one we just wrote. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", runtime)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime)] //! //! > [`frame::pallet_macros::derive_impl`] is a FRAME feature that enables developers to have //! > defaults for associated types. @@ -182,7 +182,7 @@ //! to learn is that all of your pallet testing code should be wrapped in //! [`frame::testing_prelude::TestState`]. This is a type that provides access to an in-memory state //! to be used in our tests. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", first_test)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", first_test)] //! //! In the first test, we simply assert that there is no total issuance, and no balance associated //! with Alice's account. Then, we mint some balance into Alice's, and re-check. @@ -206,16 +206,16 @@ //! //! Let's see how we can implement a better test setup using this pattern. First, we define a //! `struct StateBuilder`. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", StateBuilder)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", StateBuilder)] //! //! This struct is meant to contain the same list of accounts and balances that we want to have at //! the beginning of each block. We hardcoded this to `let accounts = vec![(ALICE, 100), (2, 100)];` //! so far. Then, if desired, we attach a default value for this struct. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", default_state_builder)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", default_state_builder)] //! //! Like any other builder pattern, we attach functions to the type to mutate its internal //! properties. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", impl_state_builder_add)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_add)] //! //! Finally --the useful part-- we write our own custom `build_and_execute` function on //! this type. This function will do multiple things: @@ -227,23 +227,23 @@ //! after each test. For example, in this test, we do some additional checking about the //! correctness of the `TotalIssuance`. We leave it up to you as an exercise to learn why the //! assertion should always hold, and how it is checked. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", impl_state_builder_build)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_build)] //! //! We can write tests that specifically check the initial state, and making sure our `StateBuilder` //! is working exactly as intended. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", state_builder_works)] -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", state_builder_add_balance)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_works)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_add_balance)] //! //! ### More Tests //! //! Now that we have a more ergonomic test setup, let's see how a well written test for transfer and //! mint would look like. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_works)] -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", mint_works)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_works)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", mint_works)] //! //! It is always a good idea to build a mental model where you write *at least* one test for each //! "success path" of a dispatchable, and one test for each "failure path", such as: -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_from_non_existent_fails)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_from_non_existent_fails)] //! //! We leave it up to you to write a test that triggers the `InsufficientBalance` error. //! @@ -272,8 +272,8 @@ //! With the explanation out of the way, let's see how these components can be added. Both follow a //! fairly familiar syntax: normal Rust enums, with extra [`pallet::event`] and [`pallet::error`] //! attributes attached. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Event)] -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Error)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Event)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Error)] //! //! One slightly custom part of this is the [`pallet::generate_deposit`] part. Without going into //! too much detail, in order for a pallet to emit events to the rest of the system, it needs to do @@ -288,17 +288,17 @@ //! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME //! provides a default way of storing events, and this is what [`pallet::generate_deposit`] is //! doing. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", config_v2)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", config_v2)] //! //! > These `Runtime*` types are better explained in //! > [`crate::reference_docs::frame_runtime_types`]. //! //! Then, we can rewrite the `transfer` dispatchable as such: -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_v2)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_v2)] //! //! Then, notice how now we would need to provide this `type RuntimeEvent` in our test runtime //! setup. -#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", runtime_v2)] +#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime_v2)] //! //! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent = //! RuntimeEvent`) is generated by diff --git a/docs/sdk/src/guides/your_first_pallet/with_event.rs b/docs/sdk/src/guides/your_first_pallet/with_event.rs deleted file mode 100644 index a5af29c9c31..00000000000 --- a/docs/sdk/src/guides/your_first_pallet/with_event.rs +++ /dev/null @@ -1,101 +0,0 @@ -#[frame::pallet(dev_mode)] -pub mod pallet { - use frame::prelude::*; - - #[docify::export] - pub type Balance = u128; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[docify::export] - /// Single storage item, of type `Balance`. - #[pallet::storage] - pub type TotalIssuance = StorageValue<_, Balance>; - - #[docify::export] - /// A mapping from `T::AccountId` to `Balance` - #[pallet::storage] - pub type Balances = StorageMap<_, _, T::AccountId, Balance>; - - #[docify::export(impl_pallet)] - #[pallet::call] - impl Pallet { - /// An unsafe mint that can be called by anyone. Not a great idea. - pub fn mint_unsafe( - origin: T::RuntimeOrigin, - dest: T::AccountId, - amount: Balance, - ) -> DispatchResult { - // ensure that this is a signed account, but we don't really check `_anyone`. - let _anyone = ensure_signed(origin)?; - - // update the balances map. Notice how all `` remains as ``. - Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); - // update total issuance. - TotalIssuance::::mutate(|t| *t = Some(t.unwrap_or(0) + amount)); - - Ok(()) - } - - /// Transfer `amount` from `origin` to `dest`. - pub fn transfer( - origin: T::RuntimeOrigin, - dest: T::AccountId, - amount: Balance, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - // ensure sender has enough balance, and if so, calculate what is left after `amount`. - let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - if sender_balance < amount { - return Err("NotEnoughBalance".into()) - } - let remainder = sender_balance - amount; - - // update sender and dest balances. - Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); - Balances::::insert(&sender, remainder); - - Ok(()) - } - } - - #[allow(unused)] - impl Pallet { - #[docify::export] - pub fn transfer_better( - origin: T::RuntimeOrigin, - dest: T::AccountId, - amount: Balance, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - ensure!(sender_balance >= amount, "NotEnoughBalance"); - let remainder = sender_balance - amount; - - // .. snip - Ok(()) - } - - #[docify::export] - /// Transfer `amount` from `origin` to `dest`. - pub fn transfer_better_checked( - origin: T::RuntimeOrigin, - dest: T::AccountId, - amount: Balance, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - let remainder = sender_balance.checked_sub(amount).ok_or("NotEnoughBalance")?; - - // .. snip - Ok(()) - } - } -} diff --git a/docs/sdk/src/guides/your_first_runtime.rs b/docs/sdk/src/guides/your_first_runtime.rs index c58abc1120c..79f01e66979 100644 --- a/docs/sdk/src/guides/your_first_runtime.rs +++ b/docs/sdk/src/guides/your_first_runtime.rs @@ -1,3 +1,170 @@ //! # Your first Runtime //! -//! 🚧 +//! This guide will walk you through the steps to add your pallet to a runtime. +//! +//! The good news is, in [`crate::guides::your_first_pallet`], we have already created a _test_ +//! runtime that was used for testing, and a real runtime is not that much different! +//! +//! ## Setup +//! +//! A runtime shares a few similar setup requirements as with a pallet: +//! +//! * importing [`frame`], [`codec`], and [`scale_info`] crates. +//! * following the [`std` feature-gating](crate::polkadot_sdk::substrate#wasm-build) pattern. +//! +//! But, more specifically, it also contains: +//! +//! * a `build.rs` that uses [`substrate_wasm_builder`]. This entails declaring +//! `[build-dependencies]` in the Cargo manifest file: +//! +//! ```ignore +//! [build-dependencies] +//! substrate-wasm-builder = { ... } +//! ``` +//! +//! >Note that a runtime must always be one-runtime-per-crate. You cannot define multiple runtimes +//! per rust crate. +//! +//! You can find the full code of this guide in [`first_runtime`]. +//! +//! ## Your First Runtime +//! +//! The first new property of a real runtime that it must define its +//! [`frame::runtime::prelude::RuntimeVersion`]: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", VERSION)] +//! +//! The version contains a number of very important fields, such as `spec_version` and `spec_name` +//! that play an important role in identifying your runtime and its version, more importantly in +//! runtime upgrades. More about runtime upgrades in +//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`]. +//! +//! Then, a real runtime also contains the `impl` of all individual pallets' `trait Config` for +//! `struct Runtime`, and a [`frame::runtime::prelude::construct_runtime`] macro that amalgamates +//! them all. +//! +//! In the case of our example: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", our_config_impl)] +//! +//! In this example, we bring in a number of other pallets from [`frame`] into the runtime, each of +//! their `Config` need to be implemented for `struct Runtime`: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", config_impls)] +//! +//! Notice how we use [`frame::pallet_macros::derive_impl`] to provide "default" configuration items +//! for each pallet. Feel free to dive into the definition of each default prelude (eg. +//! [`frame::prelude::frame_system::pallet::config_preludes`]) to learn more which types are exactly +//! used. +//! +//! Recall that in test runtime in [`crate::guides::your_first_pallet`], we provided `type AccountId +//! = u64` to `frame_system`, while in this case we rely on whatever is provided by +//! [`SolochainDefaultConfig`], which is indeed a "real" 32 byte account id. +//! +//! Then, a familiar instance of `construct_runtime` amalgamates all of the pallets: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", cr)] +//! +//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that every (real) runtime needs to +//! implement a set of runtime APIs that will then let the node to communicate with it. The final +//! steps of crafting a runtime are related to achieving exactly this. +//! +//! First, we define a number of types that eventually lead to the creation of an instance of +//! [`frame::runtime::prelude::Executive`]. The executive is a handy FRAME utility that, through +//! amalgamating all pallets and further types, implements some of the very very core pieces of the +//! runtime logic, such as how blocks are executed and other runtime-api implementations. +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", runtime_types)] +//! +//! Finally, we use [`frame::runtime::prelude::impl_runtime_apis`] to implement all of the runtime +//! APIs that the runtime wishes to expose. As you will see in the code, most of these runtime API +//! implementations are merely forwarding calls to `RuntimeExecutive` which handles the actual +//! logic. Given that the implementation block is somewhat large, we won't repeat it here. You can +//! look for `impl_runtime_apis!` in [`first_runtime`]. +//! +//! ```ignore +//! impl_runtime_apis! { +//! impl apis::Core for Runtime { +//! fn version() -> RuntimeVersion { +//! VERSION +//! } +//! +//! fn execute_block(block: Block) { +//! RuntimeExecutive::execute_block(block) +//! } +//! +//! fn initialize_block(header: &Header) -> ExtrinsicInclusionMode { +//! RuntimeExecutive::initialize_block(header) +//! } +//! } +//! +//! // many more trait impls... +//! } +//! ``` +//! +//! And that more or less covers the details of how you would write a real runtime! +//! +//! Once you compile a crate that contains a runtime as above, simply running `cargo build` will +//! generate the wasm blobs and place them under `./target/release/wbuild`, as explained +//! [here](crate::polkadot_sdk::substrate#wasm-build). +//! +//! ## Genesis Configuration +//! +//! Every runtime specifies a number of runtime APIs that help the outer world (most notably, a +//! `node`) know what is the genesis state of this runtime. These APIs are then used to generate +//! what is known as a **Chain Specification, or chain spec for short**. A chain spec is the +//! primary way to run a new chain. +//! +//! These APIs are defined in [`sp_genesis_builder`], and are re-exposed as a part of +//! [`frame::runtime::apis`]. Therefore, the implementation blocks can be found inside of +//! `impl_runtime_apis!` similar to: +//! +//! ```ignore +//! impl_runtime_apis! { +//! impl apis::GenesisBuilder for Runtime { +//! fn build_state(config: Vec) -> GenesisBuilderResult { +//! build_state::(config) +//! } +//! +//! fn get_preset(id: &Option) -> Option> { +//! get_preset::(id, self::genesis_config_presets::get_preset) +//! } +//! +//! fn preset_names() -> Vec { +//! crate::genesis_config_presets::preset_names() +//! } +//! } +//! +//! } +//! ``` +//! +//! The implementation of these function can naturally vary from one runtime to the other, but the +//! overall pattern is common. For the case of this runtime, we do the following: +//! +//! 1. Expose one non-default preset, namely [`sp_genesis_builder::DEV_RUNTIME_PRESET`]. This means +//! our runtime has two "presets" of genesis state in total: `DEV_RUNTIME_PRESET` and `None`. +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", preset_names)] +//! +//! For `build_state` and `get_preset`, we use the helper functions provide by frame: +//! +//! * [`frame::runtime::prelude::build_state`] and [`frame::runtime::prelude::get_preset`]. +//! +//! Indeed, our runtime needs to specify what its `DEV_RUNTIME_PRESET` genesis state should be like: +#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", development_config_genesis)] +//! +//! For more in-depth information about `GenesisConfig`, `ChainSpec`, the `GenesisBuilder` API and +//! `chain-spec-builder`, see [`crate::reference_docs::chain_spec_genesis`]. +//! +//! ## Next Step +//! +//! See [`crate::guides::your_first_node`]. +//! +//! ## Further Reading +//! +//! 1. To learn more about signed extensions, see [`crate::reference_docs::signed_extensions`]. +//! 2. `AllPalletsWithSystem` is also generated by `construct_runtime`, as explained in +//! [`crate::reference_docs::frame_runtime_types`]. +//! 3. `Executive` supports more generics, most notably allowing the runtime to configure more +//! runtime migrations, as explained in +//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`]. +//! 4. Learn more about adding and implementing runtime apis in +//! [`crate::reference_docs::custom_runtime_api_rpc`]. +//! 5. To see a complete example of a runtime+pallet that is similar to this guide, please see +//! [`crate::polkadot_sdk::templates`]. +//! +//! [`SolochainDefaultConfig`]: struct@frame_system::pallet::config_preludes::SolochainDefaultConfig diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index 86ca677d7ce..e2c5fc93479 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -12,7 +12,8 @@ //! - Start by learning about the the [`polkadot_sdk`], its structure and context. //! - Then, head over to the [`guides`]. This modules contains in-depth guides about the most //! important user-journeys of the Polkadot SDK. -//! - Whilst reading the guides, you might find back-links to [`reference_docs`]. +//! - Whilst reading the guides, you might find back-links to [`reference_docs`]. +//! - [`external_resources`] for a list of 3rd party guides and tutorials. //! - Finally, is the parent website of this crate that contains the //! list of further tools related to the Polkadot SDK. //! diff --git a/docs/sdk/src/meta_contributing.rs b/docs/sdk/src/meta_contributing.rs index e1297151b23..d68d9bca18b 100644 --- a/docs/sdk/src/meta_contributing.rs +++ b/docs/sdk/src/meta_contributing.rs @@ -69,7 +69,8 @@ //! > what topics are already covered in this crate, and how you can build on top of the information //! > that they already pose, rather than repeating yourself**. //! -//! For more details see the [latest documenting guidelines](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/DOCUMENTATION_GUIDELINES.md). +//! For more details see the [latest documenting +//! guidelines](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/DOCUMENTATION_GUIDELINES.md). //! //! #### Example: Explaining `#[pallet::call]` //! @@ -132,6 +133,13 @@ //! compromise, but in the long term, we should work towards finding a way to maintain different //! revisions of this crate. //! +//! ## Versioning +//! +//! So long as not deployed in `crates.io`, please notice that all of the information in this crate, +//! namely in [`crate::guides`] and such are compatible with the master branch of `polkadot-sdk`. A +//! few solutions have been proposed to improve this, please see +//! [here](https://github.com/paritytech/polkadot-sdk/issues/6191). +//! //! ## How to Develop Locally //! //! To view the docs specific [`crate`] locally for development, including the correct HTML headers diff --git a/docs/sdk/src/polkadot_sdk/mod.rs b/docs/sdk/src/polkadot_sdk/mod.rs index c089b6729ce..bf7346b871a 100644 --- a/docs/sdk/src/polkadot_sdk/mod.rs +++ b/docs/sdk/src/polkadot_sdk/mod.rs @@ -75,6 +75,26 @@ //! runtimes are located under the //! [`polkadot-fellows/runtimes`](https://github.com/polkadot-fellows/runtimes) repository. //! +//! ### Binaries +//! +//! The main binaries that are part of the Polkadot SDK are: + +//! * [`polkadot`]: The Polkadot relay chain node binary, as noted above. +//! * [`polkadot-omni-node`]: A white-labeled parachain collator node. See more in +//! [`crate::reference_docs::omni_node`]. +//! * [`polkadot-parachain-bin`]: The collator node used to run collators for all Polkadot system +//! parachains. +//! * [`frame-omni-bencher`]: a benchmarking tool for FRAME-based runtimes. Nodes typically contain +//! a +//! `benchmark` subcommand that does the same. +//! * [`chain_spec_builder`]: Utility to build chain-specs Nodes typically contain a `build-spec` +//! subcommand that does the same. +//! * [`subkey`]: Substrate's key management utility. +//! * [`substrate-node`](node_cli) is an extensive substrate node that contains the superset of all +//! runtime and node side features. The corresponding runtime, called [`kitchensink_runtime`] +//! contains all of the modules that are provided with `FRAME`. This node and runtime is only used +//! for testing and demonstration. +//! //! ### Summary //! //! The following diagram summarizes how some of the components of Polkadot SDK work together: @@ -116,6 +136,9 @@ //! [`cumulus`]: crate::polkadot_sdk::cumulus //! [`polkadot`]: crate::polkadot_sdk::polkadot //! [`xcm`]: crate::polkadot_sdk::xcm +//! [`frame-omni-bencher`]: https://crates.io/crates/frame-omni-bencher +//! [`polkadot-parachain-bin`]: https://crates.io/crates/polkadot-parachain-bin +//! [`polkadot-omni-node`]: https://crates.io/crates/polkadot-omni-node /// Learn about Cumulus, the framework that transforms [`substrate`]-based chains into /// [`polkadot`]-enabled parachains. diff --git a/docs/sdk/src/polkadot_sdk/substrate.rs b/docs/sdk/src/polkadot_sdk/substrate.rs index 56b89f8c9c2..ed654c2842c 100644 --- a/docs/sdk/src/polkadot_sdk/substrate.rs +++ b/docs/sdk/src/polkadot_sdk/substrate.rs @@ -90,22 +90,6 @@ //! //! In order to ensure that the WASM build is **deterministic**, the [Substrate Runtime Toolbox (srtool)](https://github.com/paritytech/srtool) can be used. //! -//! ### Binaries -//! -//! Multiple binaries are shipped with substrate, the most important of which are located in the -//! [`./bin`](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/bin) folder. -//! -//! * [`node_cli`] is an extensive substrate node that contains the superset of all runtime and node -//! side features. The corresponding runtime, called [`kitchensink_runtime`] contains all of the -//! modules that are provided with `FRAME`. This node and runtime is only used for testing and -//! demonstration. -//! * [`chain_spec_builder`]: Utility to build more detailed chain-specs for the aforementioned -//! node. Other projects typically contain a `build-spec` subcommand that does the same. -//! * [`node_template`](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/bin/node): -//! a template node that contains a minimal set of features and can act as a starting point of a -//! project. -//! * [`subkey`]: Substrate's key management utility. -//! //! ### Anatomy of a Binary Crate //! //! From the above, [`node_cli`]/[`kitchensink_runtime`] and `node-template` are essentially diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index a2e22d1ed1e..3326f433f28 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -152,10 +152,13 @@ //! presets and build the chain specification file. It is possible to use the tool with the //! [_demonstration runtime_][`chain_spec_guide_runtime`]. To build the required packages, just run //! the following command: +//! //! ```ignore //! cargo build -p staging-chain-spec-builder -p chain-spec-guide-runtime --release //! ``` +//! //! The `chain-spec-builder` util can also be installed with `cargo install`: +//! //! ```ignore //! cargo install staging-chain-spec-builder //! cargo build -p chain-spec-guide-runtime --release diff --git a/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs b/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs index cf9e5879149..68d7d31f67f 100644 --- a/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs +++ b/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs @@ -1,23 +1,212 @@ //! # FRAME Benchmarking and Weights. //! -//! Notes: +//! This reference doc explores the concept of weights within Polkadot-SDK runtimes, and more +//! specifically how FRAME-based runtimes handle it. //! -//! On Weight as a concept. +//! ## Metering //! -//! - Why we need it. Super important. People hate this. We need to argue why it is worth it. -//! - Axis of weight: PoV + Time. -//! - pre dispatch weight vs. metering and post dispatch correction. -//! - mention that we will do this for PoV -//! - you can manually refund using `DispatchResultWithPostInfo`. -//! - Technically you can have weights with any benchmarking framework. You just need one number to -//! be computed pre-dispatch. But FRAME gives you a framework for this. -//! - improve documentation of `#[weight = ..]` and `#[pallet::weight(..)]`. All syntax variation -//! should be covered. +//! The existence of "weight" as a concept in Polkadot-SDK is a direct consequence of the usage of +//! WASM as a virtual machine. Unlike a metered virtual machine like EVM, where every instruction +//! can have a (fairly) deterministic "cost" (also known as "gas price") associated with it, WASM is +//! a stack machine with more complex instruction set, and more unpredictable execution times. This +//! means that unlike EVM, it is not possible to implement a "metering" system in WASM. A metering +//! system is one in which instructions are executed one by one, and the cost/gas is stored in an +//! accumulator. The execution may then halt once a gas limit is reached. //! -//! On FRAME benchmarking machinery: +//! In Polkadot-SDK, the WASM runtime is not assumed to be metered. //! -//! - Component analysis, why everything must be linear. -//! - How to write benchmarks, how you must think of worst case. -//! - How to run benchmarks. +//! ## Trusted Code //! -//! - +//! Another important difference is that EVM is mostly used to express smart contracts, which are +//! foreign and untrusted codes from the perspective of the blockchain executing them. In such +//! cases, metering is crucial, in order to ensure a malicious code cannot consume more gas than +//! expected. +//! +//! This assumption does not hold about the runtime of Polkadot-SDK-based blockchains. The runtime +//! is trusted code, and it is assumed to be written by the same team/developers who are running the +//! blockchain itself. Therefore, this assumption of "untrusted foreign code" does not hold. +//! +//! This is why the runtime can opt for a more performant, more flexible virtual machine like WASM, +//! and get away without having metering. +//! +//! ## Benchmarking +//! +//! With the matter of untrusted code execution out of the way, the need for strict metering goes +//! out of the way. Yet, it would still be very beneficial for block producers to be able to know an +//! upper bound on how much resources a operation is going to consume before actually executing that +//! operation. This is why FRAME has a toolkit for benchmarking pallets: So that this upper bound +//! can be empirically determined. +//! +//! > Note: Benchmarking is a static analysis: It is all about knowing the upper bound of how much +//! > resources an operation takes statically, without actually executing it. In the context of +//! > FRAME extrinsics, this static-ness is expressed by the keyword "pre-dispatch". +//! +//! To understand why this upper bound is needed, consider the following: A block producer knows +//! they have 20ms left to finish producing their block, and wishes to include more transactions in +//! the block. Yet, in a metered environment, it would not know which transaction is likely to fit +//! the 20ms. In a benchmarked environment, it can examine the transactions for their upper bound, +//! and include the ones that are known to fit based on the worst case. +//! +//! The benchmarking code can be written as a part of FRAME pallet, using the macros provided in +//! [`frame_benchmarking`]. See any of the existing pallets in `polkadot-sdk`, or the pallets in our +//! [`crate::polkadot_sdk::templates`] for examples. +//! +//! ## Weight +//! +//! Finally, [`sp_weights::Weight`] is the output of the benchmarking process. It is a +//! two-dimensional data structure that demonstrates the resources consumed by a given block of +//! code (for example, a transaction). The two dimensions are: +//! +//! * reference time: The time consumed in pico-seconds, on a reference hardware. +//! * proof size: The amount of storage proof necessary to re-execute the block of code. This is +//! mainly needed for parachain <> relay-chain verification. +//! +//! ## How To Write Benchmarks: Worst Case +//! +//! The most important detail about writing benchmarking code is that it must be written such that +//! it captures the worst case execution of any block of code. +//! +//! Consider: +#![doc = docify::embed!("./src/reference_docs/frame_benchmarking_weight.rs", simple_transfer)] +//! +//! If this block of code is to be benchmarked, then the benchmarking code must be written such that +//! it captures the worst case. +//! +//! ## Gluing Pallet Benchmarking with Runtime +//! +//! FRAME pallets are mandated to provide their own benchmarking code. Runtimes contain the +//! boilerplate needed to run these benchmarking (see [Running Benchmarks +//! below](#running-benchmarks)). The outcome of running these benchmarks are meant to be fed back +//! into the pallet via a conventional `trait WeightInfo` on `Config`: +#![doc = docify::embed!("src/reference_docs/frame_benchmarking_weight.rs", WeightInfo)] +//! +//! Then, individual functions of this trait are the final values that we assigned to the +//! [`frame::pallet_macros::weight`] attribute: +#![doc = docify::embed!("./src/reference_docs/frame_benchmarking_weight.rs", simple_transfer_2)] +//! +//! ## Manual Refund +//! +//! Back to the assumption of writing benchmarks for worst case: Sometimes, the pre-dispatch weight +//! significantly differ from the post-dispatch actual weight consumed. This can be expressed with +//! the following FRAME syntax: +#![doc = docify::embed!("./src/reference_docs/frame_benchmarking_weight.rs", simple_transfer_3)] +//! +//! ## Running Benchmarks +//! +//! Two ways exist to run the benchmarks of a runtime. +//! +//! 1. The old school way: Most Polkadot-SDK based nodes (such as the ones integrated in +//! [`templates`]) have an a `benchmark` subcommand integrated into themselves. +//! 2. The more [`crate::reference_docs::omni_node`] compatible way of running the benchmarks would +//! be using [`frame-omni-bencher`] CLI, which only relies on a runtime. +//! +//! Note that by convention, the runtime and pallets always have their benchmarking code feature +//! gated as behind `runtime-benchmarks`. So, the runtime should be compiled with `--features +//! runtime-benchmarks`. +//! +//! ## Automatic Refund of `proof_size`. +//! +//! A new feature in FRAME allows the runtime to be configured for "automatic refund" of the proof +//! size weight. This is very useful for maximizing the throughput of parachains. Please see: +//! [`crate::guides::enable_pov_reclaim`]. +//! +//! ## Summary +//! +//! Polkadot-SDK runtimes use a more performant VM, namely WASM, which does not have metering. In +//! return they have to be benchmarked to provide an upper bound on the resources they consume. This +//! upper bound is represented as [`sp_weights::Weight`]. +//! +//! ## Future: PolkaVM +//! +//! With the transition of Polkadot relay chain to [JAM], a set of new features are being +//! introduced, one of which being a new virtual machine named [PolkaVM] that is as flexible as +//! WASM, but also capable of metering. This might alter the future of benchmarking in FRAME and +//! Polkadot-SDK, rendering them not needed anymore once PolkaVM is fully integrated into +//! Polkadot-sdk. For a basic explanation of JAM and PolkaVM, see [here](https://blog.kianenigma.com/posts/tech/demystifying-jam/#pvm). +//! +//! +//! [`frame-omni-bencher`]: https://crates.io/crates/frame-omni-bencher +//! [`templates`]: crate::polkadot_sdk::templates +//! [PolkaVM]: https://github.com/koute/polkavm +//! [JAM]: https://graypaper.com + +#[frame::pallet(dev_mode)] +#[allow(unused_variables, unreachable_code, unused, clippy::diverging_sub_expression)] +pub mod pallet { + use frame::prelude::*; + + #[docify::export] + pub trait WeightInfo { + fn simple_transfer() -> Weight; + } + + #[pallet::config] + pub trait Config: frame_system::Config { + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + #[docify::export] + #[pallet::weight(10_000)] + pub fn simple_transfer( + origin: OriginFor, + destination: T::AccountId, + amount: u32, + ) -> DispatchResult { + let destination_exists = todo!(); + if destination_exists { + // simpler code path + } else { + // more complex code path + } + Ok(()) + } + + #[docify::export] + #[pallet::weight(T::WeightInfo::simple_transfer())] + pub fn simple_transfer_2( + origin: OriginFor, + destination: T::AccountId, + amount: u32, + ) -> DispatchResult { + let destination_exists = todo!(); + if destination_exists { + // simpler code path + } else { + // more complex code path + } + Ok(()) + } + + #[docify::export] + // This is the worst-case, pre-dispatch weight. + #[pallet::weight(T::WeightInfo::simple_transfer())] + pub fn simple_transfer_3( + origin: OriginFor, + destination: T::AccountId, + amount: u32, + ) -> DispatchResultWithPostInfo { + // ^^ Notice the new return type + let destination_exists = todo!(); + if destination_exists { + // simpler code path + // Note that need for .into(), to convert `()` to `PostDispatchInfo` + // See: https://paritytech.github.io/polkadot-sdk/master/frame_support/dispatch/struct.PostDispatchInfo.html#impl-From%3C()%3E-for-PostDispatchInfo + Ok(().into()) + } else { + // more complex code path + let actual_weight = + todo!("this can likely come from another benchmark that is NOT the worst case"); + let pays_fee = todo!("You can set this to `Pays::Yes` or `Pays::No` to change if this transaction should pay fees"); + Ok(frame::deps::frame_support::dispatch::PostDispatchInfo { + actual_weight: Some(actual_weight), + pays_fee, + }) + } + } + } +} diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs index 9cf5605a88b..e47eece784c 100644 --- a/docs/sdk/src/reference_docs/mod.rs +++ b/docs/sdk/src/reference_docs/mod.rs @@ -78,7 +78,6 @@ pub mod frame_system_accounts; pub mod development_environment_advice; /// Learn about benchmarking and weight. -// TODO: @shawntabrizi @ggwpez https://github.com/paritytech/polkadot-sdk-docs/issues/50 pub mod frame_benchmarking_weight; /// Learn about the token-related logic in FRAME and how to apply it to your use case. @@ -109,3 +108,6 @@ pub mod umbrella_crate; /// Learn about how to create custom RPC endpoints and runtime APIs. pub mod custom_runtime_api_rpc; + +/// The [`polkadot-omni-node`](https://crates.io/crates/polkadot-omni-node) and its related binaries. +pub mod omni_node; diff --git a/docs/sdk/src/reference_docs/omni_node.rs b/docs/sdk/src/reference_docs/omni_node.rs new file mode 100644 index 00000000000..44d63704a45 --- /dev/null +++ b/docs/sdk/src/reference_docs/omni_node.rs @@ -0,0 +1,185 @@ +//! # (Omni) Node +//! +//! This reference doc elaborates on what a Polkadot-SDK/Substrate node software is, and what +//! various ways exist to run one. +//! +//! The node software, as denoted in [`crate::reference_docs::wasm_meta_protocol`], is everything in +//! a blockchain other than the WASM runtime. It contains common components such as the database, +//! networking, RPC server and consensus. Substrate-based nodes are native binaries that are +//! compiled down from the Rust source code. The `node` folder in any of the [`templates`] are +//! examples of this source. +//! +//! > Note: A typical node also contains a lot of other tools (exposed as subcommands) that are +//! > useful for operating a blockchain, such as the ones noted in +//! > [`polkadot_omni_node_lib::cli::Cli::subcommand`]. +//! +//! ## Node <> Runtime Interdependence +//! +//! While in principle the node can be mostly independent of the runtime, for various reasons, such +//! as the [native runtime](crate::reference_docs::wasm_meta_protocol#native-runtime), the node and +//! runtime were historically tightly linked together. Another reason is that the node and the +//! runtime need to be in agreement about which consensus algorithm they use, as described +//! [below](#consensus-engine). +//! +//! Specifically, the node relied on the existence of a linked runtime, and *could only reliably run +//! that runtime*. This is why if you look at any of the [`templates`], they are all composed of a +//! node, and a runtime. +//! +//! Moreover, the code and API of each of these nodes was historically very advanced, and tailored +//! towards those who wish to customize many of the node components at depth. +//! +//! > The notorious `service.rs` in any node template is a good example of this. +//! +//! A [trend](https://github.com/paritytech/polkadot-sdk/issues/62) has already been undergoing in +//! order to de-couple the node and the runtime for a long time. The north star of this effort is +//! twofold : +//! +//! 1. develop what can be described as an "omni-node": A node that can run most runtimes. +//! 2. provide a cleaner abstraction for creating a custom node. +//! +//! While a single omni-node running *all possible runtimes* is not feasible, the +//! [`polkadot-omni-node`] is an attempt at creating the former, and the [`polkadot_omni_node_lib`] +//! is the latter. +//! +//! > Note: The OmniNodes are mainly focused on the development needs of **Polkadot +//! > parachains ONLY**, not (Substrate) solo-chains. For the time being, solo-chains are not +//! > supported by the OmniNodes. This might change in the future. +//! +//! ## Types of Nodes +//! +//! With the emergence of the OmniNodes, let's look at the various Node options available to a +//! builder. +//! +//! ### [`polkadot-omni-node`] +//! +//! [`polkadot-omni-node`] is a white-labeled binary, released as a part of Polkadot SDK that is +//! capable of meeting the needs of most Polkadot parachains. +//! +//! It can act as the collator of a parachain in production, with all the related auxillary +//! functionalities that a normal collator node has: RPC server, archiving state, etc. Moreover, it +//! can also run the wasm blob of the parachain locally for testing and development. +//! +//! ### [`polkadot_omni_node_lib`] +//! +//! [`polkadot_omni_node_lib`] is the library version of the above, which can be used to create a +//! fresh parachain node, with a some limited configuration options using a lean API. +//! +//! ### Old School Nodes +//! +//! The existing node architecture, as seen in the [`templates`], is still available for those who +//! want to have full control over the node software. +//! +//! ### Summary +//! +//! We can summarize the choices for the node software of any given user of Polkadot-SDK, wishing to +//! deploy a parachain into 3 categories: +//! +//! 1. **Use the [`polkadot-omni-node`]**: This is the easiest way to get started, and is the most +//! likely to be the best choice for most users. +//! * can run almost any runtime with [`--dev-block-time`] +//! 2. **Use the [`polkadot_omni_node_lib`]**: This is the best choice for those who want to have +//! slightly more control over the node software, such as embedding a custom chain-spec. +//! 3. **Use the old school nodes**: This is the best choice for those who want to have full control +//! over the node software, such as changing the consensus engine, altering the transaction pool, +//! and so on. +//! +//! ## _OmniTools_: User Journey +//! +//! All in all, the user journey of a team/builder, in the OmniNode world is as follows: +//! +//! * The [`templates`], most notably the [`parachain-template`] is the canonical starting point. +//! That being said, the node code of the templates (which may be eventually +//! removed/feature-gated) is no longer of relevance. The only focus is in the runtime, and +//! obtaining a `.wasm` file. References: +//! * [`crate::guides::your_first_pallet`] +//! * [`crate::guides::your_first_runtime`] +//! * If need be, the weights of the runtime need to be updated using `frame-omni-bencher`. +//! References: +//! * [`crate::reference_docs::frame_benchmarking_weight`] +//! * Next, [`chain-spec-builder`] is used to generate a `chain_spec.json`, either for development, +//! or for production. References: +//! * [`crate::reference_docs::chain_spec_genesis`] +//! * For local development, the following options are available: +//! * `polkadot-omni-node` (notably, with [`--dev-block-time`]). References: +//! * [`crate::guides::your_first_node`] +//! * External tools such as `chopsticks`, `zombienet`. +//! * See the `README.md` file of the `polkadot-sdk-parachain-template`. +//! * For production `polkadot-omni-node` can be used out of the box. +//! * For further customization [`polkadot_omni_node_lib`] can be used. +//! +//! ## Appendix +//! +//! This section describes how the interdependence between the node and the runtime is related to +//! the consensus engine. This information is useful for those who want to understand the +//! historical context of the node and the runtime. +//! +//! ### Consensus Engine +//! +//! In any given substrate-based chain, both the node and the runtime will have their own +//! opinion/information about what consensus engine is going to be used. +//! +//! In practice, the majority of the implementation of any consensus engine is in the node side, but +//! the runtime also typically needs to expose a custom runtime-api to enable the particular +//! consensus engine to work, and that particular runtime-api is implemented by a pallet +//! corresponding to that consensus engine. +//! +//! For example, taking a snippet from [`solochain_template_runtime`], the runtime has to provide +//! this additional runtime-api (compared to [`minimal_template_runtime`]), if the node software is +//! configured to use the Aura consensus engine: +//! +//! ```text +//! impl sp_consensus_aura::AuraApi for Runtime { +//! fn slot_duration() -> sp_consensus_aura::SlotDuration { +//! ... +//! } +//! fn authorities() -> Vec { +//! ... +//! } +//! } +//! ``` +//! +//! For simplicity, we can break down "consensus" into two main parts: +//! +//! * Block Authoring: Deciding who gets to produce the next block. +//! * Finality: Deciding when a block is considered final. +//! +//! For block authoring, there are a number of options: +//! +//! * [`sc_consensus_manual_seal`]: Useful for testing, where any node can produce a block at any +//! time. This is often combined with a fixed interval at which a block is produced. +//! * [`sc_consensus_aura`]/[`pallet_aura`]: A simple round-robin block authoring mechanism. +//! * [`sc_consensus_babe`]/[`pallet_babe`]: A more advanced block authoring mechanism, capable of +//! anonymizing the next block author. +//! * [`sc_consensus_pow`]: Proof of Work block authoring. +//! +//! For finality, there is one main option shipped with polkadot-sdk: +//! +//! * [`sc_consensus_grandpa`]/[`pallet_grandpa`]: A finality gadget that uses a voting mechanism to +//! decide when a block +//! +//! **The most important lesson here is that the node and the runtime must have matching consensus +//! components.** +//! +//! ### Consequences for OmniNode +//! +//! +//! The consequence of the above is that anyone using the OmniNode must also be aware of the +//! consensus system used in the runtime, and be aware if it is matching that of the OmniNode or +//! not. For the time being, [`polkadot-omni-node`] only supports: +//! +//! * Parachain-based Aura consensus, with 6s async-backing block-time, and before full elastic +//! scaling). [`polkadot_omni_node_lib::cli::Cli::experimental_use_slot_based`] for fixed factor +//! scaling (a step +//! * Ability to run any runtime with [`--dev-block-time`] flag. This uses +//! [`sc_consensus_manual_seal`] under the hood, and has no restrictions on the runtime's +//! consensus. +//! +//! [This](https://github.com/paritytech/polkadot-sdk/issues/5565) future improvement to OmniNode +//! aims to make such checks automatic. +//! +//! +//! [`templates`]: crate::polkadot_sdk::templates +//! [`parachain-template`]: https://github.com/paritytech/polkadot-sdk-parachain-template +//! [`--dev-block-time`]: polkadot_omni_node_lib::cli::Cli::dev_block_time +//! [`polkadot-omni-node`]: https://crates.io/crates/polkadot-omni-node +//! [`chain-spec-builder`]: https://crates.io/crates/staging-chain-spec-builder diff --git a/docs/sdk/src/reference_docs/umbrella_crate.rs b/docs/sdk/src/reference_docs/umbrella_crate.rs index 1074cde3769..8d9bcdfc208 100644 --- a/docs/sdk/src/reference_docs/umbrella_crate.rs +++ b/docs/sdk/src/reference_docs/umbrella_crate.rs @@ -5,6 +5,7 @@ //! crate. This helps with selecting the right combination of crate versions, since otherwise 3rd //! party tools are needed to select a compatible set of versions. //! +//! //! ## Features //! //! The umbrella crate supports no-std builds and can therefore be used in the runtime and node. diff --git a/prdoc/pr_6094.prdoc b/prdoc/pr_6094.prdoc new file mode 100644 index 00000000000..23391c88915 --- /dev/null +++ b/prdoc/pr_6094.prdoc @@ -0,0 +1,21 @@ +# 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: Polkadot OmniNode Docs + +doc: + - audience: ... + description: | + Adds documentation in https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/index.html and rust-docs for polkadot-omni-node project. + +crates: + - name: sp-genesis-builder + bump: patch + - name: pallet-aura + bump: patch + - name: polkadot-omni-node-lib + bump: patch + - name: polkadot-sdk-frame # since the crate is "experimental, we don't need to bump yet." + bump: major + - name: polkadot-omni-node + bump: patch diff --git a/substrate/bin/node/cli/src/cli.rs b/substrate/bin/node/cli/src/cli.rs index c0dcacb2e4b..1d7001a5dcc 100644 --- a/substrate/bin/node/cli/src/cli.rs +++ b/substrate/bin/node/cli/src/cli.rs @@ -59,6 +59,7 @@ pub enum Subcommand { Inspect(node_inspect::cli::InspectCmd), /// Sub-commands concerned with benchmarking. + /// /// The pallet benchmarking moved to the `pallet` sub-command. #[command(subcommand)] Benchmark(frame_benchmarking_cli::BenchmarkCmd), diff --git a/substrate/frame/Cargo.toml b/substrate/frame/Cargo.toml index 595fb5a19b0..2d0daf82997 100644 --- a/substrate/frame/Cargo.toml +++ b/substrate/frame/Cargo.toml @@ -44,8 +44,10 @@ sp-offchain = { optional = true, workspace = true } sp-session = { optional = true, workspace = true } sp-consensus-aura = { optional = true, workspace = true } sp-consensus-grandpa = { optional = true, workspace = true } +sp-genesis-builder = { optional = true, workspace = true } sp-inherents = { optional = true, workspace = true } sp-storage = { optional = true, workspace = true } +sp-keyring = { optional = true, workspace = true } frame-executive = { optional = true, workspace = true } frame-system-rpc-runtime-api = { optional = true, workspace = true } @@ -73,7 +75,9 @@ runtime = [ "sp-block-builder", "sp-consensus-aura", "sp-consensus-grandpa", + "sp-genesis-builder", "sp-inherents", + "sp-keyring", "sp-offchain", "sp-session", "sp-storage", @@ -97,8 +101,10 @@ std = [ "sp-consensus-aura?/std", "sp-consensus-grandpa?/std", "sp-core/std", + "sp-genesis-builder?/std", "sp-inherents?/std", "sp-io/std", + "sp-keyring?/std", "sp-offchain?/std", "sp-runtime/std", "sp-session?/std", diff --git a/substrate/frame/aura/src/lib.rs b/substrate/frame/aura/src/lib.rs index f829578fb28..c74e864ea0d 100644 --- a/substrate/frame/aura/src/lib.rs +++ b/substrate/frame/aura/src/lib.rs @@ -400,7 +400,9 @@ impl OnTimestampSet for Pallet { assert_eq!( CurrentSlot::::get(), timestamp_slot, - "Timestamp slot must match `CurrentSlot`" + "Timestamp slot must match `CurrentSlot`. This likely means that the configured block \ + time in the node and/or rest of the runtime is not compatible with Aura's \ + `SlotDuration`", ); } } diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index fcd96b40c3c..ade1095cc50 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -222,7 +222,12 @@ pub mod runtime { // Types often used in the runtime APIs. pub use sp_core::OpaqueMetadata; + pub use sp_genesis_builder::{ + PresetId, Result as GenesisBuilderResult, DEV_RUNTIME_PRESET, + LOCAL_TESTNET_RUNTIME_PRESET, + }; pub use sp_inherents::{CheckInherentsResult, InherentData}; + pub use sp_keyring::AccountKeyring; pub use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode}; } @@ -246,6 +251,7 @@ pub mod runtime { pub use sp_block_builder::*; pub use sp_consensus_aura::*; pub use sp_consensus_grandpa::*; + pub use sp_genesis_builder::*; pub use sp_offchain::*; pub use sp_session::runtime_api::*; pub use sp_transaction_pool::runtime_api::*; @@ -396,8 +402,12 @@ pub mod deps { #[cfg(feature = "runtime")] pub use sp_consensus_grandpa; #[cfg(feature = "runtime")] + pub use sp_genesis_builder; + #[cfg(feature = "runtime")] pub use sp_inherents; #[cfg(feature = "runtime")] + pub use sp_keyring; + #[cfg(feature = "runtime")] pub use sp_offchain; #[cfg(feature = "runtime")] pub use sp_storage; diff --git a/substrate/primitives/genesis-builder/src/lib.rs b/substrate/primitives/genesis-builder/src/lib.rs index 07317bc4cb5..4763aa06341 100644 --- a/substrate/primitives/genesis-builder/src/lib.rs +++ b/substrate/primitives/genesis-builder/src/lib.rs @@ -17,17 +17,33 @@ #![cfg_attr(not(feature = "std"), no_std)] -//! Substrate genesis config builder +//! # Substrate genesis config builder. //! -//! For FRAME based runtimes, this runtime interface provides means to interact with -//! `RuntimeGenesisConfig`. Runtime provides a default `RuntimeGenesisConfig` structure in a form of -//! the JSON blob. +//! This crate contains [`GenesisBuilder`], a runtime-api to be implemented by runtimes, in order to +//! express their genesis state. //! -//! For non-FRAME runtimes this interface is intended to build genesis state of the runtime basing -//! on some input arbitrary bytes array. This documentation uses term `RuntimeGenesisConfig`, which -//! for non-FRAME runtimes may be understood as the runtime-side entity representing initial runtime -//! configuration. The representation of the preset is an arbitrary `Vec` and does not -//! necessarily have to represent a JSON blob. +//! The overall flow of the methods in [`GenesisBuilder`] is as follows: +//! +//! 1. [`GenesisBuilder::preset_names`]: A runtime exposes a number of different +//! `RuntimeGenesisConfig` variations, each of which is called a `preset`, and is identified by a +//! [`PresetId`]. All runtimes are encouraged to expose at least [`DEV_RUNTIME_PRESET`] and +//! [`LOCAL_TESTNET_RUNTIME_PRESET`] presets for consistency. +//! 2. [`GenesisBuilder::get_preset`]: Given a `PresetId`, this the runtime returns the JSON blob +//! representation of the `RuntimeGenesisConfig` for that preset. This JSON blob is often mixed +//! into the broader `chain_spec`. If `None` is given, [`GenesisBuilder::get_preset`] provides a +//! JSON represention of the default `RuntimeGenesisConfig` (by simply serializing the +//! `RuntimeGenesisConfig::default()` value into JSON format). This is used as a base for +//! applying patches / presets. + +//! 3. [`GenesisBuilder::build_state`]: Given a JSON blob, this method should deserialize it and +//! enact it (using `frame_support::traits::BuildGenesisConfig` for Frame-based runtime), +//! essentially writing it to the state. +//! +//! The first two flows are often done in between a runtime, and the `chain_spec_builder` binary. +//! The latter is used when a new blockchain is launched to enact and store the genesis state. See +//! the documentation of `chain_spec_builder` for more info. +//! +//! ## Patching //! //! The runtime may provide a number of partial predefined `RuntimeGenesisConfig` configurations in //! the form of patches which shall be applied on top of the default `RuntimeGenesisConfig`. The @@ -35,19 +51,22 @@ //! customized in the default runtime genesis config. These predefined configurations are referred //! to as presets. //! -//! This allows the runtime to provide a number of predefined configs (e.g. for different -//! testnets or development) without neccessity to leak the runtime types outside the itself (e.g. -//! node or chain-spec related tools). +//! This allows the runtime to provide a number of predefined configs (e.g. for different testnets +//! or development) without necessarily to leak the runtime types outside itself (e.g. node or +//! chain-spec related tools). +//! +//! ## FRAME vs. non-FRAME +//! +//! For FRAME based runtimes [`GenesisBuilder`] provides means to interact with +//! `RuntimeGenesisConfig`. +//! +//! For non-FRAME runtimes this interface is intended to build genesis state of the runtime basing +//! on some input arbitrary bytes array. This documentation uses term `RuntimeGenesisConfig`, which +//! for non-FRAME runtimes may be understood as the "runtime-side entity representing initial +//! runtime genesis configuration". The representation of the preset is an arbitrary `Vec` and +//! does not necessarily have to represent a JSON blob. //! -//! This Runtime API allows to interact with `RuntimeGenesisConfig`, in particular: -//! - provide the list of available preset names, -//! - provide a number of named presets of `RuntimeGenesisConfig`, -//! - provide a JSON represention of the default `RuntimeGenesisConfig` (by simply serializing the -//! default `RuntimeGenesisConfig` struct into JSON format), -//! - deserialize the full `RuntimeGenesisConfig` from given JSON blob and put the resulting -//! `RuntimeGenesisConfig` structure into the state storage creating the initial runtime's state. -//! Allows to build customized genesis. This operation internally calls `GenesisBuild::build` -//! function for all runtime pallets. +//! ## Genesis Block State //! //! Providing externalities with an empty storage and putting `RuntimeGenesisConfig` into storage //! (by calling `build_state`) allows to construct the raw storage of `RuntimeGenesisConfig` @@ -75,14 +94,15 @@ pub const DEV_RUNTIME_PRESET: &'static str = "development"; pub const LOCAL_TESTNET_RUNTIME_PRESET: &'static str = "local_testnet"; sp_api::decl_runtime_apis! { - /// API to interact with RuntimeGenesisConfig for the runtime + /// API to interact with `RuntimeGenesisConfig` for the runtime pub trait GenesisBuilder { /// Build `RuntimeGenesisConfig` from a JSON blob not using any defaults and store it in the /// storage. /// - /// In the case of a FRAME-based runtime, this function deserializes the full `RuntimeGenesisConfig` from the given JSON blob and - /// puts it into the storage. If the provided JSON blob is incorrect or incomplete or the - /// deserialization fails, an error is returned. + /// In the case of a FRAME-based runtime, this function deserializes the full + /// `RuntimeGenesisConfig` from the given JSON blob and puts it into the storage. If the + /// provided JSON blob is incorrect or incomplete or the deserialization fails, an error + /// is returned. /// /// Please note that provided JSON blob must contain all `RuntimeGenesisConfig` fields, no /// defaults will be used. @@ -91,7 +111,7 @@ sp_api::decl_runtime_apis! { /// Returns a JSON blob representation of the built-in `RuntimeGenesisConfig` identified by /// `id`. /// - /// If `id` is `None` the function returns JSON blob representation of the default + /// If `id` is `None` the function should return JSON blob representation of the default /// `RuntimeGenesisConfig` struct of the runtime. Implementation must provide default /// `RuntimeGenesisConfig`. /// diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index 464cad4e3da..4f914a823bf 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -324,7 +324,7 @@ impl_runtime_apis! { } fn preset_names() -> Vec { - self::genesis_config_presets::preset_names() + crate::genesis_config_presets::preset_names() } } } diff --git a/templates/parachain/runtime/src/genesis_config_presets.rs b/templates/parachain/runtime/src/genesis_config_presets.rs index 322c91f4f13..394bde0be77 100644 --- a/templates/parachain/runtime/src/genesis_config_presets.rs +++ b/templates/parachain/runtime/src/genesis_config_presets.rs @@ -76,6 +76,8 @@ 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(), ) } -- GitLab From 2324bd7b2d6dafb21c20ffaeb73e0a393db19e22 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Thu, 24 Oct 2024 12:47:44 +0300 Subject: [PATCH 35/50] Fix `zombienet-bridges-0001-asset-transfer-works` (#6175) Closes https://github.com/paritytech/polkadot-sdk/issues/6161 Westend BridgeHub freezes for a while at block 3 and if we try to init the bridge and fund the accounts during that time, it fails. So we wait untill all the parachains produced at least 10 blocks, in order to make sure that they work reliably. --- .gitlab/pipeline/zombienet/bridges.yml | 4 +--- .../rococo-westend/{rococo.zndsl => rococo-bridge.zndsl} | 0 .../environments/rococo-westend/rococo-start.zndsl | 8 ++++++++ bridges/testing/environments/rococo-westend/spawn.sh | 5 +++-- .../testing/environments/rococo-westend/start_relayer.sh | 4 ++-- .../{westend.zndsl => westend-bridge.zndsl} | 0 .../environments/rococo-westend/westend-start.zndsl | 8 ++++++++ 7 files changed, 22 insertions(+), 7 deletions(-) rename bridges/testing/environments/rococo-westend/{rococo.zndsl => rococo-bridge.zndsl} (100%) create mode 100644 bridges/testing/environments/rococo-westend/rococo-start.zndsl rename bridges/testing/environments/rococo-westend/{westend.zndsl => westend-bridge.zndsl} (100%) create mode 100644 bridges/testing/environments/rococo-westend/westend-start.zndsl diff --git a/.gitlab/pipeline/zombienet/bridges.yml b/.gitlab/pipeline/zombienet/bridges.yml index 0472601a6a2..07711e32a9a 100644 --- a/.gitlab/pipeline/zombienet/bridges.yml +++ b/.gitlab/pipeline/zombienet/bridges.yml @@ -11,7 +11,7 @@ - if: $CI_COMMIT_REF_NAME =~ /^gh-readonly-queue.*$/ variables: DOCKER_IMAGES_VERSION: ${CI_COMMIT_SHORT_SHA} - - !reference [.build-refs, rules] + - !reference [ .build-refs, rules ] before_script: - echo "Zombienet Tests Config" - echo "${ZOMBIENET_IMAGE}" @@ -47,8 +47,6 @@ - cp -r /tmp/bridges-tests-run-*/bridge_hub_rococo_local_network/*.log ./zombienet-logs/ # copy logs of westend nodes - cp -r /tmp/bridges-tests-run-*/bridge_hub_westend_local_network/*.log ./zombienet-logs/ - tags: - - zombienet-polkadot-integration-test zombienet-bridges-0001-asset-transfer-works: extends: diff --git a/bridges/testing/environments/rococo-westend/rococo.zndsl b/bridges/testing/environments/rococo-westend/rococo-bridge.zndsl similarity index 100% rename from bridges/testing/environments/rococo-westend/rococo.zndsl rename to bridges/testing/environments/rococo-westend/rococo-bridge.zndsl diff --git a/bridges/testing/environments/rococo-westend/rococo-start.zndsl b/bridges/testing/environments/rococo-westend/rococo-start.zndsl new file mode 100644 index 00000000000..8c719b010df --- /dev/null +++ b/bridges/testing/environments/rococo-westend/rococo-start.zndsl @@ -0,0 +1,8 @@ +Description: Check if the Rococo parachains started producing blocks reliably +Network: ./bridge_hub_westend_local_network.toml +Creds: config + +# ensure that initialization has completed +asset-hub-rococo-collator1: reports block height is at least 10 within 180 seconds +bridge-hub-rococo-collator1: reports block height is at least 10 within 180 seconds + diff --git a/bridges/testing/environments/rococo-westend/spawn.sh b/bridges/testing/environments/rococo-westend/spawn.sh index a0ab00be144..83b3b0720bb 100755 --- a/bridges/testing/environments/rococo-westend/spawn.sh +++ b/bridges/testing/environments/rococo-westend/spawn.sh @@ -35,9 +35,11 @@ start_zombienet $TEST_DIR $westend_def westend_dir westend_pid echo if [[ $init -eq 1 ]]; then + run_zndsl ${BASH_SOURCE%/*}/rococo-start.zndsl $rococo_dir + run_zndsl ${BASH_SOURCE%/*}/westend-start.zndsl $westend_dir + rococo_init_log=$logs_dir/rococo-init.log echo -e "Setting up the rococo side of the bridge. Logs available at: $rococo_init_log\n" - westend_init_log=$logs_dir/westend-init.log echo -e "Setting up the westend side of the bridge. Logs available at: $westend_init_log\n" @@ -47,7 +49,6 @@ if [[ $init -eq 1 ]]; then westend_init_pid=$! wait -n $rococo_init_pid $westend_init_pid - $helper_script init-bridge-hub-rococo-local >> $rococo_init_log 2>&1 & rococo_init_pid=$! $helper_script init-bridge-hub-westend-local >> $westend_init_log 2>&1 & diff --git a/bridges/testing/environments/rococo-westend/start_relayer.sh b/bridges/testing/environments/rococo-westend/start_relayer.sh index 9c57e4a6ab6..150fce03507 100755 --- a/bridges/testing/environments/rococo-westend/start_relayer.sh +++ b/bridges/testing/environments/rococo-westend/start_relayer.sh @@ -29,8 +29,8 @@ messages_relayer_log=$logs_dir/relayer_messages.log echo -e "Starting rococo-westend messages relayer. Logs available at: $messages_relayer_log\n" start_background_process "$helper_script run-messages-relay" $messages_relayer_log messages_relayer_pid -run_zndsl ${BASH_SOURCE%/*}/rococo.zndsl $rococo_dir -run_zndsl ${BASH_SOURCE%/*}/westend.zndsl $westend_dir +run_zndsl ${BASH_SOURCE%/*}/rococo-bridge.zndsl $rococo_dir +run_zndsl ${BASH_SOURCE%/*}/westend-bridge.zndsl $westend_dir eval $__finality_relayer_pid="'$finality_relayer_pid'" eval $__parachains_relayer_pid="'$parachains_relayer_pid'" diff --git a/bridges/testing/environments/rococo-westend/westend.zndsl b/bridges/testing/environments/rococo-westend/westend-bridge.zndsl similarity index 100% rename from bridges/testing/environments/rococo-westend/westend.zndsl rename to bridges/testing/environments/rococo-westend/westend-bridge.zndsl diff --git a/bridges/testing/environments/rococo-westend/westend-start.zndsl b/bridges/testing/environments/rococo-westend/westend-start.zndsl new file mode 100644 index 00000000000..fe587322edb --- /dev/null +++ b/bridges/testing/environments/rococo-westend/westend-start.zndsl @@ -0,0 +1,8 @@ +Description: Check if the Westend parachains started producing blocks reliably +Network: ./bridge_hub_westend_local_network.toml +Creds: config + +# ensure that initialization has completed +asset-hub-westend-collator1: reports block height is at least 10 within 180 seconds +bridge-hub-westend-collator1: reports block height is at least 10 within 180 seconds + -- GitLab From d4b01add06139b39b9ce69216f06b827f7f388a7 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 24 Oct 2024 12:14:02 +0200 Subject: [PATCH 36/50] [pallet-revive] fix hardcoded gas in tests (#6192) Fix hardcoded gas limits in tests --------- Co-authored-by: GitHub Action --- prdoc/pr_6192.prdoc | 7 ++++++ substrate/frame/revive/src/evm/runtime.rs | 30 +++++++++++++++++------ substrate/frame/revive/src/lib.rs | 3 ++- 3 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 prdoc/pr_6192.prdoc diff --git a/prdoc/pr_6192.prdoc b/prdoc/pr_6192.prdoc new file mode 100644 index 00000000000..cd925548670 --- /dev/null +++ b/prdoc/pr_6192.prdoc @@ -0,0 +1,7 @@ +title: '[pallet-revive] fix hardcoded gas in tests' +doc: +- audience: Runtime Dev + description: Fix hardcoded gas limits in tests +crates: +- name: pallet-revive + bump: patch diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index e4340b27a18..4c3fdeca720 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -356,12 +356,7 @@ pub trait EthExtra { Default::default(), ) .into(); - - log::debug!(target: LOG_TARGET, "Checking Ethereum transaction fees: - dispatch_info: {info:?} - encoded_len: {encoded_len:?} - fees: {actual_fee:?} - "); + log::debug!(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:?}"); @@ -490,11 +485,30 @@ mod test { } } + fn estimate_gas(&mut self) { + let dry_run = crate::Pallet::::bare_eth_transact( + Account::default().account_id(), + self.tx.to, + self.tx.value.try_into().unwrap(), + self.tx.input.clone().0, + Weight::MAX, + u64::MAX, + |call| { + let call = RuntimeCall::Contracts(call); + let uxt: Ex = sp_runtime::generic::UncheckedExtrinsic::new_bare(call).into(); + uxt.encoded_size() as u32 + }, + crate::DebugInfo::Skip, + crate::CollectEvents::Skip, + ); + self.tx.gas = ((dry_run.fee + GAS_PRICE as u64) / (GAS_PRICE as u64)).into(); + } + /// Create a new builder with a call to the given address. fn call_with(dest: H160) -> Self { let mut builder = Self::new(); builder.tx.to = Some(dest); - builder.tx.gas = U256::from(516_708u128); + builder.estimate_gas(); builder } @@ -502,7 +516,7 @@ mod test { fn instantiate_with(code: Vec, data: Vec) -> Self { let mut builder = Self::new(); builder.tx.input = Bytes(code.into_iter().chain(data.into_iter()).collect()); - builder.tx.gas = U256::from(1_035_070u128); + builder.estimate_gas(); builder } diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 9b0bbb2d6bc..11752e47cf2 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -1212,6 +1212,7 @@ where to: Some(dest), ..Default::default() }; + let eth_dispatch_call = crate::Call::::eth_transact { payload: tx.dummy_signed_payload(), gas_limit: result.gas_required, @@ -1238,7 +1239,7 @@ where ) .into(); - log::debug!(target: LOG_TARGET, "Call dry run Result: dispatch_info: {dispatch_info:?} len: {encoded_len:?} fee: {fee:?}"); + log::debug!(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(), -- GitLab From b9c3172313a9c381210bbc237888648b22269722 Mon Sep 17 00:00:00 2001 From: Andrii Date: Thu, 24 Oct 2024 15:21:52 +0300 Subject: [PATCH 37/50] Added Trusted Query API implementation for Westend and Rococo relay chains (#6212) Added missing API methods to Rococo and Westend parachains. Preparatory work for making chopstick tests run smoothly. Follow-up of [PR#6039](https://github.com/paritytech/polkadot-sdk/pull/6039) --- polkadot/runtime/rococo/src/lib.rs | 14 ++++++++++++- polkadot/runtime/westend/src/lib.rs | 14 ++++++++++++- prdoc/pr_6212.prdoc | 32 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 prdoc/pr_6212.prdoc diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index e94b6666ed0..44dd820f8c3 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -112,7 +112,10 @@ use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm::{latest::prelude::*, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; +use xcm::{ + latest::prelude::*, VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, + VersionedXcm, +}; use xcm_builder::PayOverXcm; pub use frame_system::Call as SystemCall; @@ -2622,6 +2625,15 @@ sp_api::impl_runtime_apis! { genesis_config_presets::preset_names() } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_teleporter(asset, location) + } + } } #[cfg(all(test, feature = "try-runtime"))] diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 461be186ee5..a91ca399db4 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -107,7 +107,10 @@ use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm::{latest::prelude::*, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; +use xcm::{ + latest::prelude::*, VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, + VersionedXcm, +}; use xcm_builder::PayOverXcm; use xcm_runtime_apis::{ @@ -2791,4 +2794,13 @@ sp_api::impl_runtime_apis! { genesis_config_presets::preset_names() } } + + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + XcmPallet::is_trusted_teleporter(asset, location) + } + } } diff --git a/prdoc/pr_6212.prdoc b/prdoc/pr_6212.prdoc new file mode 100644 index 00000000000..97f522025d1 --- /dev/null +++ b/prdoc/pr_6212.prdoc @@ -0,0 +1,32 @@ +# 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 Trusted Query API calls for Westend and Rococo chains" + +doc: + - audience: Runtime Dev + description: | + Added is_trusted_reserve and is_trusted_teleporter API calls to relay chains. + Given an asset and a location, they return if the chain trusts that location as a reserve or teleporter for that asset respectively. + You can implement them on your runtime by simply calling a helper function on `pallet-xcm`. + ```rust + impl xcm_runtime_apis::trusted_query::TrustedQueryApi for Runtime { + fn is_trusted_reserve(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_reserve(asset, location) + } + fn is_trusted_teleporter(asset: VersionedAsset, location: VersionedLocation) -> Result { + PolkadotXcm::is_trusted_teleporter(asset, location) + } + } + ``` + + - audience: Runtime User + description: | + There's a new runtime API to check if a chain trust a Location as a reserve or teleporter for a given Asset. + It's implemented in all the relays and system parachains in Westend and Rococo. + +crates: + - name: westend-runtime + bump: minor + - name: rococo-runtime + bump: minor -- GitLab From 5682f9a273309c7160722a7937a75152a2793b23 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 24 Oct 2024 14:27:01 +0200 Subject: [PATCH 38/50] Snowbridge: PNA Audit Better Documentation and minor Refactorings (#6216) # Description Snowbridge PNA has been audited. A number of issues where raised due to not understanding the fee model for Polkadot Native Assets(PNA) implementation. This PR addresses this by adding more comments and better naming of private functions. ## Integration None, documentation and private method name changes. --- .../primitives/router/src/inbound/mod.rs | 2 ++ .../primitives/router/src/outbound/mod.rs | 26 ++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index fbfc52d01c8..a9324ac4247 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -416,6 +416,8 @@ where // Final destination is a 32-byte account on AssetHub Destination::AccountId32 { id } => Ok(Location::new(0, [AccountId32 { network: None, id }])), + // Forwarding to a destination parachain is not allowed for PNA and is validated on the + // Ethereum side. https://github.com/Snowfork/snowbridge/blob/e87ddb2215b513455c844463a25323bb9c01ff36/contracts/src/Assets.sol#L216-L224 _ => Err(ConvertMessageError::InvalidDestination), }?; diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index efc1ef56f30..3b5dbdb77c8 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -207,9 +207,9 @@ where fn convert(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { let result = match self.peek() { - Ok(ReserveAssetDeposited { .. }) => self.send_native_tokens_message(), + Ok(ReserveAssetDeposited { .. }) => self.make_mint_foreign_token_command(), // Get withdraw/deposit and make native tokens create message. - Ok(WithdrawAsset { .. }) => self.send_tokens_message(), + Ok(WithdrawAsset { .. }) => self.make_unlock_native_token_command(), Err(e) => Err(e), _ => return Err(XcmConverterError::UnexpectedInstruction), }?; @@ -222,7 +222,9 @@ where Ok(result) } - fn send_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + fn make_unlock_native_token_command( + &mut self, + ) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; // Get the reserve assets from WithdrawAsset. @@ -271,7 +273,12 @@ where ensure!(reserve_assets.len() == 1, TooManyAssets); let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; - // If there was a fee specified verify it. + // Fees are collected on AH, up front and directly from the user, to cover the + // complete cost of the transfer. Any additional fees provided in the XCM program are + // refunded to the beneficiary. We only validate the fee here if its provided to make sure + // the XCM program is well formed. Another way to think about this from an XCM perspective + // would be that the user offered to pay X amount in fees, but we charge 0 of that X amount + // (no fee) and refund X to the user. if let Some(fee_asset) = fee_asset { // The fee asset must be the same as the reserve asset. if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { @@ -328,7 +335,9 @@ where /// # BuyExecution /// # DepositAsset /// # SetTopic - fn send_native_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + fn make_mint_foreign_token_command( + &mut self, + ) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; // Get the reserve assets. @@ -377,7 +386,12 @@ where ensure!(reserve_assets.len() == 1, TooManyAssets); let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; - // If there was a fee specified verify it. + // Fees are collected on AH, up front and directly from the user, to cover the + // complete cost of the transfer. Any additional fees provided in the XCM program are + // refunded to the beneficiary. We only validate the fee here if its provided to make sure + // the XCM program is well formed. Another way to think about this from an XCM perspective + // would be that the user offered to pay X amount in fees, but we charge 0 of that X amount + // (no fee) and refund X to the user. if let Some(fee_asset) = fee_asset { // The fee asset must be the same as the reserve asset. if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { -- GitLab From c0df223e13862851ab5a4dadc1e592acea37f88d Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:01:12 +0300 Subject: [PATCH 39/50] Enable approval-voting-parallel by default on kusama (#6218) The approval-voting-parallel introduced with https://github.com/paritytech/polkadot-sdk/pull/4849 has been tested on `versi` and approximately 3 weeks on parity's existing kusama nodes https://github.com/paritytech/devops/issues/3583, things worked as expected, so enable it by default on all kusama nodes in the next release. The next step will be enabling by default on polkadot if no issue arrises while running on kusama. --------- Signed-off-by: Alexandru Gheorghe --- polkadot/node/service/src/lib.rs | 9 ++++----- prdoc/pr_6218.prdoc | 9 +++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 prdoc/pr_6218.prdoc diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index da3ab760ed2..d6f24159e1d 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -759,13 +759,12 @@ pub fn new_full< Some(backoff) }; - // Running approval voting in parallel is enabled by default on all networks except Polkadot and - // Kusama, unless explicitly enabled by the commandline option. + // Running approval voting in parallel is enabled by default on all networks except Polkadot + // unless explicitly enabled by the commandline option. // This is meant to be temporary until we have enough confidence in the new system to enable it // by default on all networks. - let enable_approval_voting_parallel = (!config.chain_spec.is_kusama() && - !config.chain_spec.is_polkadot()) || - enable_approval_voting_parallel; + let enable_approval_voting_parallel = + !config.chain_spec.is_polkadot() || enable_approval_voting_parallel; let disable_grandpa = config.disable_grandpa; let name = config.network.node_name.clone(); diff --git a/prdoc/pr_6218.prdoc b/prdoc/pr_6218.prdoc new file mode 100644 index 00000000000..5c97c926f23 --- /dev/null +++ b/prdoc/pr_6218.prdoc @@ -0,0 +1,9 @@ +title: Enable approval-voting-parallel by default on kusama + +doc: + - audience: Node Dev + description: | + Enable approval-voting-parallel by default on kusama +crates: + - name: polkadot-service + bump: patch -- GitLab From 68af85e739b4e7589466879fb99ecd8624e0c29f Mon Sep 17 00:00:00 2001 From: Guillaume Thiolliere Date: Thu, 24 Oct 2024 16:33:59 +0200 Subject: [PATCH 40/50] pallet macro: Support instantiable pallets in tasks (#5194) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix https://github.com/paritytech/polkadot-sdk/issues/5185 also implement handling of attr in expansion in construct-runtime --------- Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Bastian Köcher --- prdoc/pr_5194.prdoc | 11 ++ .../src/construct_runtime/expand/task.rs | 57 +++++-- .../procedural/src/pallet/expand/tasks.rs | 155 +++++++----------- .../src/pallet/expand/tt_default_parts.rs | 4 +- .../procedural/src/pallet/parse/mod.rs | 7 +- .../procedural/src/pallet/parse/tasks.rs | 34 ++-- .../test/tests/pallet_ui/pass/task_valid.rs | 26 +++ .../test/tests/pallet_ui/task_invalid_gen.rs | 39 +++++ .../tests/pallet_ui/task_invalid_gen.stderr | 5 + .../test/tests/pallet_ui/task_invalid_gen2.rs | 39 +++++ .../tests/pallet_ui/task_invalid_gen2.stderr | 13 ++ substrate/frame/support/test/tests/tasks.rs | 135 +++++++++++++++ 12 files changed, 392 insertions(+), 133 deletions(-) create mode 100644 prdoc/pr_5194.prdoc create mode 100644 substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.rs create mode 100644 substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.stderr create mode 100644 substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.rs create mode 100644 substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.stderr create mode 100644 substrate/frame/support/test/tests/tasks.rs diff --git a/prdoc/pr_5194.prdoc b/prdoc/pr_5194.prdoc new file mode 100644 index 00000000000..afb9d57e79e --- /dev/null +++ b/prdoc/pr_5194.prdoc @@ -0,0 +1,11 @@ +title: "FRAME: Support instantiable pallets in tasks." + +doc: + - audience: Runtime Dev + description: | + In FRAME, tasks can now be used in instantiable pallet. Also some fix for expansion with + conditional compilation in construct runtine. + +crates: + - name: frame-support-procedural + bump: patch diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/task.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/task.rs index 6531c0e9e07..1302f86455f 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/task.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/task.rs @@ -16,6 +16,7 @@ // limitations under the License use crate::construct_runtime::Pallet; +use core::str::FromStr; use proc_macro2::{Ident, TokenStream as TokenStream2}; use quote::quote; @@ -28,7 +29,8 @@ pub fn expand_outer_task( let mut from_impls = Vec::new(); let mut task_variants = Vec::new(); let mut variant_names = Vec::new(); - let mut task_paths = Vec::new(); + let mut task_types = Vec::new(); + let mut cfg_attrs = Vec::new(); for decl in pallet_decls { if decl.find_part("Task").is_none() { continue @@ -37,18 +39,31 @@ pub fn expand_outer_task( let variant_name = &decl.name; let path = &decl.path; let index = decl.index; + let instance = decl.instance.as_ref().map(|instance| quote!(, #path::#instance)); + let task_type = quote!(#path::Task<#runtime_name #instance>); + + let attr = decl.cfg_pattern.iter().fold(TokenStream2::new(), |acc, pattern| { + let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); from_impls.push(quote! { - impl From<#path::Task<#runtime_name>> for RuntimeTask { - fn from(hr: #path::Task<#runtime_name>) -> Self { + #attr + impl From<#task_type> for RuntimeTask { + fn from(hr: #task_type) -> Self { RuntimeTask::#variant_name(hr) } } - impl TryInto<#path::Task<#runtime_name>> for RuntimeTask { + #attr + impl TryInto<#task_type> for RuntimeTask { type Error = (); - fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> { + fn try_into(self) -> Result<#task_type, Self::Error> { match self { RuntimeTask::#variant_name(hr) => Ok(hr), _ => Err(()), @@ -58,13 +73,16 @@ pub fn expand_outer_task( }); task_variants.push(quote! { + #attr #[codec(index = #index)] - #variant_name(#path::Task<#runtime_name>), + #variant_name(#task_type), }); variant_names.push(quote!(#variant_name)); - task_paths.push(quote!(#path::Task)); + task_types.push(task_type); + + cfg_attrs.push(attr); } let prelude = quote!(#scrate::traits::tasks::__private); @@ -91,35 +109,50 @@ pub fn expand_outer_task( fn is_valid(&self) -> bool { match self { - #(RuntimeTask::#variant_names(val) => val.is_valid(),)* + #( + #cfg_attrs + RuntimeTask::#variant_names(val) => val.is_valid(), + )* _ => unreachable!(#INCOMPLETE_MATCH_QED), } } fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> { match self { - #(RuntimeTask::#variant_names(val) => val.run(),)* + #( + #cfg_attrs + RuntimeTask::#variant_names(val) => val.run(), + )* _ => unreachable!(#INCOMPLETE_MATCH_QED), } } fn weight(&self) -> #scrate::pallet_prelude::Weight { match self { - #(RuntimeTask::#variant_names(val) => val.weight(),)* + #( + #cfg_attrs + RuntimeTask::#variant_names(val) => val.weight(), + )* _ => unreachable!(#INCOMPLETE_MATCH_QED), } } fn task_index(&self) -> u32 { match self { - #(RuntimeTask::#variant_names(val) => val.task_index(),)* + #( + #cfg_attrs + RuntimeTask::#variant_names(val) => val.task_index(), + )* _ => unreachable!(#INCOMPLETE_MATCH_QED), } } fn iter() -> Self::Enumeration { let mut all_tasks = Vec::new(); - #(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::>());)* + #( + #cfg_attrs + all_tasks.extend(<#task_types>::iter().map(RuntimeTask::from).collect::>()); + )* all_tasks.into_iter() } } diff --git a/substrate/frame/support/procedural/src/pallet/expand/tasks.rs b/substrate/frame/support/procedural/src/pallet/expand/tasks.rs index 7201c352d92..b6346ca8ff3 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/tasks.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/tasks.rs @@ -20,21 +20,25 @@ //! Home of the expansion code for the Tasks API use crate::pallet::{parse::tasks::*, Def}; -use derive_syn_parse::Parse; use inflector::Inflector; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote, ToTokens}; -use syn::{parse_quote, spanned::Spanned, ItemEnum, ItemImpl}; +use syn::{parse_quote_spanned, spanned::Spanned}; impl TaskEnumDef { /// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the /// event they _don't_ specify one (which is actually the most common behavior) we have to /// generate one based on the existing [`TasksDef`]. This method performs that generation. - pub fn generate( - tasks: &TasksDef, - type_decl_bounded_generics: TokenStream2, - type_use_generics: TokenStream2, - ) -> Self { + pub fn generate(tasks: &TasksDef, def: &Def) -> Self { + // We use the span of the attribute to indicate that the error comes from code generated + // for the specific section, otherwise the item impl. + let span = tasks + .tasks_attr + .as_ref() + .map_or_else(|| tasks.item_impl.span(), |attr| attr.span()); + + let type_decl_bounded_generics = def.type_decl_bounded_generics(span); + let variants = if tasks.tasks_attr.is_some() { tasks .tasks @@ -58,7 +62,8 @@ impl TaskEnumDef { } else { Vec::new() }; - let mut task_enum_def: TaskEnumDef = parse_quote! { + + parse_quote_spanned! { span => /// Auto-generated enum that encapsulates all tasks defined by this pallet. /// /// Conceptually similar to the [`Call`] enum, but for tasks. This is only @@ -69,33 +74,32 @@ impl TaskEnumDef { #variants, )* } - }; - task_enum_def.type_use_generics = type_use_generics; - task_enum_def + } } } -impl ToTokens for TaskEnumDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let item_enum = &self.item_enum; - let ident = &item_enum.ident; - let vis = &item_enum.vis; - let attrs = &item_enum.attrs; - let generics = &item_enum.generics; - let variants = &item_enum.variants; - let scrate = &self.scrate; - let type_use_generics = &self.type_use_generics; - if self.attr.is_some() { +impl TaskEnumDef { + fn expand_to_tokens(&self, def: &Def) -> TokenStream2 { + if let Some(attr) = &self.attr { + let ident = &self.item_enum.ident; + let vis = &self.item_enum.vis; + let attrs = &self.item_enum.attrs; + let generics = &self.item_enum.generics; + let variants = &self.item_enum.variants; + let frame_support = &def.frame_support; + let type_use_generics = &def.type_use_generics(attr.span()); + let type_impl_generics = &def.type_impl_generics(attr.span()); + // `item_enum` is short-hand / generated enum - tokens.extend(quote! { + quote! { #(#attrs)* #[derive( - #scrate::CloneNoBound, - #scrate::EqNoBound, - #scrate::PartialEqNoBound, - #scrate::pallet_prelude::Encode, - #scrate::pallet_prelude::Decode, - #scrate::pallet_prelude::TypeInfo, + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::pallet_prelude::Encode, + #frame_support::pallet_prelude::Decode, + #frame_support::pallet_prelude::TypeInfo, )] #[codec(encode_bound())] #[codec(decode_bound())] @@ -104,32 +108,25 @@ impl ToTokens for TaskEnumDef { #variants #[doc(hidden)] #[codec(skip)] - __Ignore(core::marker::PhantomData, #scrate::Never), + __Ignore(core::marker::PhantomData<(#type_use_generics)>, #frame_support::Never), } - impl core::fmt::Debug for #ident<#type_use_generics> { + impl<#type_impl_generics> core::fmt::Debug for #ident<#type_use_generics> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct(stringify!(#ident)).field("value", self).finish() } } - }); + } } else { // `item_enum` is a manually specified enum (no attribute) - tokens.extend(item_enum.to_token_stream()); + self.item_enum.to_token_stream() } } } -/// Represents an already-expanded [`TasksDef`]. -#[derive(Parse)] -pub struct ExpandedTasksDef { - pub task_item_impl: ItemImpl, - pub task_trait_impl: ItemImpl, -} - -impl ToTokens for TasksDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let scrate = &self.scrate; +impl TasksDef { + fn expand_to_tokens(&self, def: &Def) -> TokenStream2 { + let frame_support = &def.frame_support; let enum_ident = syn::Ident::new("Task", self.enum_ident.span()); let enum_arguments = &self.enum_arguments; let enum_use = quote!(#enum_ident #enum_arguments); @@ -160,21 +157,21 @@ impl ToTokens for TasksDef { let task_arg_names = self.tasks.iter().map(|task| &task.arg_names).collect::>(); let impl_generics = &self.item_impl.generics; - tokens.extend(quote! { + quote! { impl #impl_generics #enum_use { #(#task_fn_impls)* } - impl #impl_generics #scrate::traits::Task for #enum_use + impl #impl_generics #frame_support::traits::Task for #enum_use { - type Enumeration = #scrate::__private::IntoIter<#enum_use>; + type Enumeration = #frame_support::__private::IntoIter<#enum_use>; fn iter() -> Self::Enumeration { - let mut all_tasks = #scrate::__private::vec![]; + let mut all_tasks = #frame_support::__private::vec![]; #(all_tasks .extend(#task_iters.map(|(#(#task_arg_names),*)| #enum_ident::#task_fn_idents { #(#task_arg_names: #task_arg_names.clone()),* }) - .collect::<#scrate::__private::Vec<_>>()); + .collect::<#frame_support::__private::Vec<_>>()); )* all_tasks.into_iter() } @@ -193,7 +190,7 @@ impl ToTokens for TasksDef { } } - fn run(&self) -> Result<(), #scrate::pallet_prelude::DispatchError> { + fn run(&self) -> Result<(), #frame_support::pallet_prelude::DispatchError> { match self.clone() { #(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => { <#enum_use>::#task_fn_names(#( #task_arg_names, )* ) @@ -203,64 +200,32 @@ impl ToTokens for TasksDef { } #[allow(unused_variables)] - fn weight(&self) -> #scrate::pallet_prelude::Weight { + fn weight(&self) -> #frame_support::pallet_prelude::Weight { match self.clone() { #(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => #task_weights,)* Task::__Ignore(_, _) => unreachable!(), } } } - }); + } } } -/// Expands the [`TasksDef`] in the enclosing [`Def`], if present, and returns its tokens. -/// -/// This modifies the underlying [`Def`] in addition to returning any tokens that were added. -pub fn expand_tasks_impl(def: &mut Def) -> TokenStream2 { - let Some(tasks) = &mut def.tasks else { return quote!() }; - let ExpandedTasksDef { task_item_impl, task_trait_impl } = parse_quote!(#tasks); - quote! { - #task_item_impl - #task_trait_impl - } -} +/// Generate code related to tasks. +pub fn expand_tasks(def: &Def) -> TokenStream2 { + let Some(tasks_def) = &def.tasks else { + return quote!(); + }; -/// Represents a fully-expanded [`TaskEnumDef`]. -#[derive(Parse)] -pub struct ExpandedTaskEnum { - pub item_enum: ItemEnum, - pub debug_impl: ItemImpl, -} + let default_task_enum = TaskEnumDef::generate(&tasks_def, def); -/// Modifies a [`Def`] to expand the underlying [`TaskEnumDef`] if present, and also returns -/// its tokens. A blank [`TokenStream2`] is returned if no [`TaskEnumDef`] has been generated -/// or defined. -pub fn expand_task_enum(def: &mut Def) -> TokenStream2 { - let Some(task_enum) = &mut def.task_enum else { return quote!() }; - let ExpandedTaskEnum { item_enum, debug_impl } = parse_quote!(#task_enum); - quote! { - #item_enum - #debug_impl - } -} + let task_enum = def.task_enum.as_ref().unwrap_or_else(|| &default_task_enum); + + let tasks_expansion = tasks_def.expand_to_tokens(def); + let task_enum_expansion = task_enum.expand_to_tokens(def); -/// Modifies a [`Def`] to expand the underlying [`TasksDef`] and also generate a -/// [`TaskEnumDef`] if applicable. The tokens for these items are returned if they are created. -pub fn expand_tasks(def: &mut Def) -> TokenStream2 { - if let Some(tasks_def) = &def.tasks { - if def.task_enum.is_none() { - def.task_enum = Some(TaskEnumDef::generate( - &tasks_def, - def.type_decl_bounded_generics(tasks_def.item_impl.span()), - def.type_use_generics(tasks_def.item_impl.span()), - )); - } - } - let tasks_extra_output = expand_tasks_impl(def); - let task_enum_extra_output = expand_task_enum(def); quote! { - #tasks_extra_output - #task_enum_extra_output + #tasks_expansion + #task_enum_expansion } } diff --git a/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs b/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs index 1975f059152..6d53de3133e 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/tt_default_parts.rs @@ -33,7 +33,7 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); - let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,)); + let task_part = def.tasks.as_ref().map(|_| quote::quote!(Task,)); let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,)); @@ -85,7 +85,7 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); - let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); + let task_part_v2 = def.tasks.as_ref().map(|_| quote::quote!(+ Task)); let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); diff --git a/substrate/frame/support/procedural/src/pallet/parse/mod.rs b/substrate/frame/support/procedural/src/pallet/parse/mod.rs index 5036f691690..c9a150effcc 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/mod.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/mod.rs @@ -126,11 +126,11 @@ impl Def { }, Some(PalletAttr::RuntimeCall(cw, span)) if call.is_none() => call = Some(call::CallDef::try_from(span, index, item, dev_mode, cw)?), - Some(PalletAttr::Tasks(_)) if tasks.is_none() => { + Some(PalletAttr::Tasks(span)) if tasks.is_none() => { let item_tokens = item.to_token_stream(); // `TasksDef::parse` needs to know if attr was provided so we artificially // re-insert it here - tasks = Some(syn::parse2::(quote::quote! { + tasks = Some(syn::parse2::(quote::quote_spanned! { span => #[pallet::tasks_experimental] #item_tokens })?); @@ -404,6 +404,9 @@ impl Def { if let Some(extra_constants) = &self.extra_constants { instances.extend_from_slice(&extra_constants.instances[..]); } + if let Some(task_enum) = &self.task_enum { + instances.push(task_enum.instance_usage.clone()); + } let mut errors = instances.into_iter().filter_map(|instances| { if instances.has_instance == self.config.has_instance { diff --git a/substrate/frame/support/procedural/src/pallet/parse/tasks.rs b/substrate/frame/support/procedural/src/pallet/parse/tasks.rs index ed860849a4d..5bff64643df 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/tasks.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/tasks.rs @@ -25,8 +25,8 @@ use crate::assert_parse_error_matches; #[cfg(test)] use crate::pallet::parse::tests::simulate_manifest_dir; +use super::helper; use derive_syn_parse::Parse; -use frame_support_procedural_tools::generate_access_from_frame_or_crate; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use syn::{ @@ -34,8 +34,8 @@ use syn::{ parse2, spanned::Spanned, token::{Bracket, Paren, PathSep, Pound}, - Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path, PathArguments, - Result, TypePath, + Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, PathArguments, Result, + TypePath, }; pub mod keywords { @@ -57,8 +57,6 @@ pub struct TasksDef { pub tasks_attr: Option, pub tasks: Vec, pub item_impl: ItemImpl, - /// Path to `frame_support` - pub scrate: Path, pub enum_ident: Ident, pub enum_arguments: PathArguments, } @@ -114,11 +112,7 @@ impl syn::parse::Parse for TasksDef { let enum_ident = last_seg.ident.clone(); let enum_arguments = last_seg.arguments.clone(); - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; - - Ok(TasksDef { tasks_attr, item_impl, tasks, scrate, enum_ident, enum_arguments }) + Ok(TasksDef { tasks_attr, item_impl, tasks, enum_ident, enum_arguments }) } } @@ -146,12 +140,11 @@ pub type PalletTaskEnumAttr = PalletTaskAttr; /// Parsing for a manually-specified (or auto-generated) task enum, optionally including the /// attached `#[pallet::task_enum]` attribute. -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct TaskEnumDef { pub attr: Option, pub item_enum: ItemEnum, - pub scrate: Path, - pub type_use_generics: TokenStream2, + pub instance_usage: helper::InstanceUsage, } impl syn::parse::Parse for TaskEnumDef { @@ -163,13 +156,10 @@ impl syn::parse::Parse for TaskEnumDef { None => None, }; - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; - - let type_use_generics = quote!(T); + let instance_usage = + helper::check_type_def_gen(&item_enum.generics, item_enum.ident.span())?; - Ok(TaskEnumDef { attr, item_enum, scrate, type_use_generics }) + Ok(TaskEnumDef { attr, item_enum, instance_usage }) } } @@ -896,7 +886,7 @@ fn test_parse_task_enum_def_non_task_name() { simulate_manifest_dir("../../examples/basic", || { parse2::(quote! { #[pallet::task_enum] - pub enum Something { + pub enum Something { Foo } }) @@ -921,7 +911,7 @@ fn test_parse_task_enum_def_missing_attr_allowed() { fn test_parse_task_enum_def_missing_attr_alternate_name_allowed() { simulate_manifest_dir("../../examples/basic", || { parse2::(quote! { - pub enum Foo { + pub enum Foo { Red, } }) @@ -951,7 +941,7 @@ fn test_parse_task_enum_def_wrong_item() { assert_parse_error_matches!( parse2::(quote! { #[pallet::task_enum] - pub struct Something; + pub struct Something; }), "expected `enum`" ); diff --git a/substrate/frame/support/test/tests/pallet_ui/pass/task_valid.rs b/substrate/frame/support/test/tests/pallet_ui/pass/task_valid.rs index 234e220f49d..bc66c09de7e 100644 --- a/substrate/frame/support/test/tests/pallet_ui/pass/task_valid.rs +++ b/substrate/frame/support/test/tests/pallet_ui/pass/task_valid.rs @@ -39,5 +39,31 @@ mod pallet { } } +#[frame_support::pallet(dev_mode)] +mod pallet_with_instance { + use frame_support::pallet_prelude::{ValueQuery, StorageValue}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, u32, ValueQuery>; + + #[pallet::tasks_experimental] + impl, I> Pallet { + #[pallet::task_index(0)] + #[pallet::task_condition(|i, j| i == 0u32 && j == 2u64)] + #[pallet::task_list(vec![(0u32, 2u64), (2u32, 4u64)].iter())] + #[pallet::task_weight(0.into())] + fn foo(_i: u32, _j: u64) -> frame_support::pallet_prelude::DispatchResult { + >::get(); + Ok(()) + } + } +} + fn main() { } diff --git a/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.rs b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.rs new file mode 100644 index 00000000000..52ae19dcb02 --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.rs @@ -0,0 +1,39 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[frame_support::pallet(dev_mode)] +mod pallet_with_instance { + use frame_support::pallet_prelude::{ValueQuery, StorageValue}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, u32, ValueQuery>; + + #[pallet::task_enum] + pub enum Task {} + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for Task {} +} + +fn main() { +} diff --git a/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.stderr b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.stderr new file mode 100644 index 00000000000..1dc9e3d4aa1 --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen.stderr @@ -0,0 +1,5 @@ +error: Invalid generic declaration, trait is defined with instance but generic use none + --> tests/pallet_ui/task_invalid_gen.rs:32:11 + | +32 | pub enum Task {} + | ^^^^ diff --git a/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.rs b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.rs new file mode 100644 index 00000000000..56392cbad2d --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.rs @@ -0,0 +1,39 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[frame_support::pallet(dev_mode)] +mod pallet_with_instance { + use frame_support::pallet_prelude::{ValueQuery, StorageValue}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, u32, ValueQuery>; + + #[pallet::task_enum] + pub enum Task {} + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for Task {} +} + +fn main() { +} diff --git a/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.stderr b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.stderr new file mode 100644 index 00000000000..448825e6015 --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/task_invalid_gen2.stderr @@ -0,0 +1,13 @@ +error: Invalid type def generics: expected `T` or `T: Config` or `T, I = ()` or `T: Config, I: 'static = ()` + --> tests/pallet_ui/task_invalid_gen2.rs:32:11 + | +32 | pub enum Task {} + | ^^^^ + +error: unexpected end of input, expected `T` + --> tests/pallet_ui/task_invalid_gen2.rs:18:1 + | +18 | #[frame_support::pallet(dev_mode)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/substrate/frame/support/test/tests/tasks.rs b/substrate/frame/support/test/tests/tasks.rs new file mode 100644 index 00000000000..97e58388362 --- /dev/null +++ b/substrate/frame/support/test/tests/tasks.rs @@ -0,0 +1,135 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(feature = "experimental")] + +#[frame_support::pallet(dev_mode)] +mod my_pallet { + use frame_support::pallet_prelude::{StorageValue, ValueQuery}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, (u32, u64), ValueQuery>; + + #[pallet::tasks_experimental] + impl, I> Pallet { + #[pallet::task_index(0)] + #[pallet::task_condition(|i, j| i == 0u32 && j == 2u64)] + #[pallet::task_list(vec![(0u32, 2u64), (2u32, 4u64)].iter())] + #[pallet::task_weight(0.into())] + fn foo(i: u32, j: u64) -> frame_support::pallet_prelude::DispatchResult { + >::put((i, j)); + Ok(()) + } + } +} + +// Another pallet for which we won't implement the default instance. +#[frame_support::pallet(dev_mode)] +mod my_pallet_2 { + use frame_support::pallet_prelude::{StorageValue, ValueQuery}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type SomeStorage = StorageValue<_, (u32, u64), ValueQuery>; + + #[pallet::tasks_experimental] + impl, I> Pallet { + #[pallet::task_index(0)] + #[pallet::task_condition(|i, j| i == 0u32 && j == 2u64)] + #[pallet::task_list(vec![(0u32, 2u64), (2u32, 4u64)].iter())] + #[pallet::task_weight(0.into())] + fn foo(i: u32, j: u64) -> frame_support::pallet_prelude::DispatchResult { + >::put((i, j)); + Ok(()) + } + } +} + +type BlockNumber = u32; +type AccountId = u64; +type Header = sp_runtime::generic::Header; +type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; +type Block = sp_runtime::generic::Block; + +frame_support::construct_runtime!( + pub enum Runtime + { + System: frame_system, + MyPallet: my_pallet, + MyPallet2: my_pallet::, + #[cfg(feature = "frame-feature-testing")] + MyPallet3: my_pallet::, + MyPallet4: my_pallet_2::, + } +); + +// NOTE: Needed for derive_impl expansion +use frame_support::derive_impl; +#[frame_support::derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = Block; + type AccountId = AccountId; +} + +impl my_pallet::Config for Runtime {} + +impl my_pallet::Config for Runtime {} + +#[cfg(feature = "frame-feature-testing")] +impl my_pallet::Config for Runtime {} + +impl my_pallet_2::Config for Runtime {} + +fn new_test_ext() -> sp_io::TestExternalities { + use sp_runtime::BuildStorage; + + RuntimeGenesisConfig::default().build_storage().unwrap().into() +} + +#[test] +fn tasks_work() { + new_test_ext().execute_with(|| { + use frame_support::instances::{Instance1, Instance2}; + + let task = RuntimeTask::MyPallet(my_pallet::Task::::Foo { i: 0u32, j: 2u64 }); + + frame_support::assert_ok!(System::do_task(RuntimeOrigin::signed(1), task.clone(),)); + assert_eq!(my_pallet::SomeStorage::::get(), (0, 2)); + + let task = RuntimeTask::MyPallet2(my_pallet::Task::::Foo { i: 0u32, j: 2u64 }); + + frame_support::assert_ok!(System::do_task(RuntimeOrigin::signed(1), task.clone(),)); + assert_eq!(my_pallet::SomeStorage::::get(), (0, 2)); + + let task = + RuntimeTask::MyPallet4(my_pallet_2::Task::::Foo { i: 0u32, j: 2u64 }); + + frame_support::assert_ok!(System::do_task(RuntimeOrigin::signed(1), task.clone(),)); + assert_eq!(my_pallet_2::SomeStorage::::get(), (0, 2)); + }); +} -- GitLab From 860d93bdc90aad99a23ba403ac824db046d273f1 Mon Sep 17 00:00:00 2001 From: Javier Viola <363911+pepoviola@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:58:17 -0300 Subject: [PATCH 41/50] Disable tests reported in #6062 (#6064) Flaky tests reported in #6062 #6063 (already fixed) Thx! --- .gitlab/pipeline/zombienet/polkadot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index 60870caf26c..9a907d8d994 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -158,7 +158,7 @@ zombienet-polkadot-functional-0010-validator-disabling: --local-dir="${LOCAL_DIR}/functional" --test="0010-validator-disabling.zndsl" -zombienet-polkadot-functional-0011-async-backing-6-seconds-rate: +.zombienet-polkadot-functional-0011-async-backing-6-seconds-rate: extends: - .zombienet-polkadot-common script: -- GitLab From 0596928e649dbd7b8ea8f94cf301926555943534 Mon Sep 17 00:00:00 2001 From: Jun Jiang Date: Fri, 25 Oct 2024 04:27:55 +0800 Subject: [PATCH 42/50] Fix a tiny typo (#6229) Just fix a tiny typo --- templates/solochain/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/solochain/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs index 42361a2ff36..f2eb49592be 100644 --- a/templates/solochain/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -146,7 +146,7 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; -/// The `TransactionExtension`` to the basic transaction logic. +/// The `TransactionExtension` to the basic transaction logic. pub type TxExtension = ( frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, -- GitLab From 5d7181cda209053fda007679e34afccfa3e95130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 25 Oct 2024 00:53:03 +0200 Subject: [PATCH 43/50] pallet-message-queue: Fix max message size calculation (#6205) The max size of a message should not depend on the weight left in a given execution context. Instead the max message size depends on the service weights configured for the pallet. A message that may does not fit into `on_idle` is not automatically overweight, because it may can be executed successfully in `on_initialize` or in another block in `on_idle` when there is more weight left. --------- Co-authored-by: GitHub Action --- prdoc/pr_6205.prdoc | 8 ++ substrate/frame/message-queue/src/lib.rs | 49 ++++++++++- substrate/frame/message-queue/src/mock.rs | 2 +- substrate/frame/message-queue/src/tests.rs | 99 ++++++++++++---------- 4 files changed, 106 insertions(+), 52 deletions(-) create mode 100644 prdoc/pr_6205.prdoc diff --git a/prdoc/pr_6205.prdoc b/prdoc/pr_6205.prdoc new file mode 100644 index 00000000000..0874eb468db --- /dev/null +++ b/prdoc/pr_6205.prdoc @@ -0,0 +1,8 @@ +title: 'pallet-message-queue: Fix max message size calculation' +doc: +- audience: Runtime Dev + description: |- + The max size of a message should not depend on the weight left in a given execution context. Instead the max message size depends on the service weights configured for the pallet. A message that may does not fit into `on_idle` is not automatically overweight, because it may can be executed successfully in `on_initialize` or in another block in `on_idle` when there is more weight left. +crates: +- name: pallet-message-queue + bump: patch diff --git a/substrate/frame/message-queue/src/lib.rs b/substrate/frame/message-queue/src/lib.rs index 31402f2a9d8..04620fa88d8 100644 --- a/substrate/frame/message-queue/src/lib.rs +++ b/substrate/frame/message-queue/src/lib.rs @@ -868,13 +868,26 @@ impl Pallet { } } - /// The maximal weight that a single message can consume. + /// The maximal weight that a single message ever can consume. /// /// Any message using more than this will be marked as permanently overweight and not /// automatically re-attempted. Returns `None` if the servicing of a message cannot begin. /// `Some(0)` means that only messages with no weight may be served. fn max_message_weight(limit: Weight) -> Option { - limit.checked_sub(&Self::single_msg_overhead()) + let service_weight = T::ServiceWeight::get().unwrap_or_default(); + let on_idle_weight = T::IdleMaxServiceWeight::get().unwrap_or_default(); + + // Whatever weight is set, the one with the biggest one is used as the maximum weight. If a + // message is tried in one context and fails, it will be retried in the other context later. + let max_message_weight = + if service_weight.any_gt(on_idle_weight) { service_weight } else { on_idle_weight }; + + if max_message_weight.is_zero() { + // If no service weight is set, we need to use the given limit as max message weight. + limit.checked_sub(&Self::single_msg_overhead()) + } else { + max_message_weight.checked_sub(&Self::single_msg_overhead()) + } } /// The overhead of servicing a single message. @@ -896,6 +909,8 @@ impl Pallet { fn do_integrity_test() -> Result<(), String> { ensure!(!MaxMessageLenOf::::get().is_zero(), "HeapSize too low"); + let max_block = T::BlockWeights::get().max_block; + if let Some(service) = T::ServiceWeight::get() { if Self::max_message_weight(service).is_none() { return Err(format!( @@ -904,6 +919,31 @@ impl Pallet { Self::single_msg_overhead(), )) } + + if service.any_gt(max_block) { + return Err(format!( + "ServiceWeight {service} is bigger than max block weight {max_block}" + )) + } + } + + if let Some(on_idle) = T::IdleMaxServiceWeight::get() { + if on_idle.any_gt(max_block) { + return Err(format!( + "IdleMaxServiceWeight {on_idle} is bigger than max block weight {max_block}" + )) + } + } + + if let (Some(service_weight), Some(on_idle)) = + (T::ServiceWeight::get(), T::IdleMaxServiceWeight::get()) + { + if !(service_weight.all_gt(on_idle) || + on_idle.all_gt(service_weight) || + service_weight == on_idle) + { + return Err("One of `ServiceWeight` or `IdleMaxServiceWeight` needs to be `all_gt` or both need to be equal.".into()) + } } Ok(()) @@ -1531,7 +1571,7 @@ impl Pallet { let mut weight = WeightMeter::with_limit(weight_limit); // Get the maximum weight that processing a single message may take: - let max_weight = Self::max_message_weight(weight_limit).unwrap_or_else(|| { + let overweight_limit = Self::max_message_weight(weight_limit).unwrap_or_else(|| { if matches!(context, ServiceQueuesContext::OnInitialize) { defensive!("Not enough weight to service a single message."); } @@ -1549,7 +1589,8 @@ impl Pallet { let mut last_no_progress = None; loop { - let (progressed, n) = Self::service_queue(next.clone(), &mut weight, max_weight); + let (progressed, n) = + Self::service_queue(next.clone(), &mut weight, overweight_limit); next = match n { Some(n) => if !progressed { diff --git a/substrate/frame/message-queue/src/mock.rs b/substrate/frame/message-queue/src/mock.rs index d3f719c6235..f1d341d1a5d 100644 --- a/substrate/frame/message-queue/src/mock.rs +++ b/substrate/frame/message-queue/src/mock.rs @@ -42,7 +42,7 @@ impl frame_system::Config for Test { type Block = Block; } parameter_types! { - pub const HeapSize: u32 = 24; + pub const HeapSize: u32 = 40; pub const MaxStale: u32 = 2; pub const ServiceWeight: Option = Some(Weight::from_parts(100, 100)); } diff --git a/substrate/frame/message-queue/src/tests.rs b/substrate/frame/message-queue/src/tests.rs index b75764b67be..c81e486a40d 100644 --- a/substrate/frame/message-queue/src/tests.rs +++ b/substrate/frame/message-queue/src/tests.rs @@ -177,7 +177,7 @@ fn service_queues_failing_messages_works() { MessageQueue::enqueue_message(msg("stacklimitreached"), Here); MessageQueue::enqueue_message(msg("yield"), Here); // Starts with four pages. - assert_pages(&[0, 1, 2, 3, 4]); + assert_pages(&[0, 1, 2]); assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); assert_last_event::( @@ -209,7 +209,7 @@ fn service_queues_failing_messages_works() { assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); assert_eq!(System::events().len(), 4); // Last page with the `yield` stays in. - assert_pages(&[4]); + assert_pages(&[2]); }); } @@ -313,7 +313,7 @@ fn reap_page_permanent_overweight_works() { // Create 10 pages more than the stale limit. let n = (MaxStale::get() + 10) as usize; for _ in 0..n { - MessageQueue::enqueue_message(msg("weight=2"), Here); + MessageQueue::enqueue_message(msg("weight=200 datadatadata"), Here); } assert_eq!(Pages::::iter().count(), n); assert_eq!(MessageQueue::footprint(Here).pages, n as u32); @@ -334,7 +334,7 @@ fn reap_page_permanent_overweight_works() { break } assert_ok!(MessageQueue::do_reap_page(&Here, i)); - assert_eq!(QueueChanges::take(), vec![(Here, b.message_count - 1, b.size - 8)]); + assert_eq!(QueueChanges::take(), vec![(Here, b.message_count - 1, b.size - 23)]); } // Cannot reap any more pages. @@ -353,20 +353,20 @@ fn reaping_overweight_fails_properly() { build_and_execute::(|| { // page 0 - MessageQueue::enqueue_message(msg("weight=4"), Here); + MessageQueue::enqueue_message(msg("weight=200 datadata"), Here); MessageQueue::enqueue_message(msg("a"), Here); // page 1 - MessageQueue::enqueue_message(msg("weight=4"), Here); + MessageQueue::enqueue_message(msg("weight=200 datadata"), Here); MessageQueue::enqueue_message(msg("b"), Here); // page 2 - MessageQueue::enqueue_message(msg("weight=4"), Here); + MessageQueue::enqueue_message(msg("weight=200 datadata"), Here); MessageQueue::enqueue_message(msg("c"), Here); // page 3 - MessageQueue::enqueue_message(msg("bigbig 1"), Here); + MessageQueue::enqueue_message(msg("bigbig 1 datadata"), Here); // page 4 - MessageQueue::enqueue_message(msg("bigbig 2"), Here); + MessageQueue::enqueue_message(msg("bigbig 2 datadata"), Here); // page 5 - MessageQueue::enqueue_message(msg("bigbig 3"), Here); + MessageQueue::enqueue_message(msg("bigbig 3 datadata"), Here); // Double-check that exactly these pages exist. assert_pages(&[0, 1, 2, 3, 4, 5]); @@ -385,7 +385,7 @@ fn reaping_overweight_fails_properly() { // 3 stale now: can take something 4 pages in history. assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); - assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 1"), Here)]); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 1 datadata"), Here)]); // Nothing reapable yet, because we haven't hit the stale limit. for (o, i, _) in Pages::::iter() { @@ -394,7 +394,7 @@ fn reaping_overweight_fails_properly() { assert_pages(&[0, 1, 2, 4, 5]); assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); - assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 2"), Here)]); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 2 datadata"), Here)]); assert_pages(&[0, 1, 2, 5]); // First is now reapable as it is too far behind the first ready page (5). @@ -406,7 +406,7 @@ fn reaping_overweight_fails_properly() { assert_pages(&[1, 2, 5]); assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight()); - assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 3"), Here)]); + assert_eq!(MessagesProcessed::take(), vec![(vmsg("bigbig 3 datadata"), Here)]); assert_noop!(MessageQueue::do_reap_page(&Here, 0), Error::::NoPage); assert_noop!(MessageQueue::do_reap_page(&Here, 3), Error::::NoPage); @@ -1062,29 +1062,29 @@ fn footprint_on_swept_works() { fn footprint_num_pages_works() { use MessageOrigin::*; build_and_execute::(|| { - MessageQueue::enqueue_message(msg("weight=2"), Here); - MessageQueue::enqueue_message(msg("weight=3"), Here); + MessageQueue::enqueue_message(msg("weight=200"), Here); + MessageQueue::enqueue_message(msg("weight=300"), Here); - assert_eq!(MessageQueue::footprint(Here), fp(2, 2, 2, 16)); + assert_eq!(MessageQueue::footprint(Here), fp(1, 1, 2, 20)); // Mark the messages as overweight. assert_eq!(MessageQueue::service_queues(1.into_weight()), 0.into_weight()); assert_eq!(System::events().len(), 2); // `ready_pages` decreases but `page` count does not. - assert_eq!(MessageQueue::footprint(Here), fp(2, 0, 2, 16)); + assert_eq!(MessageQueue::footprint(Here), fp(1, 0, 2, 20)); // Now execute the second message. assert_eq!( - ::execute_overweight(3.into_weight(), (Here, 1, 0)) + ::execute_overweight(300.into_weight(), (Here, 0, 1)) .unwrap(), - 3.into_weight() + 300.into_weight() ); - assert_eq!(MessageQueue::footprint(Here), fp(1, 0, 1, 8)); + assert_eq!(MessageQueue::footprint(Here), fp(1, 0, 1, 10)); // And the first one: assert_eq!( - ::execute_overweight(2.into_weight(), (Here, 0, 0)) + ::execute_overweight(200.into_weight(), (Here, 0, 0)) .unwrap(), - 2.into_weight() + 200.into_weight() ); assert_eq!(MessageQueue::footprint(Here), Default::default()); assert_eq!(MessageQueue::footprint(Here), fp(0, 0, 0, 0)); @@ -1104,7 +1104,7 @@ fn execute_overweight_works() { // Enqueue a message let origin = MessageOrigin::Here; - MessageQueue::enqueue_message(msg("weight=6"), origin); + MessageQueue::enqueue_message(msg("weight=200"), origin); // Load the current book let book = BookStateFor::::get(origin); assert_eq!(book.message_count, 1); @@ -1112,10 +1112,10 @@ fn execute_overweight_works() { // Mark the message as permanently overweight. assert_eq!(MessageQueue::service_queues(4.into_weight()), 4.into_weight()); - assert_eq!(QueueChanges::take(), vec![(origin, 1, 8)]); + assert_eq!(QueueChanges::take(), vec![(origin, 1, 10)]); assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"weight=6"), + id: blake2_256(b"weight=200"), origin: MessageOrigin::Here, message_index: 0, page_index: 0, @@ -1132,9 +1132,9 @@ fn execute_overweight_works() { assert_eq!(Pages::::iter().count(), 1); assert!(QueueChanges::take().is_empty()); let consumed = - ::execute_overweight(7.into_weight(), (origin, 0, 0)) + ::execute_overweight(200.into_weight(), (origin, 0, 0)) .unwrap(); - assert_eq!(consumed, 6.into_weight()); + assert_eq!(consumed, 200.into_weight()); assert_eq!(QueueChanges::take(), vec![(origin, 0, 0)]); // There is no message left in the book. let book = BookStateFor::::get(origin); @@ -1162,7 +1162,7 @@ fn permanently_overweight_book_unknits() { set_weight("service_queue_base", 1.into_weight()); set_weight("service_page_base_completion", 1.into_weight()); - MessageQueue::enqueue_messages([msg("weight=9")].into_iter(), Here); + MessageQueue::enqueue_messages([msg("weight=200")].into_iter(), Here); // It is the only ready book. assert_ring(&[Here]); @@ -1170,7 +1170,7 @@ fn permanently_overweight_book_unknits() { assert_eq!(MessageQueue::service_queues(8.into_weight()), 4.into_weight()); assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"weight=9"), + id: blake2_256(b"weight=200"), origin: Here, message_index: 0, page_index: 0, @@ -1201,19 +1201,19 @@ fn permanently_overweight_book_unknits_multiple() { set_weight("service_page_base_completion", 1.into_weight()); MessageQueue::enqueue_messages( - [msg("weight=1"), msg("weight=9"), msg("weight=9")].into_iter(), + [msg("weight=1"), msg("weight=200"), msg("weight=200")].into_iter(), Here, ); assert_ring(&[Here]); // Process the first message. assert_eq!(MessageQueue::service_queues(4.into_weight()), 4.into_weight()); - assert_eq!(num_overweight_enqueued_events(), 0); + assert_eq!(num_overweight_enqueued_events(), 1); assert_eq!(MessagesProcessed::take().len(), 1); // Book is still ready since it was not marked as overweight yet. assert_ring(&[Here]); - assert_eq!(MessageQueue::service_queues(8.into_weight()), 5.into_weight()); + assert_eq!(MessageQueue::service_queues(8.into_weight()), 4.into_weight()); assert_eq!(num_overweight_enqueued_events(), 2); assert_eq!(MessagesProcessed::take().len(), 0); // Now it is overweight. @@ -1566,12 +1566,12 @@ fn service_queues_suspend_works() { fn execute_overweight_respects_suspension() { build_and_execute::(|| { let origin = MessageOrigin::Here; - MessageQueue::enqueue_message(msg("weight=5"), origin); + MessageQueue::enqueue_message(msg("weight=200"), origin); // Mark the message as permanently overweight. MessageQueue::service_queues(4.into_weight()); assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"weight=5"), + id: blake2_256(b"weight=200"), origin, message_index: 0, page_index: 0, @@ -1598,9 +1598,9 @@ fn execute_overweight_respects_suspension() { assert_last_event::( Event::Processed { - id: blake2_256(b"weight=5").into(), + id: blake2_256(b"weight=200").into(), origin, - weight_used: 5.into_weight(), + weight_used: 200.into_weight(), success: true, } .into(), @@ -1768,7 +1768,7 @@ fn recursive_overweight_while_service_is_forbidden() { // Check that the message was permanently overweight. assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"weight=10"), + id: blake2_256(b"weight=200"), origin: There, message_index: 0, page_index: 0, @@ -1786,13 +1786,13 @@ fn recursive_overweight_while_service_is_forbidden() { Ok(()) })); - MessageQueue::enqueue_message(msg("weight=10"), There); + MessageQueue::enqueue_message(msg("weight=200"), There); MessageQueue::enqueue_message(msg("callback=0"), Here); // Mark it as permanently overweight. MessageQueue::service_queues(5.into_weight()); assert_ok!(::execute_overweight( - 10.into_weight(), + 200.into_weight(), (There, 0, 0) )); }); @@ -1812,7 +1812,7 @@ fn recursive_reap_page_is_forbidden() { // Create 10 pages more than the stale limit. let n = (MaxStale::get() + 10) as usize; for _ in 0..n { - MessageQueue::enqueue_message(msg("weight=2"), Here); + MessageQueue::enqueue_message(msg("weight=200"), Here); } // Mark all pages as stale since their message is permanently overweight. @@ -1886,6 +1886,11 @@ fn process_enqueued_on_idle_requires_enough_weight() { // Not enough weight to process on idle. Pallet::::on_idle(1, Weight::from_parts(0, 0)); assert_eq!(MessagesProcessed::take(), vec![]); + + assert!(!System::events().into_iter().any(|e| matches!( + e.event, + RuntimeEvent::MessageQueue(Event::::OverweightEnqueued { .. }) + ))); }) } @@ -1923,12 +1928,12 @@ fn execute_overweight_keeps_stack_ov_message() { // We need to create a mocked message that first reports insufficient weight, and then // `StackLimitReached`: IgnoreStackOvError::set(true); - MessageQueue::enqueue_message(msg("stacklimitreached"), Here); + MessageQueue::enqueue_message(msg("weight=200 stacklimitreached"), Here); MessageQueue::service_queues(0.into_weight()); assert_last_event::( Event::OverweightEnqueued { - id: blake2_256(b"stacklimitreached"), + id: blake2_256(b"weight=200 stacklimitreached"), origin: MessageOrigin::Here, message_index: 0, page_index: 0, @@ -1952,7 +1957,7 @@ fn execute_overweight_keeps_stack_ov_message() { ); assert_last_event::( Event::ProcessingFailed { - id: blake2_256(b"stacklimitreached").into(), + id: blake2_256(b"weight=200 stacklimitreached").into(), origin: MessageOrigin::Here, error: ProcessMessageError::StackLimitReached, } @@ -1964,16 +1969,16 @@ fn execute_overweight_keeps_stack_ov_message() { // Now let's process it normally: IgnoreStackOvError::set(true); assert_eq!( - ::execute_overweight(1.into_weight(), (Here, 0, 0)) + ::execute_overweight(200.into_weight(), (Here, 0, 0)) .unwrap(), - 1.into_weight() + 200.into_weight() ); assert_last_event::( Event::Processed { - id: blake2_256(b"stacklimitreached").into(), + id: blake2_256(b"weight=200 stacklimitreached").into(), origin: MessageOrigin::Here, - weight_used: 1.into_weight(), + weight_used: 200.into_weight(), success: true, } .into(), -- GitLab From 7e99621108b2a98644c73a22eaceb732b1c0f743 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:42:07 +0200 Subject: [PATCH 44/50] `RuntimeGenesiConfig`: json macro added (#5813) This PR adds `build_struct_json_patch` which helps to generate a JSON used for preset. Here is doc and example: https://github.com/paritytech/polkadot-sdk/blob/d868b858758d3886d16c8ba8feb3c93c188044f3/substrate/frame/support/src/generate_genesis_config.rs#L168-L266 And real-world usage: https://github.com/paritytech/polkadot-sdk/blob/d868b858758d3886d16c8ba8feb3c93c188044f3/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs#L37-L61 Closes #5700 --------- Co-authored-by: Sebastian Kunert --- Cargo.lock | 2 + .../src/genesis_config_presets.rs | 17 +- .../src/reference_docs/chain_spec_genesis.rs | 22 +- .../chain_spec_runtime/Cargo.toml | 2 + .../chain_spec_runtime/src/pallets.rs | 2 +- .../chain_spec_runtime/src/presets.rs | 42 +- .../tests/chain_spec_builder_tests.rs | 19 +- .../westend/src/genesis_config_presets.rs | 17 +- prdoc/pr_5813.prdoc | 18 + substrate/frame/support/Cargo.toml | 1 + .../support/src/generate_genesis_config.rs | 951 ++++++++++++++++++ substrate/frame/support/src/lib.rs | 6 +- 12 files changed, 1044 insertions(+), 55 deletions(-) create mode 100644 prdoc/pr_5813.prdoc create mode 100644 substrate/frame/support/src/generate_genesis_config.rs diff --git a/Cargo.lock b/Cargo.lock index 602891892a2..335c465e0aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2824,6 +2824,7 @@ name = "chain-spec-guide-runtime" version = "0.0.0" dependencies = [ "docify", + "frame-support", "pallet-balances", "pallet-sudo", "pallet-timestamp", @@ -6442,6 +6443,7 @@ dependencies = [ name = "frame-support" version = "28.0.0" dependencies = [ + "Inflector", "aquamarine", "array-bytes", "assert_matches", 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 dc98d00f8f6..d15dd971a81 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 @@ -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; @@ -33,15 +34,14 @@ fn asset_hub_rococo_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_ROCOCO_ED * 16, - ..Default::default() }, session: SessionConfig { keys: invulnerables @@ -54,16 +54,9 @@ fn asset_hub_rococo_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. diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 3326f433f28..b7a0a648d0c 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -100,17 +100,22 @@ //! others useful for testing. //! //! Internally, presets can be provided in a number of ways: -//! - JSON in string form: -#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)] -//! - JSON using runtime types to serialize values: +//! - using [`build_struct_json_patch`] macro (**recommended**): #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_2)] +//! - JSON using runtime types to serialize values: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)] +//! - JSON in string form: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)] +//! //! It is worth noting that a preset does not have to be the full `RuntimeGenesisConfig`, in that //! sense that it does not have to contain all the keys of the struct. The preset is actually a JSON //! patch that will be merged with the default value of `RuntimeGenesisConfig`. This approach should //! simplify maintenance of built-in presets. The following example illustrates a runtime genesis -//! config patch: +//! config patch with a single key built using [`build_struct_json_patch`] macro: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_4)] +//! This results in the following JSON blob: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", preset_4_json)] +//! //! //! ## Note on the importance of testing presets //! @@ -122,8 +127,8 @@ //! //! ## Note on the importance of using the `deny_unknown_fields` attribute //! -//! It is worth noting that it is easy to make a hard-to-spot mistake, as in the following example -//! ([`FooStruct`] does not contain `fieldC`): +//! It is worth noting that when manually building preset JSON blobs it is easy to make a +//! hard-to-spot mistake, as in the following example ([`FooStruct`] does not contain `fieldC`): #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_invalid)] //! Even though `preset_invalid` contains a key that does not exist, the deserialization of the JSON //! blob does not fail. The misspelling is silently ignored due to the lack of the @@ -131,6 +136,10 @@ //! `GenesisConfig`. #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", invalid_preset_works)] //! +//! To avoid this problem [`build_struct_json_patch`] macro shall be used whenever possible (it +//! internally instantiates the struct before serializang it JSON blob, so all unknown fields shall +//! be caught at compilation time). +//! //! ## Runtime `GenesisConfig` raw format //! //! A raw format of genesis config contains just the state's keys and values as they are stored in @@ -182,6 +191,7 @@ //! [`get_preset`]: frame_support::genesis_builder_helper::get_preset //! [`pallet::genesis_build`]: frame_support::pallet_macros::genesis_build //! [`pallet::genesis_config`]: frame_support::pallet_macros::genesis_config +//! [`build_struct_json_patch`]: frame_support::build_struct_json_patch //! [`BuildGenesisConfig`]: frame_support::traits::BuildGenesisConfig //! [`serde`]: https://serde.rs/field-attrs.html //! [`get_storage_for_patch`]: sc_chain_spec::GenesisConfigBuilderRuntimeCaller::get_storage_for_patch diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml index 02849571203..07c0342f5fb 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml @@ -12,6 +12,7 @@ publish = false [dependencies] docify = { workspace = true } codec = { workspace = true } +frame-support = { workspace = true } scale-info = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } @@ -49,6 +50,7 @@ std = [ "codec/std", "scale-info/std", + "frame-support/std", "frame/std", "pallet-balances/std", diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs index 2ff2d9539e2..571632ecd27 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs @@ -118,7 +118,7 @@ pub mod pallet_foo { pub some_enum: FooEnum, pub some_struct: FooStruct, #[serde(skip)] - _phantom: PhantomData, + pub _phantom: PhantomData, } #[pallet::genesis_build] 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 02c2d90f7c8..c1316b2f873 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 @@ -17,8 +17,12 @@ //! Presets for the chain-spec demo runtime. -use crate::pallets::{FooEnum, SomeFooData1, SomeFooData2}; +use crate::{ + pallets::{FooEnum, SomeFooData1, SomeFooData2}, + runtime::{BarConfig, FooConfig, RuntimeGenesisConfig}, +}; use alloc::vec; +use frame_support::build_struct_json_patch; use serde_json::{json, to_string, Value}; use sp_application_crypto::Ss58Codec; use sp_keyring::AccountKeyring; @@ -27,7 +31,7 @@ use sp_keyring::AccountKeyring; pub const PRESET_1: &str = "preset_1"; /// A demo preset with real types. pub const PRESET_2: &str = "preset_2"; -/// Another demo preset with real types. +/// Another demo preset with real types and manually created json object. pub const PRESET_3: &str = "preset_3"; /// A single value patch preset. pub const PRESET_4: &str = "preset_4"; @@ -58,21 +62,21 @@ fn preset_1() -> Value { } #[docify::export] -/// Function provides a preset demonstrating how use the actual types to create a preset. +/// Function provides a preset demonstrating how to create a preset using +/// [`build_struct_json_patch`] macro. fn preset_2() -> Value { - json!({ - "bar": { - "initialAccount": AccountKeyring::Ferdie.public().to_ss58check(), - }, - "foo": { - "someEnum": FooEnum::Data2(SomeFooData2 { values: vec![12,16] }), - "someInteger": 200 + build_struct_json_patch!(RuntimeGenesisConfig { + foo: FooConfig { + some_integer: 200, + some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] }) }, + bar: BarConfig { initial_account: Some(AccountKeyring::Ferdie.public().into()) }, }) } #[docify::export] -/// Function provides a preset demonstrating how use the actual types to create a preset. +/// Function provides a preset demonstrating how use the actual types to manually create a JSON +/// representing the preset. fn preset_3() -> Value { json!({ "bar": { @@ -92,22 +96,16 @@ fn preset_3() -> Value { #[docify::export] /// Function provides a minimal preset demonstrating how to patch single key in -/// `RuntimeGenesisConfig`. -fn preset_4() -> Value { - json!({ - "foo": { - "someEnum": { - "Data2": { - "values": "0x0c0f" - } - }, - }, +/// `RuntimeGenesisConfig` using [`build_struct_json_patch`] macro. +pub fn preset_4() -> Value { + build_struct_json_patch!(RuntimeGenesisConfig { + foo: FooConfig { some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] }) }, }) } #[docify::export] /// Function provides an invalid preset demonstrating how important is use of -/// [`deny_unknown_fields`] in data structures used in `GenesisConfig`. +/// `deny_unknown_fields` in data structures used in `GenesisConfig`. fn preset_invalid() -> Value { json!({ "foo": { diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs index cc273685fcb..c2fe5a6727e 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs @@ -58,7 +58,7 @@ fn get_preset() { let output: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); - //note: copy of chain_spec_guide_runtime::preset_1 + //note: copy of chain_spec_guide_runtime::preset_2 let expected_output = json!({ "bar": { "initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", @@ -186,3 +186,20 @@ fn generate_para_chain_spec() { }); assert_eq!(output, expected_output, "Output did not match expected"); } + +#[test] +#[docify::export] +fn preset_4_json() { + assert_eq!( + chain_spec_guide_runtime::presets::preset_4(), + json!({ + "foo": { + "someEnum": { + "Data2": { + "values": "0x0c10" + } + }, + }, + }) + ); +} diff --git a/polkadot/runtime/westend/src/genesis_config_presets.rs b/polkadot/runtime/westend/src/genesis_config_presets.rs index 621ef38f0b7..a9e22898ddb 100644 --- a/polkadot/runtime/westend/src/genesis_config_presets.rs +++ b/polkadot/runtime/westend/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 pallet_staking::{Forcing, StakerStatus}; use polkadot_primitives::{AccountId, AssignmentId, SchedulerParams, ValidatorId}; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; @@ -170,7 +171,7 @@ fn westend_testnet_genesis( 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().map(|k| (k.clone(), ENDOWMENT)).collect::>(), }, @@ -192,7 +193,6 @@ fn westend_testnet_genesis( ) }) .collect::>(), - ..Default::default() }, staking: StakingConfig { minimum_validator_count: 1, @@ -204,19 +204,12 @@ fn westend_testnet_genesis( invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::>(), force_era: Forcing::NotForcing, 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(root_key) }, 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 }, + }) } // staging_testnet diff --git a/prdoc/pr_5813.prdoc b/prdoc/pr_5813.prdoc new file mode 100644 index 00000000000..e48f29bbfb6 --- /dev/null +++ b/prdoc/pr_5813.prdoc @@ -0,0 +1,18 @@ +title: "build_struct_json_patch macro added" + +doc: + - audience: Runtime Dev + description: | + This PR adds a macro that allows to construct a RuntimeGenesisConfig preset + containing only provided fields, while performing the validation of the + entire struct. + + Related issue: https://github.com/paritytech/polkadot-sdk/issues/5700 + +crates: + - name: frame-support + bump: minor + - name: asset-hub-rococo-runtime + bump: patch + - name: westend-runtime + bump: patch diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index 18ca4a3acda..d7da034b349 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -71,6 +71,7 @@ pretty_assertions = { workspace = true } sp-timestamp = { workspace = true } frame-system = { workspace = true, default-features = true } sp-crypto-hashing = { workspace = true, default-features = true } +Inflector = { workspace = true } [features] default = ["std"] diff --git a/substrate/frame/support/src/generate_genesis_config.rs b/substrate/frame/support/src/generate_genesis_config.rs new file mode 100644 index 00000000000..fc21e76c742 --- /dev/null +++ b/substrate/frame/support/src/generate_genesis_config.rs @@ -0,0 +1,951 @@ +// 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. + +//! Helper macro allowing to construct JSON representation of partially initialized structs. + +use serde_json::Value; +extern crate alloc; +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`). +/// +/// Intended to be used in `build_struct_json_patch` macro. +#[derive(Debug)] +pub enum InitializedField<'a> { + /// 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>), +} + +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, + } + } + + /// 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(), + }; + } + + /// Creates new partial field instiance. + pub fn partial(s: &'a str) -> Self { + Self::Partial(s.into()) + } + + /// Creates new full field instiance. + pub fn full(s: &'a str) -> Self { + Self::Full(s.into()) + } +} + +impl PartialEq for InitializedField<'_> { + fn eq(&self, other: &String) -> bool { + #[inline] + /// We need to respect the `camelCase` naming for field names. This means that + /// `"camelCaseKey"` should be considered equal to `"camel_case_key"`. This + /// function implements this comparison. + fn compare_keys(ident_chars: core::str::Chars, camel_chars: core::str::Chars) -> bool { + ident_chars + .filter(|c| *c != '_') + .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()), + } + } +} + +/// Recursively removes keys from provided `json_value` object, retaining only specified keys. +/// +/// This function modifies the provided `json_value` in-place, keeping only the keys listed in +/// `keys_to_retain`. The keys are matched recursively by combining the current key with +/// the `current_root`, allowing for nested field retention. +/// +/// Keys marked as `Full`, are retained as-is. For keys marked as `Partial`, the +/// function recurses into nested objects to retain matching subfields. +/// +/// Function respects the `camelCase` serde_json attribute for structures. This means that +/// `"camelCaseKey"` key will be retained in JSON blob if `"camel_case_key"` exists in +/// `keys_to_retain`. +/// +/// Intended to be used from `build_struct_json_patch` macro. +pub fn retain_initialized_fields( + json_value: &mut Value, + keys_to_retain: &[InitializedField], + current_root: String, +) { + if let serde_json::Value::Object(ref mut map) = json_value { + map.retain(|key, value| { + 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(_)) => { + retain_initialized_fields(value, keys_to_retain, current_key.clone()); + true + }, + None => false, + } + }) + } +} + +/// Creates a JSON patch for given `struct_type`, supporting recursive field initialization. +/// +/// This macro creates a default `struct_type`, initializing specified fields (which can be nested) +/// with the provided values. Any fields not explicitly given are initialized with their default +/// values. The macro then serializes the fully initialized structure into a JSON blob, retaining +/// only the fields that were explicitly provided, either partially or fully initialized. +/// +/// Using this macro prevents errors from manually creating JSON objects, such as typos or +/// inconsistencies with the `struct_type` structure, by relying on the actual +/// struct definition. This ensures the generated JSON is valid and reflects any future changes +/// to the structure. +/// +/// # Example +/// +/// ```rust +/// use frame_support::build_struct_json_patch; +/// #[derive(Default, serde::Serialize, serde::Deserialize)] +/// #[serde(rename_all = "camelCase")] +/// struct RuntimeGenesisConfig { +/// a_field: u32, +/// b_field: B, +/// c_field: u32, +/// } +/// +/// #[derive(Default, serde::Serialize, serde::Deserialize)] +/// #[serde(rename_all = "camelCase")] +/// struct B { +/// i_field: u32, +/// j_field: u32, +/// } +/// impl B { +/// fn new() -> Self { +/// Self { i_field: 0, j_field: 2 } +/// } +/// } +/// +/// assert_eq!( +/// build_struct_json_patch! ( RuntimeGenesisConfig { +/// a_field: 66, +/// }), +/// serde_json::json!({ +/// "aField": 66, +/// }) +/// ); +/// +/// assert_eq!( +/// build_struct_json_patch! ( RuntimeGenesisConfig { +/// //"partial" initialization of `b_field` +/// b_field: B { +/// i_field: 2, +/// } +/// }), +/// serde_json::json!({ +/// "bField": {"iField": 2} +/// }) +/// ); +/// +/// assert_eq!( +/// build_struct_json_patch! ( RuntimeGenesisConfig { +/// a_field: 66, +/// //"full" initialization of `b_field` +/// b_field: B::new() +/// }), +/// serde_json::json!({ +/// "aField": 66, +/// "bField": {"iField": 0, "jField": 2} +/// }) +/// ); +/// ``` +/// +/// In this example: +/// ```ignore +/// build_struct_json_patch! ( RuntimeGenesisConfig { +/// b_field: B { +/// i_field: 2, +/// } +/// }), +/// ``` +/// `b_field` is partially initialized, it will be expanded to: +/// ```ignore +/// RuntimeGenesisConfig { +/// b_field { +/// i_field: 2, +/// ..Default::default() +/// }, +/// ..Default::default() +/// } +/// ``` +/// While all other fields are initialized with default values. The macro serializes this, retaining +/// only the provided fields. +#[macro_export] +macro_rules! build_struct_json_patch { + ( + $($struct_type:ident)::+ { $($tail:tt)* } + ) => { + { + 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 + } + }; + ($($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 @ $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() + } + }, + ..Default::default() + } + }; + ($($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: $($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 + }, + .. $crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*) + } + }; + ($($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 + }, + ..$crate::build_struct_json_patch!($($struct_type)::+, $all_keys @ $($tail)*) + } + }; + ($($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() + } + }; + + ($($struct_type:ident)::+, $all_keys:ident @ $(,)?) => { + $($struct_type)::+ { ..Default::default() } + }; +} + +#[cfg(test)] +mod test { + mod nested_mod { + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + pub struct InsideMod { + pub a: u32, + pub b: u32, + } + + pub mod nested_mod2 { + pub mod nested_mod3 { + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + pub struct InsideMod3 { + pub a: u32, + pub b: u32, + pub s: super::super::InsideMod, + } + } + } + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct TestStruct { + a: u32, + b: u32, + s: S, + s3: S3, + t3: S3, + i: Nested1, + e: E, + t: nested_mod::InsideMod, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3, + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct S { + x: u32, + } + + impl S { + fn new(c: u32) -> Self { + Self { x: c } + } + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct E(u8); + + #[derive(Default, Debug, serde::Serialize, serde::Deserialize)] + enum SomeEnum { + #[default] + A, + B(T), + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct S3 { + x: u32, + y: u32, + z: u32, + } + + impl S3 { + fn new(c: u32) -> Self { + Self { x: c, y: c, z: c } + } + + fn new_from_s(s: S) -> Self { + Self { x: s.x, ..Default::default() } + } + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct Nested3 { + a: u32, + b: u32, + s: S, + v: Vec<(u32, u32, u32, SomeEnum)>, + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct Nested2 { + a: u32, + iii: Nested3, + v: Vec, + s3: S3, + } + + impl Nested2 { + fn new(a: u32) -> Self { + Nested2 { + a, + v: vec![a, a, a], + iii: Nested3 { a, b: a, ..Default::default() }, + s3: S3 { x: a, ..Default::default() }, + } + } + } + + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] + struct Nested1 { + a: u32, + ii: Nested2, + } + + 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); + }}; + } + + #[test] + fn test_generate_config_macro() { + let t = 5; + const C: u32 = 5; + test!(TestStruct { b: 5 }, { "b": 5 }); + #[allow(unused_braces)] + { + test!(TestStruct { b: { 4 + 34 } } , { "b": 38 }); + } + test!(TestStruct { s: S { x: 5 } }, { "s": { "x": 5 } }); + test!( + TestStruct { s: S::new(C) }, + { + "s": { "x": 5 } + } + ); + test!( + TestStruct { s: S { x: t } }, + { + "s": { "x": t } + } + ); + test!( + TestStruct { + b: 5, + s: S { x: t } + }, + { + "b": 5, + "s": { "x": 5 } + } + ); + test!( + TestStruct { s: S::new(C), b: 5 }, + { + "s": { "x": 5 }, "b": 5 + } + ); + test!( + TestStruct { s3: S3 { x: t } }, + { + "s3": { "x": 5 } + } + ); + test!( + TestStruct { + s3: S3 { x: t, y: 2 } + }, + { + "s3": { "x": 5, "y": 2 } + } + ); + // // + test!( + TestStruct { + s3: S3 { x: t, y: 2 }, + t3: S3 { x: 2 } + }, + { + "s3": { "x": t, "y": 2 }, + "t3": { "x": 2 } + } + + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { iii: Nested3 { a: 2 } } + } + } + , + { + "i": { + "ii": { "iii": { "a": 2 } } + } + } + + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + iii: Nested3 { a: 2, s: S::new(C) } + } + } + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5} } + } + } + } + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + iii: Nested3 { s: S::new(C), a: 2 } + }, + a: 44 + }, + a: 3, + s3: S3 { x: 5 }, + b: 4 + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5} } + }, + "a": 44 + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2::new(66), + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4 + }, + { + "i": { + "ii": { + "a": 66, + "s3": { "x":66, "y": 0, "z": 0 }, + "iii": { "a": 66,"b":66, "s": { "x": 0 }, "v": Vec::::default() }, + "v": vec![66,66,66] + }, + "a": 44 + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + a: 66, + s3: S3 { x: 66 }, + iii: Nested3 { + a: 66,b:66 + }, + v: vec![66,66,66] + }, + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4 + }, + { + "i": { + "ii": { + "a": 66, + "s3": { "x":66, }, + "iii": { "a": 66,"b":66, }, + "v": vec![66,66,66] + }, + "a": 44 + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + iii: Nested3 { a: 2, s: S::new(C) }, + }, + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4, + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5 } }, + }, + "a" : 44, + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + s3: S3::new(5), + iii: Nested3 { a: 2, s: S::new(C) }, + }, + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4, + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5 } }, + "s3": {"x": 5, "y": 5, "z": 5 } + }, + "a" : 44, + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + test!( + TestStruct { + a: 3, + s3: S3 { x: 5 }, + b: 4, + i: Nested1 { + ii: Nested2 { + iii: Nested3 { a: 2, s: S::new(C) }, + s3: S3::new_from_s(S { x: 4 }) + }, + a: 44, + } + }, + { + "i": { + "ii": { + "iii": { "a": 2, "s": { "x": 5 } }, + "s3": {"x": 4, "y": 0, "z": 0 } + }, + "a" : 44, + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + let i = [0u32, 1u32, 2u32]; + test!( + TestStruct { + i: Nested1 { + ii: Nested2 { + iii: Nested3 { + a: 2, + s: S::new(C), + v: i.iter() + .map(|x| (*x, 2 * x, 100 + x, SomeEnum::::A)) + .collect::>(), + }, + s3: S3::new_from_s(S { x: 4 }) + }, + a: 44, + }, + a: 3, + s3: S3 { x: 5 }, + b: 4, + }, + + { + "i": { + "ii": { + "iii": { + "a": 2, + "s": { "x": 5 }, + "v": i.iter() + .map(|x| (*x, 2 * x, 100 + x, SomeEnum::::A)) + .collect::>(), + }, + "s3": {"x": 4, "y": 0, "z": 0 } + }, + "a" : 44, + }, + "a": 3, + "s3": { "x": 5 }, + "b": 4 + } + ); + } + + #[test] + fn test_generate_config_macro_with_nested_mods() { + test!( + TestStruct { t: nested_mod::InsideMod { a: 32 } }, + { + "t" : { "a": 32 } + } + ); + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { a: 32 } + }, + { + "t" : { "a": 32 }, + "u" : { "a": 32 } + } + ); + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3 { + a: 32, + s: nested_mod::InsideMod { a: 34 }, + } + }, + { + "t" : { "a": 32 }, + "u" : { "a": 32, "s": { "a": 34 } } + } + ); + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3::default() + }, + { + "t" : { "a": 32 }, + "u" : { "a": 0, "b": 0, "s": { "a": 0, "b": 0} } + } + ); + + let i = [0u32, 1u32, 2u32]; + const C: u32 = 5; + test!( + TestStruct { + t: nested_mod::InsideMod { a: 32 }, + u: nested_mod::nested_mod2::nested_mod3::InsideMod3::default(), + i: Nested1 { + ii: Nested2 { + iii: Nested3 { + a: 2, + s: S::new(C), + v: i.iter() + .map(|x| (*x, 2 * x, 100 + x, SomeEnum::::A)) + .collect::>(), + }, + s3: S3::new_from_s(S { x: 4 }) + }, + a: 44, + }, + }, + { + "t" : { "a": 32 }, + "u" : { "a": 0, "b": 0, "s": { "a": 0, "b": 0} } , + "i": { + "ii": { + "iii": { + "a": 2, + "s": { "x": 5 }, + "v": i.iter() + .map(|x| (*x, 2 * x, 100 + x, SomeEnum::::A)) + .collect::>(), + }, + "s3": {"x": 4, "y": 0, "z": 0 } + }, + "a" : 44, + }, + } + ); + } +} + +#[cfg(test)] +mod retain_keys_test { + use super::*; + use serde_json::json; + + macro_rules! check_initialized_field_eq_cc( + ( $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)* ]) => { + let field = InitializedField::full( + concat!( $f $(,".",$r)+ ) + ); + let cc = [ $f $(,$r)+ ].into_iter() + .map(|s| inflector::cases::camelcase::to_camel_case(s)) + .collect::>() + .join("."); + println!("field: {:?}, cc: {}", field, cc); + assert_eq!(field,cc); + } ; + ); + + #[test] + fn test_initialized_field_eq_cc_string() { + check_initialized_field_eq_cc!("a_"); + check_initialized_field_eq_cc!("abc"); + check_initialized_field_eq_cc!("aBc"); + check_initialized_field_eq_cc!("aBC"); + check_initialized_field_eq_cc!("ABC"); + check_initialized_field_eq_cc!("2abs"); + check_initialized_field_eq_cc!("2Abs"); + check_initialized_field_eq_cc!("2ABs"); + check_initialized_field_eq_cc!("2aBs"); + check_initialized_field_eq_cc!("AlreadyCamelCase"); + check_initialized_field_eq_cc!("alreadyCamelCase"); + check_initialized_field_eq_cc!("C"); + check_initialized_field_eq_cc!("1a"); + check_initialized_field_eq_cc!("_1a"); + check_initialized_field_eq_cc!("a_b"); + check_initialized_field_eq_cc!("_a_b"); + check_initialized_field_eq_cc!("a___b"); + check_initialized_field_eq_cc!("__a_b"); + check_initialized_field_eq_cc!("_a___b_C"); + check_initialized_field_eq_cc!("__A___B_C"); + check_initialized_field_eq_cc!(&["a_b", "b_c"]); + check_initialized_field_eq_cc!(&["al_pha", "_a___b_C"]); + check_initialized_field_eq_cc!(&["al_pha_", "_a___b_C"]); + check_initialized_field_eq_cc!(&["first_field", "al_pha_", "_a___b_C"]); + check_initialized_field_eq_cc!(&["al_pha_", "__2nd_field", "_a___b_C"]); + check_initialized_field_eq_cc!(&["al_pha_", "__2nd3and_field", "_a___b_C"]); + check_initialized_field_eq_cc!(&["_a1", "_a2", "_a3_"]); + } + + #[test] + fn test01() { + let mut v = json!({ + "a":1 + }); + let e = v.clone(); + retain_initialized_fields(&mut v, &[InitializedField::full("a")], String::default()); + assert_eq!(e, v); + } + + #[test] + fn test02() { + let mut v = json!({ + "a":1 + }); + retain_initialized_fields(&mut v, &[InitializedField::full("b")], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test03() { + let mut v = json!({}); + retain_initialized_fields(&mut v, &[], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test04() { + let mut v = json!({}); + retain_initialized_fields(&mut v, &[InitializedField::full("b")], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test05() { + let mut v = json!({ + "a":1 + }); + retain_initialized_fields(&mut v, &[], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test06() { + let mut v = json!({ + "a": { + "b":1, + "c":2 + } + }); + retain_initialized_fields(&mut v, &[], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test07() { + let mut v = json!({ + "a": { + "b":1, + "c":2 + } + }); + retain_initialized_fields(&mut v, &[InitializedField::full("a.b")], String::default()); + assert_eq!(Value::Object(Default::default()), v); + } + + #[test] + fn test08() { + let mut v = json!({ + "a": { + "b":1, + "c":2 + } + }); + let e = json!({ + "a": { + "b":1, + } + }); + retain_initialized_fields( + &mut v, + &[InitializedField::partial("a"), InitializedField::full("a.b")], + String::default(), + ); + assert_eq!(e, v); + } + + #[test] + fn test09() { + let mut v = json!({ + "a": { + "b":1, + "c":2 + } + }); + let e = json!({ + "a": { + "b":1, + "c":2, + } + }); + retain_initialized_fields(&mut v, &[InitializedField::full("a")], String::default()); + assert_eq!(e, v); + } +} diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index cc805d72485..2e7ea0a07d7 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -53,6 +53,7 @@ pub mod __private { pub use paste; pub use scale_info; pub use serde; + pub use serde_json; pub use sp_core::{Get, OpaqueMetadata, Void}; pub use sp_crypto_hashing_proc_macro; pub use sp_inherents; @@ -2588,9 +2589,12 @@ sp_core::generate_feature_enabled_macro!(try_runtime_enabled, feature = "try-run sp_core::generate_feature_enabled_macro!(try_runtime_or_std_enabled, any(feature = "try-runtime", feature = "std"), $); sp_core::generate_feature_enabled_macro!(try_runtime_and_std_not_enabled, all(not(feature = "try-runtime"), not(feature = "std")), $); -// Helper for implementing GenesisBuilder runtime API +/// Helper for implementing GenesisBuilder runtime API pub mod genesis_builder_helper; +/// Helper for generating the `RuntimeGenesisConfig` instance for presets. +pub mod generate_genesis_config; + #[cfg(test)] mod test { // use super::*; -- GitLab From 5a142856520fd450b6be8361a50e1f4d385e8a6c Mon Sep 17 00:00:00 2001 From: "Shoyu Vanilla (Flint)" Date: Fri, 25 Oct 2024 18:00:15 +0900 Subject: [PATCH 45/50] substrate-offchain: upgrade hyper to v1 (#5919) Closes #4896 --- Cargo.lock | 67 +++++--- Cargo.toml | 9 +- polkadot/node/service/src/lib.rs | 6 +- prdoc/pr_5919.prdoc | 19 +++ substrate/bin/node/cli/src/service.rs | 14 +- substrate/client/offchain/Cargo.toml | 10 +- substrate/client/offchain/src/api.rs | 2 +- substrate/client/offchain/src/api/http.rs | 183 +++++++++++++--------- substrate/client/offchain/src/lib.rs | 11 +- templates/minimal/node/src/service.rs | 12 +- templates/parachain/node/src/service.rs | 12 +- templates/solochain/node/src/service.rs | 12 +- 12 files changed, 211 insertions(+), 146 deletions(-) create mode 100644 prdoc/pr_5919.prdoc diff --git a/Cargo.lock b/Cargo.lock index 335c465e0aa..3d5193fe0ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7501,16 +7501,17 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", "hyper 1.3.1", "hyper-util", "log", - "rustls 0.23.10", + "rustls 0.23.14", + "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -8148,7 +8149,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee-core 0.23.2", "pin-project", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pki-types", "rustls-platform-verifier", "soketto 0.8.0", @@ -8173,7 +8174,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee-core 0.24.3", "pin-project", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pki-types", "rustls-platform-verifier", "soketto 0.8.0", @@ -8287,11 +8288,11 @@ dependencies = [ "base64 0.22.1", "http-body 1.0.0", "hyper 1.3.1", - "hyper-rustls 0.27.2", + "hyper-rustls 0.27.3", "hyper-util", "jsonrpsee-core 0.24.3", "jsonrpsee-types 0.24.3", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-platform-verifier", "serde", "serde_json", @@ -17083,7 +17084,7 @@ dependencies = [ "quinn-proto 0.11.8", "quinn-udp 0.5.4", "rustc-hash 2.0.0", - "rustls 0.23.10", + "rustls 0.23.14", "socket2 0.5.7", "thiserror", "tokio", @@ -17117,7 +17118,7 @@ dependencies = [ "rand", "ring 0.17.7", "rustc-hash 2.0.0", - "rustls 0.23.10", + "rustls 0.23.14", "slab", "thiserror", "tinyvec", @@ -17610,7 +17611,7 @@ dependencies = [ "http-body 1.0.0", "http-body-util", "hyper 1.3.1", - "hyper-rustls 0.27.2", + "hyper-rustls 0.27.3", "hyper-util", "ipnet", "js-sys", @@ -17620,7 +17621,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn 0.11.5", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pemfile 2.0.0", "rustls-pki-types", "serde", @@ -18199,22 +18200,22 @@ dependencies = [ "log", "ring 0.17.7", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.8", "subtle 2.5.0", "zeroize", ] [[package]] name = "rustls" -version = "0.23.10" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "log", "once_cell", "ring 0.17.7", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.8", "subtle 2.5.0", "zeroize", ] @@ -18244,6 +18245,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.0.0", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.3" @@ -18265,9 +18279,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-platform-verifier" @@ -18280,10 +18294,10 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-native-certs 0.7.0", "rustls-platform-verifier-android", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.8", "security-framework", "security-framework-sys", "webpki-roots 0.26.3", @@ -18308,9 +18322,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring 0.17.7", "rustls-pki-types", @@ -19485,14 +19499,17 @@ dependencies = [ "fnv", "futures", "futures-timer", - "hyper 0.14.29", - "hyper-rustls 0.24.2", + "http-body-util", + "hyper 1.3.1", + "hyper-rustls 0.27.3", + "hyper-util", "log", "num_cpus", "once_cell", "parity-scale-codec", "parking_lot 0.12.3", "rand", + "rustls 0.23.14", "sc-block-builder", "sc-client-api", "sc-client-db", @@ -24976,7 +24993,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pki-types", "tokio", ] @@ -25700,7 +25717,7 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.10", + "rustls 0.23.14", "rustls-pki-types", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 6ba91de3c09..dd1fa40e146 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -757,6 +757,8 @@ femme = { version = "2.2.1" } filetime = { version = "0.2.16" } finality-grandpa = { version = "0.16.2", default-features = false } finality-relay = { path = "bridges/relays/finality" } +first-pallet = { package = "polkadot-sdk-docs-first-pallet", path = "docs/sdk/packages/guides/first-pallet", default-features = false } +first-runtime = { package = "polkadot-sdk-docs-first-runtime", path = "docs/sdk/packages/guides/first-runtime", default-features = false } flate2 = { version = "1.0" } fnv = { version = "1.0.6" } fork-tree = { path = "substrate/utils/fork-tree", default-features = false } @@ -805,12 +807,8 @@ http = { version = "1.1" } http-body = { version = "1", default-features = false } http-body-util = { version = "0.1.2", default-features = false } hyper = { version = "1.3.1", default-features = false } -hyper-rustls = { version = "0.24.2" } +hyper-rustls = { version = "0.27.3", default-features = false, features = ["http1", "http2", "logging", "ring", "rustls-native-certs", "tls12"] } hyper-util = { version = "0.1.5", default-features = false } -# TODO: remove hyper v0.14 https://github.com/paritytech/polkadot-sdk/issues/4896 -first-pallet = { package = "polkadot-sdk-docs-first-pallet", path = "docs/sdk/packages/guides/first-pallet", default-features = false } -first-runtime = { package = "polkadot-sdk-docs-first-runtime", path = "docs/sdk/packages/guides/first-runtime", default-features = false } -hyperv14 = { package = "hyper", version = "0.14.29", default-features = false } impl-serde = { version = "0.5.0", default-features = false } impl-trait-for-tuples = { version = "0.2.2" } indexmap = { version = "2.0.0" } @@ -1127,6 +1125,7 @@ rstest = { version = "0.18.2" } rustc-hash = { version = "1.1.0" } rustc-hex = { version = "2.1.0", default-features = false } rustix = { version = "0.36.7", default-features = false } +rustls = { version = "0.23.14", default-features = false, features = ["logging", "ring", "std", "tls12"] } rustversion = { version = "1.0.17" } rusty-fork = { version = "0.3.0", default-features = false } safe-mix = { version = "1.0", default-features = false } diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index d6f24159e1d..abba91a38a9 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -1044,7 +1044,7 @@ pub fn new_full< is_validator: role.is_authority(), enable_http_requests: false, custom_extensions: move |_| vec![], - }) + })? .run(client.clone(), task_manager.spawn_handle()) .boxed(), ); @@ -1436,7 +1436,7 @@ pub fn new_chain_ops( } else if config.chain_spec.is_kusama() { chain_ops!(config, None) } else if config.chain_spec.is_westend() { - return chain_ops!(config, None) + return chain_ops!(config, None); } else { chain_ops!(config, None) } @@ -1488,7 +1488,7 @@ pub fn revert_backend( let revertible = blocks.min(best_number - finalized); if revertible == 0 { - return Ok(()) + return Ok(()); } let number = best_number - revertible; diff --git a/prdoc/pr_5919.prdoc b/prdoc/pr_5919.prdoc new file mode 100644 index 00000000000..1b48a24a9e2 --- /dev/null +++ b/prdoc/pr_5919.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: "substrate-offchain: upgrade hyper to v1" + +doc: + - audience: Node Dev + description: | + Bump depencency `hyper` of `substrait-offchain` for http from `0.14` to `1`. + This changed APIs a bit; + - `sc_offchain::Offchainworker::new()` now returns `std::io::Result` (Previously was `Self`) + +crates: + - name: sc-offchain + bump: major + - name: polkadot-service + bump: patch + - name: staging-node-cli + bump: patch diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 7b166f94bcc..057e0bbdcef 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -790,9 +790,7 @@ pub fn new_full_base::Hash>>( ); if enable_offchain_worker { - task_manager.spawn_handle().spawn( - "offchain-workers-runner", - "offchain-work", + let offchain_workers = sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { runtime_api_provider: client.clone(), keystore: Some(keystore_container.keystore()), @@ -806,9 +804,11 @@ pub fn new_full_base::Hash>>( custom_extensions: move |_| { vec![Box::new(statement_store.clone().as_statement_store_ext()) as Box<_>] }, - }) - .run(client.clone(), task_manager.spawn_handle()) - .boxed(), + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-work", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), ); } @@ -992,7 +992,7 @@ mod tests { sc_consensus_babe::authorship::claim_slot(slot.into(), &epoch, &keystore) .map(|(digest, _)| digest) { - break (babe_pre_digest, epoch_descriptor) + break (babe_pre_digest, epoch_descriptor); } slot += 1; diff --git a/substrate/client/offchain/Cargo.toml b/substrate/client/offchain/Cargo.toml index bbbe7018d10..71b40211e12 100644 --- a/substrate/client/offchain/Cargo.toml +++ b/substrate/client/offchain/Cargo.toml @@ -22,15 +22,15 @@ codec = { features = ["derive"], workspace = true, default-features = true } fnv = { workspace = true } futures = { workspace = true } futures-timer = { workspace = true } -hyperv14 = { features = [ - "http2", - "stream", -], workspace = true, default-features = true } -hyper-rustls = { features = ["http2"], workspace = true } +http-body-util = { workspace = true } +hyper = { features = ["http1", "http2"], workspace = true, default-features = true } +hyper-rustls = { workspace = true } +hyper-util = { features = ["client-legacy", "http1", "http2"], workspace = true } num_cpus = { workspace = true } once_cell = { workspace = true } parking_lot = { workspace = true, default-features = true } rand = { workspace = true, default-features = true } +rustls = { workspace = true } threadpool = { workspace = true } tracing = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } diff --git a/substrate/client/offchain/src/api.rs b/substrate/client/offchain/src/api.rs index 19ccdbcf498..a5981f14c09 100644 --- a/substrate/client/offchain/src/api.rs +++ b/substrate/client/offchain/src/api.rs @@ -326,7 +326,7 @@ mod tests { fn offchain_api() -> (Api, AsyncApi) { sp_tracing::try_init_simple(); let mock = Arc::new(TestNetwork()); - let shared_client = SharedClient::new(); + let shared_client = SharedClient::new().unwrap(); AsyncApi::new(mock, false, shared_client) } diff --git a/substrate/client/offchain/src/api/http.rs b/substrate/client/offchain/src/api/http.rs index 73407b1359d..56f5c023009 100644 --- a/substrate/client/offchain/src/api/http.rs +++ b/substrate/client/offchain/src/api/http.rs @@ -27,14 +27,14 @@ //! (i.e.: the socket should continue being processed) in the background even if the runtime isn't //! actively calling any function. -use hyperv14 as hyper; - use crate::api::timestamp; use bytes::buf::{Buf, Reader}; use fnv::FnvHashMap; use futures::{channel::mpsc, future, prelude::*}; -use hyper::{client, Body, Client as HyperClient}; +use http_body_util::{combinators::BoxBody, StreamBody}; +use hyper::body::Body as _; use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; +use hyper_util::{client::legacy as client, rt::TokioExecutor}; use once_cell::sync::Lazy; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_core::offchain::{HttpError, HttpRequestId, HttpRequestStatus, Timestamp}; @@ -48,21 +48,26 @@ use std::{ const LOG_TARGET: &str = "offchain-worker::http"; +pub type Body = BoxBody; + +type Sender = mpsc::Sender, hyper::Error>>; +type Receiver = mpsc::Receiver, hyper::Error>>; + +type HyperClient = client::Client, Body>; +type LazyClient = Lazy HyperClient + Send>>; + /// Wrapper struct used for keeping the hyper_rustls client running. #[derive(Clone)] -pub struct SharedClient(Arc, Body>>>); +pub struct SharedClient(Arc); impl SharedClient { - pub fn new() -> Self { - Self(Arc::new(Lazy::new(|| { - let connector = HttpsConnectorBuilder::new() - .with_native_roots() - .https_or_http() - .enable_http1() - .enable_http2() - .build(); - HyperClient::builder().build(connector) - }))) + pub fn new() -> std::io::Result { + let builder = HttpsConnectorBuilder::new() + .with_provider_and_native_roots(rustls::crypto::ring::default_provider())?; + Ok(Self(Arc::new(Lazy::new(Box::new(|| { + let connector = builder.https_or_http().enable_http1().enable_http2().build(); + client::Client::builder(TokioExecutor::new()).build(connector) + }))))) } } @@ -105,23 +110,23 @@ pub struct HttpApi { /// One active request within `HttpApi`. enum HttpApiRequest { /// The request object is being constructed locally and not started yet. - NotDispatched(hyper::Request, hyper::body::Sender), + NotDispatched(hyper::Request, Sender), /// The request has been dispatched and we're in the process of sending out the body (if the /// field is `Some`) or waiting for a response (if the field is `None`). - Dispatched(Option), + Dispatched(Option), /// Received a response. Response(HttpApiRequestRp), /// A request has been dispatched but the worker notified us of an error. We report this /// failure to the user as an `IoError` and remove the request from the list as soon as /// possible. - Fail(hyper::Error), + Fail(client::Error), } /// A request within `HttpApi` that has received a response. struct HttpApiRequestRp { /// We might still be writing the request's body when the response comes. /// This field allows to continue writing that body. - sending_body: Option, + sending_body: Option, /// Status code of the response. status_code: hyper::StatusCode, /// Headers of the response. @@ -132,7 +137,7 @@ struct HttpApiRequestRp { /// Elements extracted from the channel are first put into `current_read_chunk`. /// If the channel produces an error, then that is translated into an `IoError` and the request /// is removed from the list. - body: stream::Fuse>>, + body: stream::Fuse, /// Chunk that has been extracted from the channel and that is currently being read. /// Reading data from the response should read from this field in priority. current_read_chunk: Option>, @@ -144,7 +149,9 @@ impl HttpApi { // Start by building the prototype of the request. // We do this first so that we don't touch anything in `self` if building the prototype // fails. - let (body_sender, body) = hyper::Body::channel(); + let (body_sender, receiver) = mpsc::channel(0); + let body = StreamBody::new(receiver); + let body = BoxBody::new(body); let mut request = hyper::Request::new(body); *request.method_mut() = hyper::Method::from_bytes(method.as_bytes()).map_err(|_| ())?; *request.uri_mut() = hyper::Uri::from_maybe_shared(uri.to_owned()).map_err(|_| ())?; @@ -158,7 +165,7 @@ impl HttpApi { target: LOG_TARGET, "Overflow in offchain worker HTTP request ID assignment" ); - return Err(()) + return Err(()); }, }; self.requests @@ -213,7 +220,7 @@ impl HttpApi { // Closure that writes data to a sender, taking the deadline into account. Can return `Ok` // (if the body has been written), or `DeadlineReached`, or `IoError`. // If `IoError` is returned, don't forget to remove the request from the list. - let mut poll_sender = move |sender: &mut hyper::body::Sender| -> Result<(), HttpError> { + let mut poll_sender = move |sender: &mut Sender| -> Result<(), HttpError> { let mut when_ready = future::maybe_done(future::poll_fn(|cx| sender.poll_ready(cx))); futures::executor::block_on(future::select(&mut when_ready, &mut deadline)); match when_ready { @@ -221,12 +228,15 @@ impl HttpApi { future::MaybeDone::Done(Err(_)) => return Err(HttpError::IoError), future::MaybeDone::Future(_) | future::MaybeDone::Gone => { debug_assert!(matches!(deadline, future::MaybeDone::Done(..))); - return Err(HttpError::DeadlineReached) + return Err(HttpError::DeadlineReached); }, }; futures::executor::block_on( - sender.send_data(hyper::body::Bytes::from(chunk.to_owned())), + async { + future::poll_fn(|cx| sender.poll_ready(cx)).await?; + sender.start_send(Ok(hyper::body::Frame::data(hyper::body::Bytes::from(chunk.to_owned())))) + } ) .map_err(|_| { tracing::error!(target: "offchain-worker::http", "HTTP sender refused data despite being ready"); @@ -250,13 +260,13 @@ impl HttpApi { match poll_sender(&mut sender) { Err(HttpError::IoError) => { tracing::debug!(target: LOG_TARGET, id = %request_id.0, "Encountered io error while trying to add new chunk to body"); - return Err(HttpError::IoError) + return Err(HttpError::IoError); }, other => { tracing::debug!(target: LOG_TARGET, id = %request_id.0, res = ?other, "Added chunk to body"); self.requests .insert(request_id, HttpApiRequest::Dispatched(Some(sender))); - return other + return other; }, } } else { @@ -265,7 +275,7 @@ impl HttpApi { // Writing an empty body is a hint that we should stop writing. Dropping // the sender. self.requests.insert(request_id, HttpApiRequest::Dispatched(None)); - return Ok(()) + return Ok(()); } }, @@ -281,13 +291,13 @@ impl HttpApi { ) { Err(HttpError::IoError) => { tracing::debug!(target: LOG_TARGET, id = %request_id.0, "Encountered io error while trying to add new chunk to body"); - return Err(HttpError::IoError) + return Err(HttpError::IoError); }, other => { tracing::debug!(target: LOG_TARGET, id = %request_id.0, res = ?other, "Added chunk to body"); self.requests .insert(request_id, HttpApiRequest::Response(response)); - return other + return other; }, } } else { @@ -302,7 +312,7 @@ impl HttpApi { ..response }), ); - return Ok(()) + return Ok(()); } }, @@ -311,7 +321,7 @@ impl HttpApi { // If the request has already failed, return without putting back the request // in the list. - return Err(HttpError::IoError) + return Err(HttpError::IoError); }, v @ HttpApiRequest::Dispatched(None) | @@ -320,7 +330,7 @@ impl HttpApi { // We have already finished sending this body. self.requests.insert(request_id, v); - return Err(HttpError::Invalid) + return Err(HttpError::Invalid); }, } } @@ -340,7 +350,7 @@ impl HttpApi { Some(HttpApiRequest::Dispatched(sending_body)) | Some(HttpApiRequest::Response(HttpApiRequestRp { sending_body, .. })) => { let _ = sending_body.take(); - continue + continue; }, _ => continue, }; @@ -405,7 +415,7 @@ impl HttpApi { }, } } - return output + return output; } } @@ -418,7 +428,7 @@ impl HttpApi { msg } else { debug_assert!(matches!(deadline, future::MaybeDone::Done(..))); - continue + continue; } }; @@ -458,7 +468,7 @@ impl HttpApi { None => { tracing::error!(target: "offchain-worker::http", "Worker has crashed"); - return ids.iter().map(|_| HttpRequestStatus::IoError).collect() + return ids.iter().map(|_| HttpRequestStatus::IoError).collect(); }, } } @@ -498,14 +508,14 @@ impl HttpApi { // and we still haven't received a response. Some(rq @ HttpApiRequest::Dispatched(_)) => { self.requests.insert(request_id, rq); - return Err(HttpError::DeadlineReached) + return Err(HttpError::DeadlineReached); }, // The request has failed. Some(HttpApiRequest::Fail { .. }) => return Err(HttpError::IoError), // Request hasn't been dispatched yet; reading the body is invalid. Some(rq @ HttpApiRequest::NotDispatched(_, _)) => { self.requests.insert(request_id, rq); - return Err(HttpError::Invalid) + return Err(HttpError::Invalid); }, None => return Err(HttpError::Invalid), }; @@ -526,12 +536,12 @@ impl HttpApi { ..response }), ); - return Ok(n) + return Ok(n); }, Err(err) => { // This code should never be reached unless there's a logic error somewhere. tracing::error!(target: "offchain-worker::http", "Failed to read from current read chunk: {:?}", err); - return Err(HttpError::IoError) + return Err(HttpError::IoError); }, } } @@ -544,7 +554,10 @@ impl HttpApi { if let future::MaybeDone::Done(next_body) = next_body { match next_body { - Some(Ok(chunk)) => response.current_read_chunk = Some(chunk.reader()), + Some(Ok(chunk)) => + if let Ok(chunk) = chunk.into_data() { + response.current_read_chunk = Some(chunk.reader()); + }, Some(Err(_)) => return Err(HttpError::IoError), None => return Ok(0), // eof } @@ -552,7 +565,7 @@ impl HttpApi { if let future::MaybeDone::Done(_) = deadline { self.requests.insert(request_id, HttpApiRequest::Response(response)); - return Err(HttpError::DeadlineReached) + return Err(HttpError::DeadlineReached); } } } @@ -587,7 +600,7 @@ enum ApiToWorker { /// ID to send back when the response comes back. id: HttpRequestId, /// Request to start executing. - request: hyper::Request, + request: hyper::Request, }, } @@ -608,14 +621,14 @@ enum WorkerToApi { /// the next item. /// Can also be used to send an error, in case an error happened on the HTTP socket. After /// an error is sent, the channel will close. - body: mpsc::Receiver>, + body: Receiver, }, /// A request has failed because of an error. The request is then no longer valid. Fail { /// The ID that was passed to the worker. id: HttpRequestId, /// Error that happened. - error: hyper::Error, + error: client::Error, }, } @@ -626,7 +639,7 @@ pub struct HttpWorker { /// Used to receive messages from the `HttpApi`. from_api: TracingUnboundedReceiver, /// The engine that runs HTTP requests. - http_client: Arc, Body>>>, + http_client: Arc, /// HTTP requests that are being worked on by the engine. requests: Vec<(HttpRequestId, HttpWorkerRequest)>, } @@ -634,13 +647,13 @@ pub struct HttpWorker { /// HTTP request being processed by the worker. enum HttpWorkerRequest { /// Request has been dispatched and is waiting for a response from the Internet. - Dispatched(hyper::client::ResponseFuture), + Dispatched(client::ResponseFuture), /// Progressively reading the body of the response and sending it to the channel. ReadBody { /// Body to read `Chunk`s from. Only used if the channel is ready to accept data. - body: hyper::Body, + body: Body, /// Channel to the [`HttpApi`] where we send the chunks to. - tx: mpsc::Sender>, + tx: Sender, }, } @@ -663,12 +676,12 @@ impl Future for HttpWorker { let response = match Future::poll(Pin::new(&mut future), cx) { Poll::Pending => { me.requests.push((id, HttpWorkerRequest::Dispatched(future))); - continue + continue; }, Poll::Ready(Ok(response)) => response, Poll::Ready(Err(error)) => { let _ = me.to_api.unbounded_send(WorkerToApi::Fail { id, error }); - continue // don't insert the request back + continue; // don't insert the request back }, }; @@ -684,9 +697,12 @@ impl Future for HttpWorker { body: body_rx, }); - me.requests.push((id, HttpWorkerRequest::ReadBody { body, tx: body_tx })); + me.requests.push(( + id, + HttpWorkerRequest::ReadBody { body: Body::new(body), tx: body_tx }, + )); cx.waker().wake_by_ref(); // reschedule in order to poll the new future - continue + continue; }, HttpWorkerRequest::ReadBody { mut body, mut tx } => { @@ -697,12 +713,11 @@ impl Future for HttpWorker { Poll::Ready(Err(_)) => continue, // don't insert the request back Poll::Pending => { me.requests.push((id, HttpWorkerRequest::ReadBody { body, tx })); - continue + continue; }, } - // `tx` is ready. Read a chunk from the socket and send it to the channel. - match Stream::poll_next(Pin::new(&mut body), cx) { + match Pin::new(&mut body).poll_frame(cx) { Poll::Ready(Some(Ok(chunk))) => { let _ = tx.start_send(Ok(chunk)); me.requests.push((id, HttpWorkerRequest::ReadBody { body, tx })); @@ -762,19 +777,22 @@ mod tests { }; use crate::api::timestamp; use core::convert::Infallible; - use futures::{future, StreamExt}; + use futures::future; + use http_body_util::BodyExt; use sp_core::offchain::{Duration, Externalities, HttpError, HttpRequestId, HttpRequestStatus}; use std::sync::LazyLock; // Using LazyLock to avoid spawning lots of different SharedClients, // as spawning a SharedClient is CPU-intensive and opens lots of fds. - static SHARED_CLIENT: LazyLock = LazyLock::new(|| SharedClient::new()); + static SHARED_CLIENT: LazyLock = LazyLock::new(|| SharedClient::new().unwrap()); // Returns an `HttpApi` whose worker is ran in the background, and a `SocketAddr` to an HTTP // server that runs in the background as well. macro_rules! build_api_server { () => { - build_api_server!(hyper::Response::new(hyper::Body::from("Hello World!"))) + build_api_server!(hyper::Response::new(http_body_util::Full::new( + hyper::body::Bytes::from("Hello World!") + ))) }; ( $response:expr ) => {{ let hyper_client = SHARED_CLIENT.clone(); @@ -785,21 +803,32 @@ mod tests { let rt = tokio::runtime::Runtime::new().unwrap(); let worker = rt.spawn(worker); let server = rt.spawn(async move { - let server = hyper::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve( - hyper::service::make_service_fn(|_| async move { - Ok::<_, Infallible>(hyper::service::service_fn( - move |req: hyper::Request| async move { - // Wait until the complete request was received and processed, - // otherwise the tests are flaky. - let _ = req.into_body().collect::>().await; - - Ok::<_, Infallible>($response) - }, - )) - }), - ); - let _ = addr_tx.send(server.local_addr()); - server.await.map_err(drop) + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 0)); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + let _ = addr_tx.send(listener.local_addr().unwrap()); + loop { + let (stream, _) = listener.accept().await.unwrap(); + let io = hyper_util::rt::TokioIo::new(stream); + tokio::task::spawn(async move { + if let Err(err) = hyper::server::conn::http1::Builder::new() + .serve_connection( + io, + hyper::service::service_fn( + move |req: hyper::Request| async move { + // Wait until the complete request was received and + // processed, otherwise the tests are flaky. + let _ = req.into_body().collect().await; + + Ok::<_, Infallible>($response) + }, + ), + ) + .await + { + eprintln!("Error serving connection: {:?}", err); + } + }); + } }); let _ = rt.block_on(future::join(worker, server)); }); @@ -839,7 +868,7 @@ mod tests { let (mut api, addr) = build_api_server!(hyper::Response::builder() .version(hyper::Version::HTTP_2) - .body(hyper::Body::from("Hello World!")) + .body(http_body_util::Full::new(hyper::body::Bytes::from("Hello World!"))) .unwrap()); let id = api.request_start("POST", &format!("http://{}", addr)).unwrap(); @@ -1097,7 +1126,7 @@ mod tests { #[test] fn shared_http_client_is_only_initialized_on_access() { - let shared_client = SharedClient::new(); + let shared_client = SharedClient::new().unwrap(); { let mock = Arc::new(TestNetwork()); @@ -1112,7 +1141,7 @@ mod tests { // Check that the http client wasn't initialized, because it wasn't used. assert!(Lazy::into_value(Arc::try_unwrap(shared_client.0).unwrap()).is_err()); - let shared_client = SharedClient::new(); + let shared_client = SharedClient::new().unwrap(); { let mock = Arc::new(TestNetwork()); diff --git a/substrate/client/offchain/src/lib.rs b/substrate/client/offchain/src/lib.rs index 3d5728aad17..b0a7a66520b 100644 --- a/substrate/client/offchain/src/lib.rs +++ b/substrate/client/offchain/src/lib.rs @@ -153,14 +153,14 @@ impl OffchainWorkers { enable_http_requests, custom_extensions, }: OffchainWorkerOptions, - ) -> Self { - Self { + ) -> std::io::Result { + Ok(Self { runtime_api_provider, thread_pool: Mutex::new(ThreadPool::with_name( "offchain-worker".into(), num_cpus::get(), )), - shared_http_client: api::SharedClient::new(), + shared_http_client: api::SharedClient::new()?, enable_http_requests, keystore, offchain_db: offchain_db.map(OffchainDb::new), @@ -168,7 +168,7 @@ impl OffchainWorkers { is_validator, network_provider, custom_extensions: Box::new(custom_extensions), - } + }) } } @@ -466,7 +466,8 @@ mod tests { is_validator: false, enable_http_requests: false, custom_extensions: |_| Vec::new(), - }); + }) + .unwrap(); futures::executor::block_on(offchain.on_block_imported(&header)); // then diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index 6ba6959202c..169993e2e93 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -161,9 +161,7 @@ pub fn new_full::Ha })?; if config.offchain_worker.enabled { - task_manager.spawn_handle().spawn( - "offchain-workers-runner", - "offchain-worker", + let offchain_workers = sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { runtime_api_provider: client.clone(), is_validator: config.role.is_authority(), @@ -175,9 +173,11 @@ pub fn new_full::Ha network_provider: Arc::new(network.clone()), enable_http_requests: true, custom_extensions: |_| vec![], - }) - .run(client.clone(), task_manager.spawn_handle()) - .boxed(), + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-worker", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), ); } diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index dd7dff2ebf1..57ffcb9049d 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -287,9 +287,7 @@ pub async fn start_parachain_node( if parachain_config.offchain_worker.enabled { use futures::FutureExt; - task_manager.spawn_handle().spawn( - "offchain-workers-runner", - "offchain-work", + let offchain_workers = sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { runtime_api_provider: client.clone(), keystore: Some(params.keystore_container.keystore()), @@ -301,9 +299,11 @@ pub async fn start_parachain_node( is_validator: parachain_config.role.is_authority(), enable_http_requests: false, custom_extensions: move |_| vec![], - }) - .run(client.clone(), task_manager.spawn_handle()) - .boxed(), + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-work", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), ); } diff --git a/templates/solochain/node/src/service.rs b/templates/solochain/node/src/service.rs index 4192128b672..2524906fd50 100644 --- a/templates/solochain/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -197,9 +197,7 @@ pub fn new_full< })?; if config.offchain_worker.enabled { - task_manager.spawn_handle().spawn( - "offchain-workers-runner", - "offchain-worker", + let offchain_workers = sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { runtime_api_provider: client.clone(), is_validator: config.role.is_authority(), @@ -211,9 +209,11 @@ pub fn new_full< network_provider: Arc::new(network.clone()), enable_http_requests: true, custom_extensions: |_| vec![], - }) - .run(client.clone(), task_manager.spawn_handle()) - .boxed(), + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-worker", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), ); } -- GitLab From 0796326267bb9e84e6aeed13ef334115dc1a5ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Fri, 25 Oct 2024 11:27:46 +0200 Subject: [PATCH 46/50] pallet-revive: Add stateful address mapping (#6096) Fixes #5576 This allows contracts to be used with an AccountId32 through normal extrinsics and not only through the eth compat layer. It works by adding a new extrinsic `map_account` that lets people register their AccountId32. --------- Co-authored-by: command-bot <> Co-authored-by: GitHub Action Co-authored-by: Cyrill Leutwiler --- .../assets/asset-hub-westend/src/lib.rs | 2 +- prdoc/pr_6096.prdoc | 15 + substrate/bin/node/runtime/src/lib.rs | 2 +- .../contracts/locking_delegate_dependency.rs | 4 +- .../fixtures/contracts/self_destruct.rs | 4 +- .../contracts/terminate_and_send_to_eve.rs | 33 + .../src/parachain/contracts_config.rs | 2 +- substrate/frame/revive/src/address.rs | 288 ++++- .../frame/revive/src/benchmarking/mod.rs | 112 +- substrate/frame/revive/src/evm/runtime.rs | 4 +- substrate/frame/revive/src/exec.rs | 121 +- substrate/frame/revive/src/lib.rs | 79 +- substrate/frame/revive/src/test_utils.rs | 39 +- substrate/frame/revive/src/tests.rs | 115 +- substrate/frame/revive/src/weights.rs | 1089 +++++++++-------- 15 files changed, 1235 insertions(+), 674 deletions(-) create mode 100644 prdoc/pr_6096.prdoc create mode 100644 substrate/frame/revive/fixtures/contracts/terminate_and_send_to_eve.rs 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 9a60de77a58..adfa2b74df6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -957,7 +957,7 @@ impl pallet_revive::Config for Runtime { type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_revive::weights::SubstrateWeight; type ChainExtension = (); - type AddressMapper = pallet_revive::DefaultAddressMapper; + type AddressMapper = pallet_revive::AccountId32Mapper; type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; type UnsafeUnstableInterface = ConstBool; diff --git a/prdoc/pr_6096.prdoc b/prdoc/pr_6096.prdoc new file mode 100644 index 00000000000..c77c323a2e0 --- /dev/null +++ b/prdoc/pr_6096.prdoc @@ -0,0 +1,15 @@ +title: 'pallet-revive: Add stateful address mapping' +doc: +- audience: + - Runtime Dev + description: |- + Fixes #5576 + + This allows contracts to be used with an AccountId32 through normal extrinsics and not only through the eth compat layer. It works by adding a new extrinsic `map_account` that lets people register their AccountId32. +crates: +- name: pallet-revive-fixtures + bump: minor +- name: pallet-revive-mock-network + bump: patch +- name: pallet-revive + bump: major diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index d6a17856e47..89b5bf26ce7 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1415,7 +1415,7 @@ impl pallet_revive::Config for Runtime { type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_revive::weights::SubstrateWeight; type ChainExtension = (); - type AddressMapper = pallet_revive::DefaultAddressMapper; + type AddressMapper = pallet_revive::AccountId32Mapper; type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; type UnsafeUnstableInterface = ConstBool; diff --git a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs index 2efacb4e683..54c7c7f3d5e 100644 --- a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs +++ b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs @@ -23,7 +23,7 @@ use common::input; use uapi::{HostFn, HostFnImpl as api}; -const ETH_ALICE: [u8; 20] = [1u8; 20]; +const ALICE_FALLBACK: [u8; 20] = [1u8; 20]; /// Load input data and perform the action specified by the input. /// If `delegate_call` is true, then delegate call into the contract. @@ -44,7 +44,7 @@ fn load_input(delegate_call: bool) { }, // 3 = Terminate 3 => { - api::terminate(Ð_ALICE); + api::terminate(&ALICE_FALLBACK); }, // Everything else is a noop _ => {}, diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs index 524979991ec..2f37706634b 100644 --- a/substrate/frame/revive/fixtures/contracts/self_destruct.rs +++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs @@ -21,7 +21,7 @@ use common::input; use uapi::{HostFn, HostFnImpl as api}; -const ETH_DJANGO: [u8; 20] = [4u8; 20]; +const DJANGO_FALLBACK: [u8; 20] = [4u8; 20]; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -52,6 +52,6 @@ pub extern "C" fn call() { .unwrap(); } else { // Try to terminate and give balance to django. - api::terminate(Ð_DJANGO); + api::terminate(&DJANGO_FALLBACK); } } diff --git a/substrate/frame/revive/fixtures/contracts/terminate_and_send_to_eve.rs b/substrate/frame/revive/fixtures/contracts/terminate_and_send_to_eve.rs new file mode 100644 index 00000000000..c078f9d46c1 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/terminate_and_send_to_eve.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let eve = [5u8; 20]; + api::terminate(&eve); +} diff --git a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs index c13c337d166..a2fa7cbf706 100644 --- a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs +++ b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs @@ -20,7 +20,7 @@ use frame_support::derive_impl; #[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] impl pallet_revive::Config for Runtime { - type AddressMapper = pallet_revive::DefaultAddressMapper; + type AddressMapper = pallet_revive::AccountId32Mapper; type Currency = Balances; type Time = super::Timestamp; type Xcm = pallet_xcm::Pallet; diff --git a/substrate/frame/revive/src/address.rs b/substrate/frame/revive/src/address.rs index 4633fce1f32..45b5bf822dc 100644 --- a/substrate/frame/revive/src/address.rs +++ b/substrate/frame/revive/src/address.rs @@ -17,61 +17,173 @@ //! Functions that deal contract addresses. +use crate::{ensure, AddressSuffix, Config, Error, HoldReason}; use alloc::vec::Vec; -use sp_core::H160; +use core::marker::PhantomData; +use frame_support::traits::{fungible::MutateHold, tokens::Precision}; +use sp_core::{Get, H160}; use sp_io::hashing::keccak_256; -use sp_runtime::AccountId32; +use sp_runtime::{AccountId32, DispatchResult, SaturatedConversion, Saturating}; /// Map between the native chain account id `T` and an Ethereum [`H160`]. /// /// This trait exists only to emulate specialization for different concrete /// native account ids. **Not** to make the mapping user configurable. Hence -/// the trait is `Sealed` and only one mandatory implementor [`DefaultAddressMapper`] -/// exists. +/// the trait is `Sealed` and depending on your runtime configuration you need +/// to pick either [`AccountId32Mapper`] or [`H160Mapper`]. Picking the wrong +/// one will result in a compilation error. No footguns here. /// /// Please note that we assume that the native account is at least 20 bytes and /// only implement this type for a `T` where this is the case. Luckily, this is the -/// case for all existing runtimes as of right now. Reasing is that this will allow +/// case for all existing runtimes as of right now. Reasoning is that this will allow /// us to reverse an address -> account_id mapping by just stripping the prefix. -pub trait AddressMapper: private::Sealed { - /// Convert an account id to an ethereum address. - /// - /// This mapping is **not** required to be reversible. - fn to_address(account_id: &T) -> H160; +/// +/// We require the mapping to be reversible. Since we are potentially dealing with types of +/// different sizes one direction of the mapping is necessarily lossy. This requires the mapping to +/// make use of the [`AddressSuffix`] storage item to reverse the mapping. +pub trait AddressMapper: private::Sealed { + /// Convert an account id to an ethereum adress. + fn to_address(account_id: &T::AccountId) -> H160; /// Convert an ethereum address to a native account id. + fn to_account_id(address: &H160) -> T::AccountId; + + /// Same as [`Self::to_account_id`] but always returns the fallback account. + /// + /// This skips the query into [`AddressSuffix`] and always returns the stateless + /// fallback account. This is useful when we know for a fact that the `address` + /// in question is originally a `H160`. This is usually only the case when we + /// generated a new contract address. + fn to_fallback_account_id(address: &H160) -> T::AccountId; + + /// Create a stateful mapping for `account_id` + /// + /// This will enable `to_account_id` to map back to the original + /// `account_id` instead of the fallback account id. + fn map(account_id: &T::AccountId) -> DispatchResult; + + /// Remove the mapping in order to reclaim the deposit. /// - /// This mapping is **required** to be reversible. - fn to_account_id(address: &H160) -> T; + /// There is no reason why one would unmap their `account_id` except + /// for reclaiming the deposit. + fn unmap(account_id: &T::AccountId) -> DispatchResult; - /// Same as [`Self::to_account_id`] but when we know the address is a contract. + /// Returns true if the `account_id` is useable as an origin. /// - /// This is only the case when we just generated the new address. - fn to_account_id_contract(address: &H160) -> T; + /// This means either the `account_id` doesn't require a stateful mapping + /// or a stateful mapping exists. + fn is_mapped(account_id: &T::AccountId) -> bool; } mod private { pub trait Sealed {} - impl Sealed for super::DefaultAddressMapper {} + impl Sealed for super::AccountId32Mapper {} + impl Sealed for super::H160Mapper {} } -/// The only implementor for `AddressMapper`. -pub enum DefaultAddressMapper {} +/// The mapper to be used if the account id is `AccountId32`. +/// +/// It converts between addresses by either truncating the last 12 bytes or +/// suffixing them. The suffix is queried from [`AddressSuffix`] and will fall +/// back to all `0xEE` if no suffix was registered. This means contracts and +/// plain wallets controlled by an `secp256k1` always have a `0xEE` suffixed +/// account. +pub struct AccountId32Mapper(PhantomData); + +/// The mapper to be used if the account id is `H160`. +/// +/// It just trivially returns its inputs and doesn't make use of any state. +pub struct H160Mapper(PhantomData); -impl AddressMapper for DefaultAddressMapper { +impl AddressMapper for AccountId32Mapper +where + T: Config, +{ fn to_address(account_id: &AccountId32) -> H160 { H160::from_slice(&>::as_ref(&account_id)[..20]) } fn to_account_id(address: &H160) -> AccountId32 { + if let Some(suffix) = >::get(address) { + let mut account_id = Self::to_fallback_account_id(address); + let account_bytes: &mut [u8; 32] = account_id.as_mut(); + account_bytes[20..].copy_from_slice(suffix.as_slice()); + account_id + } else { + Self::to_fallback_account_id(address) + } + } + + fn to_fallback_account_id(address: &H160) -> AccountId32 { let mut account_id = AccountId32::new([0xEE; 32]); - >::as_mut(&mut account_id)[..20] - .copy_from_slice(address.as_bytes()); + let account_bytes: &mut [u8; 32] = account_id.as_mut(); + account_bytes[..20].copy_from_slice(address.as_bytes()); account_id } - fn to_account_id_contract(address: &H160) -> AccountId32 { - Self::to_account_id(address) + fn map(account_id: &T::AccountId) -> DispatchResult { + ensure!(!Self::is_mapped(account_id), >::AccountAlreadyMapped); + + let account_bytes: &[u8; 32] = account_id.as_ref(); + + // each mapping entry stores one AccountId32 distributed between key and value + let deposit = T::DepositPerByte::get() + .saturating_mul(account_bytes.len().saturated_into()) + .saturating_add(T::DepositPerItem::get()); + + let suffix: [u8; 12] = account_bytes[20..] + .try_into() + .expect("Skipping 20 byte of a an 32 byte array will fit into 12 bytes; qed"); + T::Currency::hold(&HoldReason::AddressMapping.into(), account_id, deposit)?; + >::insert(Self::to_address(account_id), suffix); + Ok(()) + } + + fn unmap(account_id: &T::AccountId) -> DispatchResult { + // will do nothing if address is not mapped so no check required + >::remove(Self::to_address(account_id)); + T::Currency::release_all( + &HoldReason::AddressMapping.into(), + account_id, + Precision::BestEffort, + )?; + Ok(()) + } + + fn is_mapped(account_id: &T::AccountId) -> bool { + let account_bytes: &[u8; 32] = account_id.as_ref(); + &account_bytes[20..] == &[0xEE; 12] || + >::contains_key(Self::to_address(account_id)) + } +} + +impl AddressMapper for H160Mapper +where + T: Config, + crate::AccountIdOf: AsRef<[u8; 20]> + From, +{ + fn to_address(account_id: &T::AccountId) -> H160 { + H160::from_slice(account_id.as_ref()) + } + + fn to_account_id(address: &H160) -> T::AccountId { + Self::to_fallback_account_id(address) + } + + fn to_fallback_account_id(address: &H160) -> T::AccountId { + (*address).into() + } + + fn map(_account_id: &T::AccountId) -> DispatchResult { + Ok(()) + } + + fn unmap(_account_id: &T::AccountId) -> DispatchResult { + Ok(()) + } + + fn is_mapped(_account_id: &T::AccountId) -> bool { + true } } @@ -102,7 +214,16 @@ pub fn create2(deployer: &H160, code: &[u8], input_data: &[u8], salt: &[u8; 32]) #[cfg(test)] mod test { use super::*; - use crate::test_utils::ALICE_ADDR; + use crate::{ + test_utils::*, + tests::{ExtBuilder, Test}, + AddressMapper, Error, + }; + use frame_support::{ + assert_err, + traits::fungible::{InspectHold, Mutate}, + }; + use pretty_assertions::assert_eq; use sp_core::{hex2array, H160}; #[test] @@ -125,4 +246,123 @@ mod test { H160(hex2array!("7f31e795e5836a19a8f919ab5a9de9a197ecd2b6")), ) } + + #[test] + fn fallback_map_works() { + assert!(::AddressMapper::is_mapped(&ALICE)); + assert_eq!( + ALICE_FALLBACK, + ::AddressMapper::to_fallback_account_id(&ALICE_ADDR) + ); + assert_eq!(ALICE_ADDR, ::AddressMapper::to_address(&ALICE_FALLBACK)); + } + + #[test] + fn map_works() { + ExtBuilder::default().build().execute_with(|| { + ::Currency::set_balance(&EVE, 1_000_000); + // before mapping the fallback account is returned + assert!(!::AddressMapper::is_mapped(&EVE)); + assert_eq!(EVE_FALLBACK, ::AddressMapper::to_account_id(&EVE_ADDR)); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ), + 0 + ); + + // when mapped the full account id is returned + ::AddressMapper::map(&EVE).unwrap(); + assert!(::AddressMapper::is_mapped(&EVE)); + assert_eq!(EVE, ::AddressMapper::to_account_id(&EVE_ADDR)); + assert!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ) > 0 + ); + }); + } + + #[test] + fn map_fallback_account_fails() { + ExtBuilder::default().build().execute_with(|| { + assert!(::AddressMapper::is_mapped(&ALICE)); + // alice is an e suffixed account and hence cannot be mapped + assert_err!( + ::AddressMapper::map(&ALICE), + >::AccountAlreadyMapped, + ); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &ALICE + ), + 0 + ); + }); + } + + #[test] + fn double_map_fails() { + ExtBuilder::default().build().execute_with(|| { + assert!(!::AddressMapper::is_mapped(&EVE)); + ::Currency::set_balance(&EVE, 1_000_000); + ::AddressMapper::map(&EVE).unwrap(); + assert!(::AddressMapper::is_mapped(&EVE)); + let deposit = ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE, + ); + assert_err!( + ::AddressMapper::map(&EVE), + >::AccountAlreadyMapped, + ); + assert!(::AddressMapper::is_mapped(&EVE)); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ), + deposit + ); + }); + } + + #[test] + fn unmap_works() { + ExtBuilder::default().build().execute_with(|| { + ::Currency::set_balance(&EVE, 1_000_000); + ::AddressMapper::map(&EVE).unwrap(); + assert!(::AddressMapper::is_mapped(&EVE)); + assert!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ) > 0 + ); + + ::AddressMapper::unmap(&EVE).unwrap(); + assert!(!::AddressMapper::is_mapped(&EVE)); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ), + 0 + ); + + // another unmap is a noop + ::AddressMapper::unmap(&EVE).unwrap(); + assert!(!::AddressMapper::is_mapped(&EVE)); + assert_eq!( + ::Currency::balance_on_hold( + &HoldReason::AddressMapping.into(), + &EVE + ), + 0 + ); + }); + } } diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index ebafb6c7054..8c9bf2cf70f 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -63,6 +63,7 @@ const UNBALANCED_TRIE_LAYERS: u32 = 20; struct Contract { caller: T::AccountId, account_id: T::AccountId, + address: H160, } impl Contract @@ -71,11 +72,6 @@ where BalanceOf: Into + TryFrom, MomentOf: Into, { - /// Returns the address of the contract. - fn address(&self) -> H160 { - T::AddressMapper::to_address(&self.account_id) - } - /// Create new contract and use a default account id as instantiator. fn new(module: WasmModule, data: Vec) -> Result, &'static str> { Self::with_index(0, module, data) @@ -98,9 +94,12 @@ where ) -> Result, &'static str> { T::Currency::set_balance(&caller, caller_funding::()); let salt = Some([0xffu8; 32]); + let origin: T::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); + + Contracts::::map_account(origin.clone()).unwrap(); let outcome = Contracts::::bare_instantiate( - RawOrigin::Signed(caller.clone()).into(), + origin, 0u32.into(), Weight::MAX, default_deposit_limit::(), @@ -112,8 +111,8 @@ where ); let address = outcome.result?.addr; - let account_id = T::AddressMapper::to_account_id_contract(&address); - let result = Contract { caller, account_id: account_id.clone() }; + let account_id = T::AddressMapper::to_fallback_account_id(&address); + let result = Contract { caller, address, account_id }; ContractInfoOf::::insert(&address, result.info()?); @@ -143,7 +142,7 @@ where info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false) .map_err(|_| "Failed to write storage to restoration dest")?; } - >::insert(T::AddressMapper::to_address(&self.account_id), info); + >::insert(&self.address, info); Ok(()) } @@ -223,6 +222,7 @@ fn default_deposit_limit() -> BalanceOf { T: Config + pallet_balances::Config, MomentOf: Into, ::RuntimeEvent: From>, + ::RuntimeCall: From>, as Currency>::Balance: From>, )] mod benchmarks { @@ -263,13 +263,12 @@ mod benchmarks { let instance = Contract::::with_caller(whitelisted_caller(), WasmModule::sized(c), vec![])?; let value = Pallet::::min_balance(); - let callee = T::AddressMapper::to_address(&instance.account_id); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] call( RawOrigin::Signed(instance.caller.clone()), - callee, + instance.address, value, Weight::MAX, storage_deposit, @@ -293,9 +292,10 @@ mod benchmarks { T::Currency::set_balance(&caller, caller_funding::()); let WasmModule { code, .. } = WasmModule::sized(c); let origin = RawOrigin::Signed(caller.clone()); + Contracts::::map_account(origin.clone().into()).unwrap(); let deployer = T::AddressMapper::to_address(&caller); let addr = crate::address::create2(&deployer, &code, &input, &salt); - let account_id = T::AddressMapper::to_account_id_contract(&addr); + let account_id = T::AddressMapper::to_fallback_account_id(&addr); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] _(origin, value, Weight::MAX, storage_deposit, code, input, Some(salt)); @@ -305,9 +305,14 @@ mod benchmarks { // uploading the code reserves some balance in the callers account let code_deposit = T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); + let mapping_deposit = + T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &caller); assert_eq!( T::Currency::balance(&caller), - caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + caller_funding::() - + value - deposit - + code_deposit - mapping_deposit - + Pallet::::min_balance(), ); // contract has the full value assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); @@ -323,33 +328,31 @@ mod benchmarks { let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); let origin = RawOrigin::Signed(caller.clone()); + Contracts::::map_account(origin.clone().into()).unwrap(); let WasmModule { code, .. } = WasmModule::dummy(); let storage_deposit = default_deposit_limit::(); let deployer = T::AddressMapper::to_address(&caller); let addr = crate::address::create2(&deployer, &code, &input, &salt); - let hash = - Contracts::::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; - let account_id = T::AddressMapper::to_account_id_contract(&addr); + let hash = Contracts::::bare_upload_code(origin.clone().into(), code, storage_deposit)? + .code_hash; + let account_id = T::AddressMapper::to_fallback_account_id(&addr); #[extrinsic_call] - _( - RawOrigin::Signed(caller.clone()), - value, - Weight::MAX, - storage_deposit, - hash, - input, - Some(salt), - ); + _(origin, value, Weight::MAX, storage_deposit, hash, input, Some(salt)); let deposit = T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id); let code_deposit = T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &account_id); + let mapping_deposit = + T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &account_id); // value was removed from the caller assert_eq!( T::Currency::total_balance(&caller), - caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + caller_funding::() - + value - deposit - + code_deposit - mapping_deposit - + Pallet::::min_balance(), ); // contract has the full value assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); @@ -371,11 +374,10 @@ mod benchmarks { Contract::::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; let value = Pallet::::min_balance(); let origin = RawOrigin::Signed(instance.caller.clone()); - let callee = T::AddressMapper::to_address(&instance.account_id); let before = T::Currency::balance(&instance.account_id); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] - _(origin, callee, value, Weight::MAX, storage_deposit, data); + _(origin, instance.address, value, Weight::MAX, storage_deposit, data); let deposit = T::Currency::balance_on_hold( &HoldReason::StorageDepositReserve.into(), &instance.account_id, @@ -384,10 +386,15 @@ mod benchmarks { &HoldReason::CodeUploadDepositReserve.into(), &instance.caller, ); + let mapping_deposit = + T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &instance.caller); // value and value transferred via call should be removed from the caller assert_eq!( T::Currency::balance(&instance.caller), - caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), + caller_funding::() - + value - deposit - + code_deposit - mapping_deposit - + Pallet::::min_balance() ); // contract should have received the value assert_eq!(T::Currency::balance(&instance.account_id), before + value); @@ -447,14 +454,46 @@ mod benchmarks { let storage_deposit = default_deposit_limit::(); let hash = >::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; - let callee = T::AddressMapper::to_address(&instance.account_id); assert_ne!(instance.info()?.code_hash, hash); #[extrinsic_call] - _(RawOrigin::Root, callee, hash); + _(RawOrigin::Root, instance.address, hash); assert_eq!(instance.info()?.code_hash, hash); Ok(()) } + #[benchmark(pov_mode = Measured)] + fn map_account() { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let origin = RawOrigin::Signed(caller.clone()); + assert!(!T::AddressMapper::is_mapped(&caller)); + #[extrinsic_call] + _(origin); + assert!(T::AddressMapper::is_mapped(&caller)); + } + + #[benchmark(pov_mode = Measured)] + fn unmap_account() { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let origin = RawOrigin::Signed(caller.clone()); + >::map_account(origin.clone().into()).unwrap(); + assert!(T::AddressMapper::is_mapped(&caller)); + #[extrinsic_call] + _(origin); + assert!(!T::AddressMapper::is_mapped(&caller)); + } + + #[benchmark(pov_mode = Measured)] + fn dispatch_as_fallback_account() { + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + let origin = RawOrigin::Signed(caller.clone()); + let dispatchable = frame_system::Call::remark { remark: vec![] }.into(); + #[extrinsic_call] + _(origin, Box::new(dispatchable)); + } + #[benchmark(pov_mode = Measured)] fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) { let mut setup = CallSetup::::new(WasmModule::noop()); @@ -636,7 +675,7 @@ mod benchmarks { build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]); >::insert::<_, BoundedVec<_, _>>( - contract.address(), + contract.address, immutable_data.clone().try_into().unwrap(), ); @@ -669,10 +708,7 @@ mod benchmarks { } assert_ok!(result); - assert_eq!( - &memory[..], - &>::get(setup.contract().address()).unwrap()[..] - ); + assert_eq!(&memory[..], &>::get(setup.contract().address).unwrap()[..]); } #[benchmark(pov_mode = Measured)] @@ -835,7 +871,7 @@ mod benchmarks { assert_eq!( record.event, - crate::Event::ContractEmitted { contract: instance.address(), data, topics }.into(), + crate::Event::ContractEmitted { contract: instance.address, data, topics }.into(), ); } @@ -1542,7 +1578,7 @@ mod benchmarks { let salt = [42u8; 32]; let deployer = T::AddressMapper::to_address(&account_id); let addr = crate::address::create2(&deployer, &code.code, &input, &salt); - let account_id = T::AddressMapper::to_account_id_contract(&addr); + let account_id = T::AddressMapper::to_fallback_account_id(&addr); let mut memory = memory!(hash_bytes, deposit_bytes, value_bytes, input, salt,); let mut offset = { diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index 4c3fdeca720..80406a49bca 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -290,7 +290,7 @@ pub trait EthExtra { })?; let signer = - ::AddressMapper::to_account_id_contract(&signer); + ::AddressMapper::to_fallback_account_id(&signer); let TransactionLegacyUnsigned { nonce, chain_id, to, value, input, gas, gas_price, .. } = tx.transaction_legacy_unsigned; @@ -419,7 +419,7 @@ mod test { /// Get the [`AccountId`] of the account. pub fn account_id(&self) -> AccountIdOf { let address = self.address(); - ::AddressMapper::to_account_id_contract(&address) + ::AddressMapper::to_fallback_account_id(&address) } /// Get the [`H160`] address of the account. diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 759fba9f1c6..9f3a75c0090 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -167,6 +167,18 @@ impl Origin { Origin::Root => Err(DispatchError::RootNotAllowed), } } + + /// Make sure that this origin is mapped. + /// + /// We require an origin to be mapped in order to be used in a `Stack`. Otherwise + /// [`Stack::caller`] returns an address that can't be reverted to the original address. + fn ensure_mapped(&self) -> DispatchResult { + match self { + Self::Root => Ok(()), + Self::Signed(account_id) if T::AddressMapper::is_mapped(account_id) => Ok(()), + Self::Signed(_) => Err(>::AccountUnmapped.into()), + } + } } /// An interface that provides access to the external environment in which the @@ -752,7 +764,7 @@ where )? { stack.run(executable, input_data).map(|_| stack.first_frame.last_frame_output) } else { - Self::transfer_no_contract(&origin, &dest, value) + Self::transfer_from_origin(&origin, &dest, value) } } @@ -833,6 +845,7 @@ where value: BalanceOf, debug_message: Option<&'a mut DebugBuffer>, ) -> Result, ExecError> { + origin.ensure_mapped()?; let Some((first_frame, executable)) = Self::new_frame( args, value, @@ -925,7 +938,7 @@ where *executable.code_hash(), )?; ( - T::AddressMapper::to_account_id_contract(&address), + T::AddressMapper::to_fallback_account_id(&address), contract, executable, None, @@ -1241,13 +1254,13 @@ where } /// Transfer some funds from `from` to `to`. - fn transfer(from: &T::AccountId, to: &T::AccountId, value: BalanceOf) -> DispatchResult { + 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)?; } - Ok(()) + Ok(Default::default()) } /// Same as `transfer` but `from` is an `Origin`. @@ -1255,28 +1268,17 @@ where from: &Origin, to: &T::AccountId, value: BalanceOf, - ) -> DispatchResult { + ) -> ExecResult { // If the from address is root there is no account to transfer from, and therefore we can't // take any `value` other than 0. let from = match from { Origin::Signed(caller) => caller, - Origin::Root if value.is_zero() => return Ok(()), - Origin::Root => return DispatchError::RootNotAllowed.into(), + Origin::Root if value.is_zero() => return Ok(Default::default()), + Origin::Root => return Err(DispatchError::RootNotAllowed.into()), }; Self::transfer(from, to, value) } - /// Same as `transfer_from_origin` but creates an `ExecReturnValue` on success. - fn transfer_no_contract( - from: &Origin, - to: &T::AccountId, - value: BalanceOf, - ) -> ExecResult { - Self::transfer_from_origin(from, to, value) - .map(|_| ExecReturnValue::default()) - .map_err(Into::into) - } - /// Reference to the current (top) frame. fn top_frame(&self) -> &Frame { top_frame!(self) @@ -1379,12 +1381,7 @@ where )? { self.run(executable, input_data) } else { - Self::transfer_no_contract( - &Origin::from_account_id(self.account_id().clone()), - &dest, - value, - )?; - Ok(()) + Self::transfer(&self.account_id(), &dest, value).map(|_| ()) } }; @@ -1488,6 +1485,8 @@ where &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> { @@ -2031,7 +2030,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, success_ch); set_balance(&ALICE, 100); - let balance = get_balance(&BOB_CONTRACT_ID); + let balance = get_balance(&BOB_FALLBACK); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 0, value).unwrap(); @@ -2047,7 +2046,7 @@ mod tests { .unwrap(); assert_eq!(get_balance(&ALICE), 100 - value); - assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); + assert_eq!(get_balance(&BOB_FALLBACK), balance + value); }); } @@ -2069,7 +2068,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, delegate_ch); set_balance(&ALICE, 100); - let balance = get_balance(&BOB_CONTRACT_ID); + let balance = get_balance(&BOB_FALLBACK); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 0, 55).unwrap(); @@ -2085,7 +2084,7 @@ mod tests { .unwrap(); assert_eq!(get_balance(&ALICE), 100 - value); - assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); + assert_eq!(get_balance(&BOB_FALLBACK), balance + value); }); } @@ -2326,9 +2325,10 @@ mod tests { // Record the caller for bob. WitnessedCallerBob::mutate(|caller| { let origin = ctx.ext.caller(); - *caller = Some(::AddressMapper::to_address( - &origin.account_id().unwrap(), - )); + *caller = + Some(<::AddressMapper as AddressMapper>::to_address( + &origin.account_id().unwrap(), + )); }); // Call into CHARLIE contract. @@ -2350,9 +2350,10 @@ mod tests { // Record the caller for charlie. WitnessedCallerCharlie::mutate(|caller| { let origin = ctx.ext.caller(); - *caller = Some(::AddressMapper::to_address( - &origin.account_id().unwrap(), - )); + *caller = + Some(<::AddressMapper as AddressMapper>::to_address( + &origin.account_id().unwrap(), + )); }); exec_success() }); @@ -2716,10 +2717,11 @@ mod tests { ), Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address ); - let instantiated_contract_id = - ::AddressMapper::to_account_id_contract( - &instantiated_contract_address, - ); + let instantiated_contract_id = <::AddressMapper as AddressMapper< + Test, + >>::to_fallback_account_id( + &instantiated_contract_address + ); // Check that the newly created account has the expected code hash and // there are instantiation event. @@ -2771,10 +2773,11 @@ mod tests { Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address ); - let instantiated_contract_id = - ::AddressMapper::to_account_id_contract( - &instantiated_contract_address, - ); + let instantiated_contract_id = <::AddressMapper as AddressMapper< + Test, + >>::to_fallback_account_id( + &instantiated_contract_address + ); // Check that the account has not been created. assert!(ContractInfo::::load_code_hash(&instantiated_contract_id).is_none()); @@ -2837,10 +2840,11 @@ mod tests { let instantiated_contract_address = *instantiated_contract_address.borrow().as_ref().unwrap(); - let instantiated_contract_id = - ::AddressMapper::to_account_id_contract( - &instantiated_contract_address, - ); + let instantiated_contract_id = <::AddressMapper as AddressMapper< + Test, + >>::to_fallback_account_id( + &instantiated_contract_address + ); // Check that the newly created account has the expected code hash and // there are instantiation event. @@ -2895,7 +2899,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB_FALLBACK, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); @@ -3025,7 +3029,8 @@ mod tests { fn recursive_call_during_constructor_is_balance_transfer() { let code = MockLoader::insert(Constructor, |ctx, _| { let account_id = ctx.ext.account_id().clone(); - let addr = ::AddressMapper::to_address(&account_id); + let addr = + <::AddressMapper as AddressMapper>::to_address(&account_id); let balance = ctx.ext.balance(); // Calling ourselves during the constructor will trigger a balance @@ -3086,7 +3091,8 @@ mod tests { fn cannot_send_more_balance_than_available_to_self() { let code_hash = MockLoader::insert(Call, |ctx, _| { let account_id = ctx.ext.account_id().clone(); - let addr = ::AddressMapper::to_address(&account_id); + let addr = + <::AddressMapper as AddressMapper>::to_address(&account_id); let balance = ctx.ext.balance(); assert_err!( @@ -3358,7 +3364,7 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::System(frame_system::Event::Remarked { - sender: BOB_CONTRACT_ID, + sender: BOB_FALLBACK, hash: remark_hash }), topics: vec![], @@ -3442,7 +3448,7 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::System(frame_system::Event::Remarked { - sender: BOB_CONTRACT_ID, + sender: BOB_FALLBACK, hash: remark_hash }), topics: vec![], @@ -3506,7 +3512,10 @@ mod tests { ) .unwrap(); - let account_id = ::AddressMapper::to_account_id_contract(&addr); + let account_id = + <::AddressMapper as AddressMapper>::to_fallback_account_id( + &addr, + ); assert_eq!(System::account_nonce(&ALICE), alice_nonce); assert_eq!(System::account_nonce(ctx.ext.account_id()), 1); @@ -4299,7 +4308,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); @@ -4484,7 +4493,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); @@ -4601,7 +4610,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); @@ -4645,7 +4654,7 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB_CONTRACT_ID, 100); + set_balance(&BOB, 100); place_contract(&BOB, instantiator_ch); let origin = Origin::from_account_id(ALICE); let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 11752e47cf2..1967f9868d2 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -48,6 +48,7 @@ use crate::{ storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, wasm::{CodeInfo, RuntimeCosts, WasmBlob}, }; +use alloc::boxed::Box; use codec::{Codec, Decode, Encode}; use environmental::*; use frame_support::{ @@ -78,7 +79,7 @@ use sp_runtime::{ }; pub use crate::{ - address::{create1, create2, AddressMapper, DefaultAddressMapper}, + address::{create1, create2, AccountId32Mapper, AddressMapper}, debug::Tracing, exec::MomentOf, pallet::*, @@ -160,11 +161,9 @@ pub mod pallet { /// The overarching call type. #[pallet::no_default_bounds] - type RuntimeCall: Dispatchable - + GetDispatchInfo - + codec::Decode - + core::fmt::Debug - + IsType<::RuntimeCall>; + type RuntimeCall: Parameter + + Dispatchable + + GetDispatchInfo; /// Overarching hold reason. #[pallet::no_default_bounds] @@ -233,9 +232,9 @@ pub mod pallet { #[pallet::constant] type CodeHashLockupDepositPercent: Get; - /// Only valid type is [`DefaultAddressMapper`]. - #[pallet::no_default_bounds] - type AddressMapper: AddressMapper>; + /// Use either valid type is [`address::AccountId32Mapper`] or [`address::H160Mapper`]. + #[pallet::no_default] + type AddressMapper: AddressMapper; /// Make contract callable functions marked as `#[unstable]` available. /// @@ -361,7 +360,6 @@ pub mod pallet { #[inject_runtime_type] type RuntimeCall = (); - type AddressMapper = DefaultAddressMapper; type CallFilter = (); type ChainExtension = (); type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; @@ -562,6 +560,12 @@ pub mod pallet { /// Immutable data can only be set during deploys and only be read during calls. /// Additionally, it is only valid to set the data once and it must not be empty. InvalidImmutableAccess, + /// An `AccountID32` account tried to interact with the pallet without having a mapping. + /// + /// Call [`Pallet::map_account`] in order to create a mapping for the account. + AccountUnmapped, + /// Tried to map an account that is already mapped. + AccountAlreadyMapped, } /// A reason for the pallet contracts placing a hold on funds. @@ -571,6 +575,8 @@ pub mod pallet { CodeUploadDepositReserve, /// The Pallet has reserved it for storage deposit. StorageDepositReserve, + /// Deposit for creating an address mapping in [`AddressSuffix`]. + AddressMapping, } /// A mapping from a contract's code hash to its code. @@ -602,6 +608,14 @@ pub mod pallet { pub(crate) type DeletionQueueCounter = StorageValue<_, DeletionQueueManager, ValueQuery>; + /// Map a Ethereum address to its original `AccountId32`. + /// + /// Stores the last 12 byte for addresses that were originally an `AccountId32` instead + /// of an `H160`. Register your `AccountId32` using [`Pallet::map_account`] in order to + /// use it with this pallet. + #[pallet::storage] + pub(crate) type AddressSuffix = StorageMap<_, Identity, H160, [u8; 12]>; + #[pallet::extra_constants] impl Pallet { #[pallet::constant_name(ApiVersion)] @@ -995,6 +1009,51 @@ pub mod pallet { Ok(()) }) } + + /// Register the callers account id so that it can be used in contract interactions. + /// + /// This will error if the origin is already mapped or is a eth native `Address20`. It will + /// take a deposit that can be released by calling [`Self::unmap_account`]. + #[pallet::call_index(7)] + #[pallet::weight(T::WeightInfo::map_account())] + pub fn map_account(origin: OriginFor) -> DispatchResult { + let origin = ensure_signed(origin)?; + T::AddressMapper::map(&origin) + } + + /// Unregister the callers account id in order to free the deposit. + /// + /// There is no reason to ever call this function other than freeing up the deposit. + /// This is only useful when the account should no longer be used. + #[pallet::call_index(8)] + #[pallet::weight(T::WeightInfo::unmap_account())] + pub fn unmap_account(origin: OriginFor) -> DispatchResult { + let origin = ensure_signed(origin)?; + T::AddressMapper::unmap(&origin) + } + + /// Dispatch an `call` with the origin set to the callers fallback address. + /// + /// Every `AccountId32` can control its corresponding fallback account. The fallback account + /// is the `AccountId20` with the last 12 bytes set to `0xEE`. This is essentially a + /// recovery function in case an `AccountId20` was used without creating a mapping first. + #[pallet::call_index(9)] + #[pallet::weight({ + let dispatch_info = call.get_dispatch_info(); + ( + T::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight), + dispatch_info.class + ) + })] + pub fn dispatch_as_fallback_account( + origin: OriginFor, + call: Box<::RuntimeCall>, + ) -> DispatchResultWithPostInfo { + let origin = ensure_signed(origin)?; + let unmapped_account = + T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin)); + call.dispatch(RawOrigin::Signed(unmapped_account).into()) + } } } diff --git a/substrate/frame/revive/src/test_utils.rs b/substrate/frame/revive/src/test_utils.rs index 92c21297a3e..acd9a4cda38 100644 --- a/substrate/frame/revive/src/test_utils.rs +++ b/substrate/frame/revive/src/test_utils.rs @@ -27,38 +27,35 @@ use frame_support::weights::Weight; use sp_core::H160; pub use sp_runtime::AccountId32; -const fn ee_suffix(addr: H160) -> AccountId32 { - let mut id = [0u8; 32]; - let mut i = 0; - while i < 20 { - id[i] = addr.0[i]; +const fn ee_suffix(mut account: [u8; 32]) -> AccountId32 { + let mut i = 20; + while i < 32 { + account[i] = 0xee; i += 1; } - - let mut j = 20; - while j < 32 { - id[j] = 0xee; - j += 1; - } - - AccountId32::new(id) + AccountId32::new(account) } -pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); +pub const ALICE: AccountId32 = ee_suffix([1u8; 32]); pub const ALICE_ADDR: H160 = H160([1u8; 20]); -pub const ETH_ALICE: AccountId32 = ee_suffix(ALICE_ADDR); +pub const ALICE_FALLBACK: AccountId32 = ee_suffix([1u8; 32]); -pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); +pub const BOB: AccountId32 = ee_suffix([2u8; 32]); pub const BOB_ADDR: H160 = H160([2u8; 20]); -pub const BOB_CONTRACT_ID: AccountId32 = ee_suffix(BOB_ADDR); +pub const BOB_FALLBACK: AccountId32 = ee_suffix([2u8; 32]); -pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); +pub const CHARLIE: AccountId32 = ee_suffix([3u8; 32]); pub const CHARLIE_ADDR: H160 = H160([3u8; 20]); -pub const CHARLIE_CONTRACT_ID: AccountId32 = ee_suffix(CHARLIE_ADDR); +pub const CHARLIE_FALLBACK: AccountId32 = ee_suffix([3u8; 32]); -pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); +pub const DJANGO: AccountId32 = ee_suffix([4u8; 32]); pub const DJANGO_ADDR: H160 = H160([4u8; 20]); -pub const ETH_DJANGO: AccountId32 = ee_suffix(DJANGO_ADDR); +pub const DJANGO_FALLBACK: AccountId32 = ee_suffix([4u8; 32]); + +/// Eve is a non ee account and hence needs a stateful mapping. +pub const EVE: AccountId32 = AccountId32::new([5u8; 32]); +pub const EVE_ADDR: H160 = H160([5u8; 20]); +pub const EVE_FALLBACK: AccountId32 = ee_suffix([5u8; 32]); pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 94af7dbd04d..1b5e64739d8 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -39,9 +39,9 @@ use crate::{ tests::test_utils::{get_contract, get_contract_checked}, wasm::Memory, weights::WeightInfo, - BalanceOf, Code, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, DebugInfo, - DefaultAddressMapper, DeletionQueueCounter, Error, HoldReason, Origin, Pallet, PristineCode, - H160, + AccountId32Mapper, BalanceOf, Code, CodeInfoOf, CollectEvents, Config, ContractInfo, + ContractInfoOf, DebugInfo, DeletionQueueCounter, Error, HoldReason, Origin, Pallet, + PristineCode, H160, }; use crate::test_utils::builder::Contract; @@ -114,7 +114,8 @@ pub mod test_utils { pub fn place_contract(address: &AccountIdOf, code_hash: sp_core::H256) { set_balance(address, Contracts::min_balance() * 10); >::insert(code_hash, CodeInfo::new(address.clone())); - let address = ::AddressMapper::to_address(&address); + let address = + <::AddressMapper as AddressMapper>::to_address(&address); let contract = >::new(&address, 0, code_hash).unwrap(); >::insert(address, contract); } @@ -508,13 +509,13 @@ parameter_types! { #[derive_impl(crate::config_preludes::TestDefaultConfig)] impl Config for Test { type Time = Timestamp; + type AddressMapper = AccountId32Mapper; type Currency = Balances; type CallFilter = TestFilter; type ChainExtension = (TestExtension, DisabledExtension, RevertingExtension, TempStorageExtension); type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; - type AddressMapper = DefaultAddressMapper; type UnsafeUnstableInterface = UnstableInterface; type UploadOrigin = EnsureAccount; type InstantiateOrigin = EnsureAccount; @@ -635,9 +636,9 @@ mod run_tests { ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 100_000_000); assert!(!>::contains_key(BOB_ADDR)); - assert_eq!(test_utils::get_balance(&BOB_CONTRACT_ID), 0); + 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_CONTRACT_ID), 42); + assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 42); assert_eq!(result, Default::default()); }); } @@ -1302,7 +1303,7 @@ mod run_tests { let (wasm, code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(1_000).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let _ = ::Currency::set_balance(Ð_DJANGO, 1_000_000); + let _ = ::Currency::set_balance(&DJANGO_FALLBACK, 1_000_000); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. @@ -1330,7 +1331,7 @@ mod run_tests { // Check that the beneficiary (django) got remaining balance. assert_eq!( - ::Currency::free_balance(ETH_DJANGO), + ::Currency::free_balance(DJANGO_FALLBACK), 1_000_000 + 100_000 + min_balance ); @@ -1382,7 +1383,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: contract.account_id.clone(), - to: ETH_DJANGO, + to: DJANGO_FALLBACK, amount: 100_000 + min_balance, }), topics: vec![], @@ -1545,7 +1546,7 @@ mod run_tests { // Sending at least the minimum balance should result in success but // no code called. - assert_eq!(test_utils::get_balance(Ð_DJANGO), 0); + assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 0); let result = builder::bare_call(bob.addr) .data( AsRef::<[u8]>::as_ref(&DJANGO_ADDR) @@ -1556,7 +1557,7 @@ mod run_tests { ) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::Success); - assert_eq!(test_utils::get_balance(Ð_DJANGO), 55); + assert_eq!(test_utils::get_balance(&DJANGO_FALLBACK), 55); let django = builder::bare_instantiate(Code::Upload(callee_code)) .origin(RuntimeOrigin::signed(CHARLIE)) @@ -3732,7 +3733,7 @@ mod run_tests { // Instantiate the caller contract with the given input. let instantiate = |input: &(u32, H256)| { builder::bare_instantiate(Code::Upload(wasm_caller.clone())) - .origin(RuntimeOrigin::signed(ETH_ALICE)) + .origin(RuntimeOrigin::signed(ALICE_FALLBACK)) .data(input.encode()) .build() }; @@ -3740,13 +3741,13 @@ mod run_tests { // Call contract with the given input. let call = |addr_caller: &H160, input: &(u32, H256)| { builder::bare_call(*addr_caller) - .origin(RuntimeOrigin::signed(ETH_ALICE)) + .origin(RuntimeOrigin::signed(ALICE_FALLBACK)) .data(input.encode()) .build() }; const ED: u64 = 2000; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let _ = Balances::set_balance(Ð_ALICE, 1_000_000); + let _ = Balances::set_balance(&ALICE_FALLBACK, 1_000_000); // Instantiate with lock_delegate_dependency should fail since the code is not yet on // chain. @@ -3760,7 +3761,7 @@ mod run_tests { for code in callee_codes.iter() { let CodeUploadReturnValue { deposit: deposit_per_code, .. } = Contracts::bare_upload_code( - RuntimeOrigin::signed(ETH_ALICE), + RuntimeOrigin::signed(ALICE_FALLBACK), code.clone(), deposit_limit::(), ) @@ -3790,7 +3791,7 @@ mod run_tests { // Removing the code should fail, since we have added a dependency. assert_err!( - Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0]), + Contracts::remove_code(RuntimeOrigin::signed(ALICE_FALLBACK), callee_hashes[0]), >::CodeInUse ); @@ -3848,14 +3849,17 @@ mod run_tests { ); // Since we unlocked the dependency we should now be able to remove the code. - assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0])); + assert_ok!(Contracts::remove_code( + RuntimeOrigin::signed(ALICE_FALLBACK), + callee_hashes[0] + )); // Calling should fail since the delegated contract is not on chain anymore. assert_err!(call(&addr_caller, &noop_input).result, Error::::ContractTrapped); // Add the dependency back. Contracts::upload_code( - RuntimeOrigin::signed(ETH_ALICE), + RuntimeOrigin::signed(ALICE_FALLBACK), callee_codes[0].clone(), deposit_limit::(), ) @@ -3863,15 +3867,18 @@ mod run_tests { call(&addr_caller, &lock_delegate_dependency_input).result.unwrap(); // Call terminate should work, and return the deposit. - let balance_before = test_utils::get_balance(Ð_ALICE); + let balance_before = test_utils::get_balance(&ALICE_FALLBACK); assert_ok!(call(&addr_caller, &terminate_input).result); assert_eq!( - test_utils::get_balance(Ð_ALICE), + test_utils::get_balance(&ALICE_FALLBACK), ED + balance_before + contract.storage_base_deposit() + dependency_deposit ); // Terminate should also remove the dependency, so we can remove the code. - assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0])); + assert_ok!(Contracts::remove_code( + RuntimeOrigin::signed(ALICE_FALLBACK), + callee_hashes[0] + )); }); } @@ -4109,13 +4116,13 @@ mod run_tests { let (wasm, _code_hash) = compile_module("balance_of").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::set_balance(&ALICE, 1_000_000); - let _ = Balances::set_balance(Ð_ALICE, 1_000_000); + let _ = Balances::set_balance(&ALICE_FALLBACK, 1_000_000); let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract(); // The fixture asserts a non-zero returned free balance of the account; - // The ETH_ALICE account is endowed; + // The ALICE_FALLBACK account is endowed; // Hence we should not revert assert_ok!(builder::call(addr).data(ALICE_ADDR.0.to_vec()).build()); @@ -4510,4 +4517,64 @@ mod run_tests { .build()); }); } + + #[test] + fn origin_must_be_mapped() { + let (code, hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + ::Currency::set_balance(&ALICE, 1_000_000); + ::Currency::set_balance(&EVE, 1_000_000); + + let eve = RuntimeOrigin::signed(EVE); + + // alice can instantiate as she doesn't need a mapping + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + // without a mapping eve can neither call nor instantiate + assert_err!( + builder::bare_call(addr).origin(eve.clone()).build().result, + >::AccountUnmapped + ); + assert_err!( + builder::bare_instantiate(Code::Existing(hash)) + .origin(eve.clone()) + .build() + .result, + >::AccountUnmapped + ); + + // after mapping eve is usable as an origin + >::map_account(eve.clone()).unwrap(); + assert_ok!(builder::bare_call(addr).origin(eve.clone()).build().result); + assert_ok!(builder::bare_instantiate(Code::Existing(hash)).origin(eve).build().result); + }); + } + + #[test] + fn mapped_address_works() { + let (code, _) = compile_module("terminate_and_send_to_eve").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + ::Currency::set_balance(&ALICE, 1_000_000); + + // without a mapping everything will be send to the fallback account + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code.clone())).build_and_unwrap_contract(); + assert_eq!(::Currency::total_balance(&EVE_FALLBACK), 0); + builder::bare_call(addr).build_and_unwrap_result(); + assert_eq!(::Currency::total_balance(&EVE_FALLBACK), 100); + + // after mapping it will be sent to the real eve account + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + // need some balance to pay for the map deposit + ::Currency::set_balance(&EVE, 1_000); + >::map_account(RuntimeOrigin::signed(EVE)).unwrap(); + builder::bare_call(addr).build_and_unwrap_result(); + assert_eq!(::Currency::total_balance(&EVE_FALLBACK), 100); + assert_eq!(::Currency::total_balance(&EVE), 1_100); + }); + } } diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 9a1b2310b4e..3203a0cba9f 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for `pallet_revive` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-10-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-jniz7bxx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-dr4vwrkf-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -58,6 +58,9 @@ pub trait WeightInfo { fn upload_code(c: u32, ) -> Weight; fn remove_code() -> Weight; fn set_code() -> Weight; + fn map_account() -> Weight; + fn unmap_account() -> Weight; + fn dispatch_as_fallback_account() -> Weight; fn noop_host_fn(r: u32, ) -> Weight; fn seal_caller() -> Weight; fn seal_is_contract() -> Weight; @@ -126,8 +129,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_712_000 picoseconds. - Weight::from_parts(2_882_000, 1594) + // Minimum execution time: 3_053_000 picoseconds. + Weight::from_parts(3_150_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -135,18 +138,20 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `392 + k * (69 ±0)` - // Estimated: `382 + k * (70 ±0)` - // Minimum execution time: 13_394_000 picoseconds. - Weight::from_parts(13_668_000, 382) - // Standard Error: 2_208 - .saturating_add(Weight::from_parts(1_340_842, 0).saturating_mul(k.into())) + // Measured: `425 + k * (69 ±0)` + // Estimated: `415 + k * (70 ±0)` + // Minimum execution time: 15_219_000 picoseconds. + Weight::from_parts(12_576_960, 415) + // Standard Error: 1_429 + .saturating_add(Weight::from_parts(1_341_896, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } + /// Storage: `Revive::AddressSuffix` (r:2 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -158,21 +163,21 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. - fn call_with_code_per_byte(c: u32, ) -> Weight { + fn call_with_code_per_byte(_c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1466` - // Estimated: `4931` - // Minimum execution time: 80_390_000 picoseconds. - Weight::from_parts(83_627_295, 4931) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Measured: `1519` + // Estimated: `7459` + // Minimum execution time: 88_906_000 picoseconds. + Weight::from_parts(93_353_224, 7459) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:2 w:2) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + /// 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:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) @@ -185,19 +190,21 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn instantiate_with_code(_c: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `303` - // Estimated: `6232` - // Minimum execution time: 184_708_000 picoseconds. - Weight::from_parts(177_995_416, 6232) - // Standard Error: 11 - .saturating_add(Weight::from_parts(4_609, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `416` + // Estimated: `6360` + // Minimum execution time: 202_688_000 picoseconds. + Weight::from_parts(197_366_807, 6360) + // Standard Error: 13 + .saturating_add(Weight::from_parts(4_261, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// 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:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) @@ -205,19 +212,21 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// The range of component `i` is `[0, 262144]`. fn instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1261` - // Estimated: `4706` - // Minimum execution time: 150_137_000 picoseconds. - Weight::from_parts(136_548_469, 4706) + // Measured: `1313` + // Estimated: `4779` + // Minimum execution time: 169_246_000 picoseconds. + Weight::from_parts(149_480_457, 4779) // Standard Error: 16 - .saturating_add(Weight::from_parts(4_531, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(Weight::from_parts(4_041, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } + /// Storage: `Revive::AddressSuffix` (r:2 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -230,17 +239,17 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `1466` - // Estimated: `4931` - // Minimum execution time: 83_178_000 picoseconds. - Weight::from_parts(84_633_000, 4931) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Measured: `1519` + // Estimated: `7459` + // Minimum execution time: 91_129_000 picoseconds. + Weight::from_parts(94_220_000, 7459) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. @@ -248,23 +257,23 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 51_526_000 picoseconds. - Weight::from_parts(54_565_973, 3574) + // Minimum execution time: 54_849_000 picoseconds. + Weight::from_parts(57_508_591, 3574) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn remove_code() -> Weight { // Proof Size summary in bytes: // Measured: `285` // Estimated: `3750` - // Minimum execution time: 41_885_000 picoseconds. - Weight::from_parts(42_467_000, 3750) + // Minimum execution time: 45_017_000 picoseconds. + Weight::from_parts(46_312_000, 3750) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -274,113 +283,153 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `6432` - // Minimum execution time: 24_905_000 picoseconds. - Weight::from_parts(25_483_000, 6432) + // Measured: `529` + // Estimated: `6469` + // Minimum execution time: 26_992_000 picoseconds. + Weight::from_parts(28_781_000, 6469) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + /// Storage: `Revive::AddressSuffix` (r:1 w:1) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + fn map_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3574` + // Minimum execution time: 44_031_000 picoseconds. + Weight::from_parts(45_133_000, 3574) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + /// Storage: `Revive::AddressSuffix` (r:0 w:1) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + fn unmap_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `56` + // Estimated: `3521` + // Minimum execution time: 35_681_000 picoseconds. + Weight::from_parts(36_331_000, 3521) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `Measured`) + fn dispatch_as_fallback_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 11_550_000 picoseconds. + Weight::from_parts(12_114_000, 3610) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } /// The range of component `r` is `[0, 1600]`. fn noop_host_fn(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_979_000 picoseconds. - Weight::from_parts(8_272_348, 0) - // Standard Error: 137 - .saturating_add(Weight::from_parts(172_489, 0).saturating_mul(r.into())) + // Minimum execution time: 7_063_000 picoseconds. + Weight::from_parts(7_671_454, 0) + // Standard Error: 105 + .saturating_add(Weight::from_parts(175_349, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 251_000 picoseconds. - Weight::from_parts(318_000, 0) + // Minimum execution time: 266_000 picoseconds. + Weight::from_parts(313_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_is_contract() -> Weight { // Proof Size summary in bytes: - // Measured: `272` - // Estimated: `3737` - // Minimum execution time: 6_966_000 picoseconds. - Weight::from_parts(7_240_000, 3737) + // Measured: `306` + // Estimated: `3771` + // Minimum execution time: 7_397_000 picoseconds. + Weight::from_parts(7_967_000, 3771) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `369` - // Estimated: `3834` - // Minimum execution time: 7_589_000 picoseconds. - Weight::from_parts(7_958_000, 3834) + // Measured: `403` + // Estimated: `3868` + // Minimum execution time: 8_395_000 picoseconds. + Weight::from_parts(8_863_000, 3868) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 235_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 265_000 picoseconds. + Weight::from_parts(292_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 283_000 picoseconds. - Weight::from_parts(326_000, 0) + // Minimum execution time: 298_000 picoseconds. + Weight::from_parts(334_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 266_000 picoseconds. - Weight::from_parts(298_000, 0) + // Minimum execution time: 262_000 picoseconds. + Weight::from_parts(274_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 240_000 picoseconds. - Weight::from_parts(290_000, 0) + // Minimum execution time: 277_000 picoseconds. + Weight::from_parts(297_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 651_000 picoseconds. - Weight::from_parts(714_000, 0) + // Minimum execution time: 620_000 picoseconds. + Weight::from_parts(706_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `103` + // Measured: `140` // Estimated: `0` - // Minimum execution time: 4_476_000 picoseconds. - Weight::from_parts(4_671_000, 0) + // Minimum execution time: 5_475_000 picoseconds. + Weight::from_parts(5_706_000, 0) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn seal_balance_of() -> Weight { // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 3_800_000 picoseconds. - Weight::from_parts(3_968_000, 3517) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Measured: `264` + // Estimated: `3729` + // Minimum execution time: 9_141_000 picoseconds. + Weight::from_parts(9_674_000, 3729) + .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) /// The range of component `n` is `[1, 4096]`. fn seal_get_immutable_data(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `205 + n * (1 ±0)` - // Estimated: `3670 + n * (1 ±0)` - // Minimum execution time: 5_845_000 picoseconds. - Weight::from_parts(6_473_478, 3670) - // Standard Error: 4 - .saturating_add(Weight::from_parts(651, 0).saturating_mul(n.into())) + // Measured: `238 + n * (1 ±0)` + // Estimated: `3703 + n * (1 ±0)` + // Minimum execution time: 6_443_000 picoseconds. + Weight::from_parts(7_252_595, 3703) + // Standard Error: 12 + .saturating_add(Weight::from_parts(915, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -391,39 +440,39 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_980_000 picoseconds. - Weight::from_parts(2_324_567, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(512, 0).saturating_mul(n.into())) + // Minimum execution time: 2_745_000 picoseconds. + Weight::from_parts(3_121_250, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(627, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 259_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 255_000 picoseconds. + Weight::from_parts(274_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 244_000 picoseconds. - Weight::from_parts(291_000, 0) + // Minimum execution time: 235_000 picoseconds. + Weight::from_parts(261_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 252_000 picoseconds. - Weight::from_parts(291_000, 0) + // Minimum execution time: 249_000 picoseconds. + Weight::from_parts(263_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 245_000 picoseconds. - Weight::from_parts(277_000, 0) + // Minimum execution time: 287_000 picoseconds. + Weight::from_parts(300_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -431,8 +480,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 5_650_000 picoseconds. - Weight::from_parts(5_783_000, 1552) + // Minimum execution time: 6_147_000 picoseconds. + Weight::from_parts(6_562_000, 1552) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 262140]`. @@ -440,21 +489,23 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 427_000 picoseconds. - Weight::from_parts(351_577, 0) + // Minimum execution time: 453_000 picoseconds. + Weight::from_parts(548_774, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(114, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(147, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 265_000 picoseconds. - Weight::from_parts(746_316, 0) + // Minimum execution time: 264_000 picoseconds. + Weight::from_parts(490_374, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(202, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(236, 0).saturating_mul(n.into())) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::DeletionQueueCounter` (r:1 w:1) /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:33 w:33) @@ -466,13 +517,13 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[0, 32]`. fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `272 + n * (88 ±0)` - // Estimated: `3737 + n * (2563 ±0)` - // Minimum execution time: 15_988_000 picoseconds. - Weight::from_parts(18_796_705, 3737) - // Standard Error: 10_437 - .saturating_add(Weight::from_parts(4_338_085, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `323 + n * (88 ±0)` + // Estimated: `3788 + n * (2563 ±0)` + // Minimum execution time: 22_833_000 picoseconds. + Weight::from_parts(24_805_620, 3788) + // Standard Error: 9_498 + .saturating_add(Weight::from_parts(4_486_714, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -484,22 +535,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_237_000 picoseconds. - Weight::from_parts(4_128_112, 0) - // Standard Error: 2_947 - .saturating_add(Weight::from_parts(198_825, 0).saturating_mul(t.into())) - // Standard Error: 26 - .saturating_add(Weight::from_parts(1_007, 0).saturating_mul(n.into())) + // Minimum execution time: 4_969_000 picoseconds. + Weight::from_parts(4_994_916, 0) + // Standard Error: 3_727 + .saturating_add(Weight::from_parts(188_374, 0).saturating_mul(t.into())) + // Standard Error: 33 + .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) } /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 292_000 picoseconds. - Weight::from_parts(1_297_376, 0) + // Minimum execution time: 328_000 picoseconds. + Weight::from_parts(928_905, 0) // Standard Error: 1 - .saturating_add(Weight::from_parts(724, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(753, 0).saturating_mul(i.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -507,8 +558,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 7_812_000 picoseconds. - Weight::from_parts(8_171_000, 744) + // Minimum execution time: 8_612_000 picoseconds. + Weight::from_parts(9_326_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -517,8 +568,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 44_179_000 picoseconds. - Weight::from_parts(45_068_000, 10754) + // Minimum execution time: 44_542_000 picoseconds. + Weight::from_parts(45_397_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -527,8 +578,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 8_964_000 picoseconds. - Weight::from_parts(9_336_000, 744) + // Minimum execution time: 10_343_000 picoseconds. + Weight::from_parts(10_883_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -538,8 +589,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 45_606_000 picoseconds. - Weight::from_parts(47_190_000, 10754) + // Minimum execution time: 46_835_000 picoseconds. + Weight::from_parts(47_446_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -551,12 +602,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 9_077_000 picoseconds. - Weight::from_parts(9_823_489, 247) - // Standard Error: 54 - .saturating_add(Weight::from_parts(392, 0).saturating_mul(n.into())) - // Standard Error: 54 - .saturating_add(Weight::from_parts(408, 0).saturating_mul(o.into())) + // Minimum execution time: 10_604_000 picoseconds. + Weight::from_parts(11_282_849, 247) + // Standard Error: 48 + .saturating_add(Weight::from_parts(496, 0).saturating_mul(n.into())) + // Standard Error: 48 + .saturating_add(Weight::from_parts(764, 0).saturating_mul(o.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -568,10 +619,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_812_000 picoseconds. - Weight::from_parts(9_626_925, 247) - // Standard Error: 77 - .saturating_add(Weight::from_parts(269, 0).saturating_mul(n.into())) + // Minimum execution time: 10_081_000 picoseconds. + Weight::from_parts(11_186_557, 247) + // Standard Error: 68 + .saturating_add(Weight::from_parts(782, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -583,10 +634,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_143_000 picoseconds. - Weight::from_parts(9_229_363, 247) - // Standard Error: 77 - .saturating_add(Weight::from_parts(1_198, 0).saturating_mul(n.into())) + // Minimum execution time: 8_758_000 picoseconds. + Weight::from_parts(9_939_492, 247) + // Standard Error: 69 + .saturating_add(Weight::from_parts(1_703, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -597,10 +648,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_899_000 picoseconds. - Weight::from_parts(8_591_860, 247) - // Standard Error: 56 - .saturating_add(Weight::from_parts(461, 0).saturating_mul(n.into())) + // Minimum execution time: 8_525_000 picoseconds. + Weight::from_parts(9_522_265, 247) + // Standard Error: 66 + .saturating_add(Weight::from_parts(426, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -611,10 +662,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 9_215_000 picoseconds. - Weight::from_parts(10_198_528, 247) - // Standard Error: 75 - .saturating_add(Weight::from_parts(1_521, 0).saturating_mul(n.into())) + // Minimum execution time: 10_603_000 picoseconds. + Weight::from_parts(11_817_752, 247) + // Standard Error: 82 + .saturating_add(Weight::from_parts(1_279, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -623,36 +674,36 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_406_000 picoseconds. - Weight::from_parts(1_515_000, 0) + // Minimum execution time: 1_553_000 picoseconds. + Weight::from_parts(1_615_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_782_000 picoseconds. - Weight::from_parts(1_890_000, 0) + // Minimum execution time: 1_932_000 picoseconds. + Weight::from_parts(2_064_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_380_000 picoseconds. - Weight::from_parts(1_422_000, 0) + // Minimum execution time: 1_510_000 picoseconds. + Weight::from_parts(1_545_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_504_000 picoseconds. - Weight::from_parts(1_583_000, 0) + // Minimum execution time: 1_663_000 picoseconds. + Weight::from_parts(1_801_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_045_000 picoseconds. - Weight::from_parts(1_138_000, 0) + // Minimum execution time: 1_026_000 picoseconds. + Weight::from_parts(1_137_000, 0) } /// The range of component `n` is `[0, 512]`. /// The range of component `o` is `[0, 512]`. @@ -660,60 +711,63 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_039_000 picoseconds. - Weight::from_parts(2_317_406, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(283, 0).saturating_mul(n.into())) - // Standard Error: 13 - .saturating_add(Weight::from_parts(347, 0).saturating_mul(o.into())) + // Minimum execution time: 2_446_000 picoseconds. + Weight::from_parts(2_644_525, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(113, 0).saturating_mul(n.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(179, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_880_000 picoseconds. - Weight::from_parts(2_251_392, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(313, 0).saturating_mul(n.into())) + // Minimum execution time: 2_085_000 picoseconds. + Weight::from_parts(2_379_853, 0) + // Standard Error: 19 + .saturating_add(Weight::from_parts(366, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_763_000 picoseconds. - Weight::from_parts(1_951_912, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(268, 0).saturating_mul(n.into())) + // Minimum execution time: 1_876_000 picoseconds. + Weight::from_parts(2_073_689, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(376, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_536_000 picoseconds. - Weight::from_parts(1_779_085, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) + // Minimum execution time: 1_688_000 picoseconds. + Weight::from_parts(1_914_470, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(125, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. - fn seal_take_transient_storage(n: u32, ) -> Weight { + fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_343_000 picoseconds. - Weight::from_parts(2_587_750, 0) - // Standard Error: 22 - .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) + // Minimum execution time: 2_479_000 picoseconds. + Weight::from_parts(2_758_250, 0) } + /// 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: `103` - // Estimated: `0` - // Minimum execution time: 9_250_000 picoseconds. - Weight::from_parts(9_637_000, 0) + // Measured: `352` + // Estimated: `3817` + // Minimum execution time: 15_745_000 picoseconds. + Weight::from_parts(16_300_000, 3817) + .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) @@ -724,17 +778,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1221 + t * (103 ±0)` - // Estimated: `4686 + t * (103 ±0)` - // Minimum execution time: 33_333_000 picoseconds. - Weight::from_parts(34_378_774, 4686) - // Standard Error: 41_131 - .saturating_add(Weight::from_parts(1_756_626, 0).saturating_mul(t.into())) + // Measured: `1309 + t * (140 ±0)` + // Estimated: `4774 + t * (140 ±0)` + // Minimum execution time: 39_639_000 picoseconds. + Weight::from_parts(40_909_376, 4774) + // Standard Error: 54_479 + .saturating_add(Weight::from_parts(1_526_185, 0).saturating_mul(t.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(Weight::from_parts(4, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 103).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 140).saturating_mul(t.into())) } /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -742,10 +796,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn seal_delegate_call() -> Weight { // Proof Size summary in bytes: - // Measured: `1048` - // Estimated: `4513` - // Minimum execution time: 27_096_000 picoseconds. - Weight::from_parts(27_934_000, 4513) + // Measured: `1081` + // Estimated: `4546` + // Minimum execution time: 29_651_000 picoseconds. + Weight::from_parts(31_228_000, 4546) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -759,12 +813,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn seal_instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1257` - // Estimated: `4715` - // Minimum execution time: 118_412_000 picoseconds. - Weight::from_parts(106_130_041, 4715) - // Standard Error: 12 - .saturating_add(Weight::from_parts(4_235, 0).saturating_mul(i.into())) + // Measured: `1327` + // Estimated: `4792` + // Minimum execution time: 126_995_000 picoseconds. + Weight::from_parts(114_028_446, 4792) + // Standard Error: 11 + .saturating_add(Weight::from_parts(3_781, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -773,73 +827,73 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 614_000 picoseconds. - Weight::from_parts(4_320_897, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_371, 0).saturating_mul(n.into())) + // Minimum execution time: 653_000 picoseconds. + Weight::from_parts(973_524, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_048, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_062_000 picoseconds. - Weight::from_parts(4_571_371, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_572, 0).saturating_mul(n.into())) + // Minimum execution time: 1_118_000 picoseconds. + Weight::from_parts(795_498, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_260, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 609_000 picoseconds. - Weight::from_parts(4_008_056, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_497, 0).saturating_mul(n.into())) + // Minimum execution time: 647_000 picoseconds. + Weight::from_parts(667_024, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 608_000 picoseconds. - Weight::from_parts(3_839_383, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_504, 0).saturating_mul(n.into())) + // Minimum execution time: 605_000 picoseconds. + Weight::from_parts(675_568, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_181, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 43_110_000 picoseconds. - Weight::from_parts(31_941_593, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(5_233, 0).saturating_mul(n.into())) + // Minimum execution time: 42_743_000 picoseconds. + Weight::from_parts(26_131_984, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(4_867, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 47_798_000 picoseconds. - Weight::from_parts(49_225_000, 0) + // Minimum execution time: 50_838_000 picoseconds. + Weight::from_parts(52_248_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_576_000 picoseconds. - Weight::from_parts(12_731_000, 0) + // Minimum execution time: 12_605_000 picoseconds. + Weight::from_parts(12_796_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn seal_set_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `266` - // Estimated: `3731` - // Minimum execution time: 14_306_000 picoseconds. - Weight::from_parts(15_011_000, 3731) + // Measured: `300` + // Estimated: `3765` + // Minimum execution time: 16_377_000 picoseconds. + Weight::from_parts(16_932_000, 3765) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -847,10 +901,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn lock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `303` - // Estimated: `3768` - // Minimum execution time: 10_208_000 picoseconds. - Weight::from_parts(10_514_000, 3768) + // Measured: `338` + // Estimated: `3803` + // Minimum execution time: 11_499_000 picoseconds. + Weight::from_parts(12_104_000, 3803) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -858,10 +912,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`) fn unlock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `303` + // Measured: `338` // Estimated: `3561` - // Minimum execution time: 9_062_000 picoseconds. - Weight::from_parts(9_414_000, 3561) + // Minimum execution time: 10_308_000 picoseconds. + Weight::from_parts(11_000_000, 3561) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -870,10 +924,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_074_000 picoseconds. - Weight::from_parts(9_646_158, 0) - // Standard Error: 58 - .saturating_add(Weight::from_parts(84_694, 0).saturating_mul(r.into())) + // Minimum execution time: 8_162_000 picoseconds. + Weight::from_parts(9_180_011, 0) + // Standard Error: 63 + .saturating_add(Weight::from_parts(84_822, 0).saturating_mul(r.into())) } } @@ -885,8 +939,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_712_000 picoseconds. - Weight::from_parts(2_882_000, 1594) + // Minimum execution time: 3_053_000 picoseconds. + Weight::from_parts(3_150_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -894,18 +948,20 @@ impl WeightInfo for () { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `392 + k * (69 ±0)` - // Estimated: `382 + k * (70 ±0)` - // Minimum execution time: 13_394_000 picoseconds. - Weight::from_parts(13_668_000, 382) - // Standard Error: 2_208 - .saturating_add(Weight::from_parts(1_340_842, 0).saturating_mul(k.into())) + // Measured: `425 + k * (69 ±0)` + // Estimated: `415 + k * (70 ±0)` + // Minimum execution time: 15_219_000 picoseconds. + Weight::from_parts(12_576_960, 415) + // Standard Error: 1_429 + .saturating_add(Weight::from_parts(1_341_896, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } + /// Storage: `Revive::AddressSuffix` (r:2 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -917,21 +973,21 @@ impl WeightInfo for () { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. - fn call_with_code_per_byte(c: u32, ) -> Weight { + fn call_with_code_per_byte(_c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1466` - // Estimated: `4931` - // Minimum execution time: 80_390_000 picoseconds. - Weight::from_parts(83_627_295, 4931) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Measured: `1519` + // Estimated: `7459` + // Minimum execution time: 88_906_000 picoseconds. + Weight::from_parts(93_353_224, 7459) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:2 w:2) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + /// 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:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) @@ -944,19 +1000,21 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn instantiate_with_code(_c: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `303` - // Estimated: `6232` - // Minimum execution time: 184_708_000 picoseconds. - Weight::from_parts(177_995_416, 6232) - // Standard Error: 11 - .saturating_add(Weight::from_parts(4_609, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `416` + // Estimated: `6360` + // Minimum execution time: 202_688_000 picoseconds. + Weight::from_parts(197_366_807, 6360) + // Standard Error: 13 + .saturating_add(Weight::from_parts(4_261, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// 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:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) @@ -964,19 +1022,21 @@ impl WeightInfo for () { /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// The range of component `i` is `[0, 262144]`. fn instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1261` - // Estimated: `4706` - // Minimum execution time: 150_137_000 picoseconds. - Weight::from_parts(136_548_469, 4706) + // Measured: `1313` + // Estimated: `4779` + // Minimum execution time: 169_246_000 picoseconds. + Weight::from_parts(149_480_457, 4779) // Standard Error: 16 - .saturating_add(Weight::from_parts(4_531, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(Weight::from_parts(4_041, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } + /// Storage: `Revive::AddressSuffix` (r:2 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) @@ -989,17 +1049,17 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `1466` - // Estimated: `4931` - // Minimum execution time: 83_178_000 picoseconds. - Weight::from_parts(84_633_000, 4931) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Measured: `1519` + // Estimated: `7459` + // Minimum execution time: 91_129_000 picoseconds. + Weight::from_parts(94_220_000, 7459) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// The range of component `c` is `[0, 262144]`. @@ -1007,23 +1067,23 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 51_526_000 picoseconds. - Weight::from_parts(54_565_973, 3574) + // Minimum execution time: 54_849_000 picoseconds. + Weight::from_parts(57_508_591, 3574) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:0 w:1) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn remove_code() -> Weight { // Proof Size summary in bytes: // Measured: `285` // Estimated: `3750` - // Minimum execution time: 41_885_000 picoseconds. - Weight::from_parts(42_467_000, 3750) + // Minimum execution time: 45_017_000 picoseconds. + Weight::from_parts(46_312_000, 3750) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1033,113 +1093,153 @@ impl WeightInfo for () { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `492` - // Estimated: `6432` - // Minimum execution time: 24_905_000 picoseconds. - Weight::from_parts(25_483_000, 6432) + // Measured: `529` + // Estimated: `6469` + // Minimum execution time: 26_992_000 picoseconds. + Weight::from_parts(28_781_000, 6469) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + /// Storage: `Revive::AddressSuffix` (r:1 w:1) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + fn map_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3574` + // Minimum execution time: 44_031_000 picoseconds. + Weight::from_parts(45_133_000, 3574) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `Measured`) + /// Storage: `Revive::AddressSuffix` (r:0 w:1) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + fn unmap_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `56` + // Estimated: `3521` + // Minimum execution time: 35_681_000 picoseconds. + Weight::from_parts(36_331_000, 3521) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `Measured`) + /// Storage: `TxPause::PausedCalls` (r:1 w:0) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `Measured`) + fn dispatch_as_fallback_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `145` + // Estimated: `3610` + // Minimum execution time: 11_550_000 picoseconds. + Weight::from_parts(12_114_000, 3610) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } /// The range of component `r` is `[0, 1600]`. fn noop_host_fn(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_979_000 picoseconds. - Weight::from_parts(8_272_348, 0) - // Standard Error: 137 - .saturating_add(Weight::from_parts(172_489, 0).saturating_mul(r.into())) + // Minimum execution time: 7_063_000 picoseconds. + Weight::from_parts(7_671_454, 0) + // Standard Error: 105 + .saturating_add(Weight::from_parts(175_349, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 251_000 picoseconds. - Weight::from_parts(318_000, 0) + // Minimum execution time: 266_000 picoseconds. + Weight::from_parts(313_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_is_contract() -> Weight { // Proof Size summary in bytes: - // Measured: `272` - // Estimated: `3737` - // Minimum execution time: 6_966_000 picoseconds. - Weight::from_parts(7_240_000, 3737) + // Measured: `306` + // Estimated: `3771` + // Minimum execution time: 7_397_000 picoseconds. + Weight::from_parts(7_967_000, 3771) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `369` - // Estimated: `3834` - // Minimum execution time: 7_589_000 picoseconds. - Weight::from_parts(7_958_000, 3834) + // Measured: `403` + // Estimated: `3868` + // Minimum execution time: 8_395_000 picoseconds. + Weight::from_parts(8_863_000, 3868) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 235_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 265_000 picoseconds. + Weight::from_parts(292_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 283_000 picoseconds. - Weight::from_parts(326_000, 0) + // Minimum execution time: 298_000 picoseconds. + Weight::from_parts(334_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 266_000 picoseconds. - Weight::from_parts(298_000, 0) + // Minimum execution time: 262_000 picoseconds. + Weight::from_parts(274_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 240_000 picoseconds. - Weight::from_parts(290_000, 0) + // Minimum execution time: 277_000 picoseconds. + Weight::from_parts(297_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 651_000 picoseconds. - Weight::from_parts(714_000, 0) + // Minimum execution time: 620_000 picoseconds. + Weight::from_parts(706_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `103` + // Measured: `140` // Estimated: `0` - // Minimum execution time: 4_476_000 picoseconds. - Weight::from_parts(4_671_000, 0) + // Minimum execution time: 5_475_000 picoseconds. + Weight::from_parts(5_706_000, 0) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn seal_balance_of() -> Weight { // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 3_800_000 picoseconds. - Weight::from_parts(3_968_000, 3517) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + // Measured: `264` + // Estimated: `3729` + // Minimum execution time: 9_141_000 picoseconds. + Weight::from_parts(9_674_000, 3729) + .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) /// The range of component `n` is `[1, 4096]`. fn seal_get_immutable_data(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `205 + n * (1 ±0)` - // Estimated: `3670 + n * (1 ±0)` - // Minimum execution time: 5_845_000 picoseconds. - Weight::from_parts(6_473_478, 3670) - // Standard Error: 4 - .saturating_add(Weight::from_parts(651, 0).saturating_mul(n.into())) + // Measured: `238 + n * (1 ±0)` + // Estimated: `3703 + n * (1 ±0)` + // Minimum execution time: 6_443_000 picoseconds. + Weight::from_parts(7_252_595, 3703) + // Standard Error: 12 + .saturating_add(Weight::from_parts(915, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1150,39 +1250,39 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_980_000 picoseconds. - Weight::from_parts(2_324_567, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(512, 0).saturating_mul(n.into())) + // Minimum execution time: 2_745_000 picoseconds. + Weight::from_parts(3_121_250, 0) + // Standard Error: 4 + .saturating_add(Weight::from_parts(627, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 259_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 255_000 picoseconds. + Weight::from_parts(274_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 244_000 picoseconds. - Weight::from_parts(291_000, 0) + // Minimum execution time: 235_000 picoseconds. + Weight::from_parts(261_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 252_000 picoseconds. - Weight::from_parts(291_000, 0) + // Minimum execution time: 249_000 picoseconds. + Weight::from_parts(263_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 245_000 picoseconds. - Weight::from_parts(277_000, 0) + // Minimum execution time: 287_000 picoseconds. + Weight::from_parts(300_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -1190,8 +1290,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 5_650_000 picoseconds. - Weight::from_parts(5_783_000, 1552) + // Minimum execution time: 6_147_000 picoseconds. + Weight::from_parts(6_562_000, 1552) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 262140]`. @@ -1199,21 +1299,23 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 427_000 picoseconds. - Weight::from_parts(351_577, 0) + // Minimum execution time: 453_000 picoseconds. + Weight::from_parts(548_774, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(114, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(147, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 265_000 picoseconds. - Weight::from_parts(746_316, 0) + // Minimum execution time: 264_000 picoseconds. + Weight::from_parts(490_374, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(202, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(236, 0).saturating_mul(n.into())) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) /// Storage: `Revive::DeletionQueueCounter` (r:1 w:1) /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:33 w:33) @@ -1225,13 +1327,13 @@ impl WeightInfo for () { /// The range of component `n` is `[0, 32]`. fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `272 + n * (88 ±0)` - // Estimated: `3737 + n * (2563 ±0)` - // Minimum execution time: 15_988_000 picoseconds. - Weight::from_parts(18_796_705, 3737) - // Standard Error: 10_437 - .saturating_add(Weight::from_parts(4_338_085, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `323 + n * (88 ±0)` + // Estimated: `3788 + n * (2563 ±0)` + // Minimum execution time: 22_833_000 picoseconds. + Weight::from_parts(24_805_620, 3788) + // Standard Error: 9_498 + .saturating_add(Weight::from_parts(4_486_714, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) @@ -1243,22 +1345,22 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_237_000 picoseconds. - Weight::from_parts(4_128_112, 0) - // Standard Error: 2_947 - .saturating_add(Weight::from_parts(198_825, 0).saturating_mul(t.into())) - // Standard Error: 26 - .saturating_add(Weight::from_parts(1_007, 0).saturating_mul(n.into())) + // Minimum execution time: 4_969_000 picoseconds. + Weight::from_parts(4_994_916, 0) + // Standard Error: 3_727 + .saturating_add(Weight::from_parts(188_374, 0).saturating_mul(t.into())) + // Standard Error: 33 + .saturating_add(Weight::from_parts(925, 0).saturating_mul(n.into())) } /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 292_000 picoseconds. - Weight::from_parts(1_297_376, 0) + // Minimum execution time: 328_000 picoseconds. + Weight::from_parts(928_905, 0) // Standard Error: 1 - .saturating_add(Weight::from_parts(724, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(753, 0).saturating_mul(i.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1266,8 +1368,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 7_812_000 picoseconds. - Weight::from_parts(8_171_000, 744) + // Minimum execution time: 8_612_000 picoseconds. + Weight::from_parts(9_326_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1276,8 +1378,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 44_179_000 picoseconds. - Weight::from_parts(45_068_000, 10754) + // Minimum execution time: 44_542_000 picoseconds. + Weight::from_parts(45_397_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1286,8 +1388,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 8_964_000 picoseconds. - Weight::from_parts(9_336_000, 744) + // Minimum execution time: 10_343_000 picoseconds. + Weight::from_parts(10_883_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1297,8 +1399,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 45_606_000 picoseconds. - Weight::from_parts(47_190_000, 10754) + // Minimum execution time: 46_835_000 picoseconds. + Weight::from_parts(47_446_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1310,12 +1412,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 9_077_000 picoseconds. - Weight::from_parts(9_823_489, 247) - // Standard Error: 54 - .saturating_add(Weight::from_parts(392, 0).saturating_mul(n.into())) - // Standard Error: 54 - .saturating_add(Weight::from_parts(408, 0).saturating_mul(o.into())) + // Minimum execution time: 10_604_000 picoseconds. + Weight::from_parts(11_282_849, 247) + // Standard Error: 48 + .saturating_add(Weight::from_parts(496, 0).saturating_mul(n.into())) + // Standard Error: 48 + .saturating_add(Weight::from_parts(764, 0).saturating_mul(o.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -1327,10 +1429,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_812_000 picoseconds. - Weight::from_parts(9_626_925, 247) - // Standard Error: 77 - .saturating_add(Weight::from_parts(269, 0).saturating_mul(n.into())) + // Minimum execution time: 10_081_000 picoseconds. + Weight::from_parts(11_186_557, 247) + // Standard Error: 68 + .saturating_add(Weight::from_parts(782, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1342,10 +1444,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_143_000 picoseconds. - Weight::from_parts(9_229_363, 247) - // Standard Error: 77 - .saturating_add(Weight::from_parts(1_198, 0).saturating_mul(n.into())) + // Minimum execution time: 8_758_000 picoseconds. + Weight::from_parts(9_939_492, 247) + // Standard Error: 69 + .saturating_add(Weight::from_parts(1_703, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1356,10 +1458,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_899_000 picoseconds. - Weight::from_parts(8_591_860, 247) - // Standard Error: 56 - .saturating_add(Weight::from_parts(461, 0).saturating_mul(n.into())) + // Minimum execution time: 8_525_000 picoseconds. + Weight::from_parts(9_522_265, 247) + // Standard Error: 66 + .saturating_add(Weight::from_parts(426, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1370,10 +1472,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 9_215_000 picoseconds. - Weight::from_parts(10_198_528, 247) - // Standard Error: 75 - .saturating_add(Weight::from_parts(1_521, 0).saturating_mul(n.into())) + // Minimum execution time: 10_603_000 picoseconds. + Weight::from_parts(11_817_752, 247) + // Standard Error: 82 + .saturating_add(Weight::from_parts(1_279, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1382,36 +1484,36 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_406_000 picoseconds. - Weight::from_parts(1_515_000, 0) + // Minimum execution time: 1_553_000 picoseconds. + Weight::from_parts(1_615_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_782_000 picoseconds. - Weight::from_parts(1_890_000, 0) + // Minimum execution time: 1_932_000 picoseconds. + Weight::from_parts(2_064_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_380_000 picoseconds. - Weight::from_parts(1_422_000, 0) + // Minimum execution time: 1_510_000 picoseconds. + Weight::from_parts(1_545_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_504_000 picoseconds. - Weight::from_parts(1_583_000, 0) + // Minimum execution time: 1_663_000 picoseconds. + Weight::from_parts(1_801_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_045_000 picoseconds. - Weight::from_parts(1_138_000, 0) + // Minimum execution time: 1_026_000 picoseconds. + Weight::from_parts(1_137_000, 0) } /// The range of component `n` is `[0, 512]`. /// The range of component `o` is `[0, 512]`. @@ -1419,60 +1521,63 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_039_000 picoseconds. - Weight::from_parts(2_317_406, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(283, 0).saturating_mul(n.into())) - // Standard Error: 13 - .saturating_add(Weight::from_parts(347, 0).saturating_mul(o.into())) + // Minimum execution time: 2_446_000 picoseconds. + Weight::from_parts(2_644_525, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(113, 0).saturating_mul(n.into())) + // Standard Error: 17 + .saturating_add(Weight::from_parts(179, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_880_000 picoseconds. - Weight::from_parts(2_251_392, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(313, 0).saturating_mul(n.into())) + // Minimum execution time: 2_085_000 picoseconds. + Weight::from_parts(2_379_853, 0) + // Standard Error: 19 + .saturating_add(Weight::from_parts(366, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_763_000 picoseconds. - Weight::from_parts(1_951_912, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(268, 0).saturating_mul(n.into())) + // Minimum execution time: 1_876_000 picoseconds. + Weight::from_parts(2_073_689, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(376, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_536_000 picoseconds. - Weight::from_parts(1_779_085, 0) - // Standard Error: 14 - .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) + // Minimum execution time: 1_688_000 picoseconds. + Weight::from_parts(1_914_470, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(125, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. - fn seal_take_transient_storage(n: u32, ) -> Weight { + fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_343_000 picoseconds. - Weight::from_parts(2_587_750, 0) - // Standard Error: 22 - .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) + // Minimum execution time: 2_479_000 picoseconds. + Weight::from_parts(2_758_250, 0) } + /// 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: `103` - // Estimated: `0` - // Minimum execution time: 9_250_000 picoseconds. - Weight::from_parts(9_637_000, 0) + // Measured: `352` + // Estimated: `3817` + // Minimum execution time: 15_745_000 picoseconds. + Weight::from_parts(16_300_000, 3817) + .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) @@ -1483,17 +1588,17 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1221 + t * (103 ±0)` - // Estimated: `4686 + t * (103 ±0)` - // Minimum execution time: 33_333_000 picoseconds. - Weight::from_parts(34_378_774, 4686) - // Standard Error: 41_131 - .saturating_add(Weight::from_parts(1_756_626, 0).saturating_mul(t.into())) + // Measured: `1309 + t * (140 ±0)` + // Estimated: `4774 + t * (140 ±0)` + // Minimum execution time: 39_639_000 picoseconds. + Weight::from_parts(40_909_376, 4774) + // Standard Error: 54_479 + .saturating_add(Weight::from_parts(1_526_185, 0).saturating_mul(t.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(Weight::from_parts(4, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 103).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 140).saturating_mul(t.into())) } /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -1501,10 +1606,10 @@ impl WeightInfo for () { /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn seal_delegate_call() -> Weight { // Proof Size summary in bytes: - // Measured: `1048` - // Estimated: `4513` - // Minimum execution time: 27_096_000 picoseconds. - Weight::from_parts(27_934_000, 4513) + // Measured: `1081` + // Estimated: `4546` + // Minimum execution time: 29_651_000 picoseconds. + Weight::from_parts(31_228_000, 4546) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -1518,12 +1623,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn seal_instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1257` - // Estimated: `4715` - // Minimum execution time: 118_412_000 picoseconds. - Weight::from_parts(106_130_041, 4715) - // Standard Error: 12 - .saturating_add(Weight::from_parts(4_235, 0).saturating_mul(i.into())) + // Measured: `1327` + // Estimated: `4792` + // Minimum execution time: 126_995_000 picoseconds. + Weight::from_parts(114_028_446, 4792) + // Standard Error: 11 + .saturating_add(Weight::from_parts(3_781, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1532,73 +1637,73 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 614_000 picoseconds. - Weight::from_parts(4_320_897, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_371, 0).saturating_mul(n.into())) + // Minimum execution time: 653_000 picoseconds. + Weight::from_parts(973_524, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_048, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_062_000 picoseconds. - Weight::from_parts(4_571_371, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(3_572, 0).saturating_mul(n.into())) + // Minimum execution time: 1_118_000 picoseconds. + Weight::from_parts(795_498, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(3_260, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 609_000 picoseconds. - Weight::from_parts(4_008_056, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_497, 0).saturating_mul(n.into())) + // Minimum execution time: 647_000 picoseconds. + Weight::from_parts(667_024, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_183, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 608_000 picoseconds. - Weight::from_parts(3_839_383, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_504, 0).saturating_mul(n.into())) + // Minimum execution time: 605_000 picoseconds. + Weight::from_parts(675_568, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1_181, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 43_110_000 picoseconds. - Weight::from_parts(31_941_593, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(5_233, 0).saturating_mul(n.into())) + // Minimum execution time: 42_743_000 picoseconds. + Weight::from_parts(26_131_984, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(4_867, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 47_798_000 picoseconds. - Weight::from_parts(49_225_000, 0) + // Minimum execution time: 50_838_000 picoseconds. + Weight::from_parts(52_248_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_576_000 picoseconds. - Weight::from_parts(12_731_000, 0) + // Minimum execution time: 12_605_000 picoseconds. + Weight::from_parts(12_796_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn seal_set_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `266` - // Estimated: `3731` - // Minimum execution time: 14_306_000 picoseconds. - Weight::from_parts(15_011_000, 3731) + // Measured: `300` + // Estimated: `3765` + // Minimum execution time: 16_377_000 picoseconds. + Weight::from_parts(16_932_000, 3765) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1606,10 +1711,10 @@ impl WeightInfo for () { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn lock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `303` - // Estimated: `3768` - // Minimum execution time: 10_208_000 picoseconds. - Weight::from_parts(10_514_000, 3768) + // Measured: `338` + // Estimated: `3803` + // Minimum execution time: 11_499_000 picoseconds. + Weight::from_parts(12_104_000, 3803) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1617,10 +1722,10 @@ impl WeightInfo for () { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`) fn unlock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `303` + // Measured: `338` // Estimated: `3561` - // Minimum execution time: 9_062_000 picoseconds. - Weight::from_parts(9_414_000, 3561) + // Minimum execution time: 10_308_000 picoseconds. + Weight::from_parts(11_000_000, 3561) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1629,9 +1734,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_074_000 picoseconds. - Weight::from_parts(9_646_158, 0) - // Standard Error: 58 - .saturating_add(Weight::from_parts(84_694, 0).saturating_mul(r.into())) + // Minimum execution time: 8_162_000 picoseconds. + Weight::from_parts(9_180_011, 0) + // Standard Error: 63 + .saturating_add(Weight::from_parts(84_822, 0).saturating_mul(r.into())) } } -- GitLab From efd660309f2ee944718a6302627bbb956ada3729 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 25 Oct 2024 11:31:21 +0200 Subject: [PATCH 47/50] Fix migrations for pallet-xcm (#6148) Relates to: https://github.com/paritytech/polkadot-sdk/pull/4826 Relates to: https://github.com/paritytech/polkadot-sdk/issues/3214 ## Description `pallet-xcm` stores some operational data that uses `Versioned*` XCM types. When we add a new XCM version (XV), we deprecate XV-2 and remove XV-3. Without proper migration, this can lead to issues with [undecodable storage](https://github.com/paritytech/polkadot-sdk/actions/runs/11381324568/job/31662577532?pr=6092), as was identified on the XCMv5 branch where XCMv2 was removed. This PR extends the existing `MigrateToLatestXcmVersion` to include migration for the `Queries`, `LockedFungibles`, and `RemoteLockedFungibles` storage types. Additionally, more checks were added to `try_state` for these types. ## TODO - [x] create tracking issue for `polkadot-fellows` https://github.com/polkadot-fellows/runtimes/issues/492 - [x] Add missing `MigrateToLatestXcmVersion` for westend - [x] fix pallet-xcm `Queries` - fails for Westend https://github.com/paritytech/polkadot-sdk/actions/runs/11381324568/job/31662577532?pr=6092 - `V2` was removed from `Versioned*` stuff, but we have a live data with V2 e.g. Queries - e.g. Kusama or Polkadot relay chains ``` VersionNotifier: { origin: { V2: { parents: 0 interior: { X1: { Parachain: 2,124 } } } } isActive: true } ``` ![image](https://github.com/user-attachments/assets/f59f761b-46a7-4def-8aea-45c4e41c0a00) - [x] fix also for `RemoteLockedFungibles` - [x] fix also for `LockedFungibles` ## Follow-ups - [ ] deploy on Westend chains before XCMv5 - [ ] https://github.com/paritytech/polkadot-sdk/issues/6188 --------- Co-authored-by: command-bot <> Co-authored-by: GitHub Action Co-authored-by: Francisco Aguirre --- polkadot/runtime/westend/src/lib.rs | 2 + .../parachain/xcm_config.rs | 2 +- .../relay_chain/xcm_config.rs | 2 +- polkadot/xcm/pallet-xcm/src/lib.rs | 42 +- polkadot/xcm/pallet-xcm/src/migration.rs | 379 +++++++++++++++++- polkadot/xcm/pallet-xcm/src/tests/mod.rs | 172 +++++++- .../single_asset_adapter/mock.rs | 2 +- .../xcm/xcm-builder/src/tests/pay/mock.rs | 3 +- polkadot/xcm/xcm-runtime-apis/tests/mock.rs | 3 +- prdoc/pr_6148.prdoc | 17 + 10 files changed, 610 insertions(+), 14 deletions(-) create mode 100644 prdoc/pr_6148.prdoc diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index a91ca399db4..4941d91df57 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1809,6 +1809,8 @@ pub mod migrations { >, parachains_shared::migration::MigrateToV1, parachains_scheduler::migration::MigrateV2ToV3, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, ); } 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 7cb230f6e00..f8a1826b8ab 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 @@ -168,7 +168,7 @@ impl pallet_xcm::Config for Runtime { type UniversalLocation = UniversalLocation; // No version discovery needed const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0; - type AdvertisedXcmVersion = frame::traits::ConstU32<3>; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type AdminOrigin = frame_system::EnsureRoot; // No locking type TrustedLockers = (); 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 a31e664d821..e7b602df733 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 @@ -142,7 +142,7 @@ impl pallet_xcm::Config for Runtime { type UniversalLocation = UniversalLocation; // No version discovery needed const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0; - type AdvertisedXcmVersion = frame::traits::ConstU32<3>; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type AdminOrigin = frame_system::EnsureRoot; // No locking type TrustedLockers = (); diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 254bc2c7b83..9b8f735b478 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -2807,6 +2807,44 @@ impl Pallet { /// set. #[cfg(any(feature = "try-runtime", test))] pub fn do_try_state() -> Result<(), TryRuntimeError> { + use migration::data::NeedsMigration; + + // Take the minimum version between `SafeXcmVersion` and `latest - 1` and ensure that the + // operational data is stored at least at that version, for example, to prevent issues when + // removing older XCM versions. + let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::::get() + { + XCM_VERSION.saturating_sub(1).min(safe_xcm_version) + } else { + XCM_VERSION.saturating_sub(1) + }; + + // check `Queries` + ensure!( + !Queries::::iter_values() + .any(|data| data.needs_migration(minimal_allowed_xcm_version)), + TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!") + ); + + // check `LockedFungibles` + ensure!( + !LockedFungibles::::iter_values() + .any(|data| data.needs_migration(minimal_allowed_xcm_version)), + TryRuntimeError::Other( + "`LockedFungibles` data should be migrated to the higher xcm version!" + ) + ); + + // check `RemoteLockedFungibles` + ensure!( + !RemoteLockedFungibles::::iter() + .any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version) || + data.needs_migration(minimal_allowed_xcm_version)), + TryRuntimeError::Other( + "`RemoteLockedFungibles` data should be migrated to the higher xcm version!" + ) + ); + // if migration has been already scheduled, everything is ok and data will be eventually // migrated if CurrentMigration::::exists() { @@ -2887,7 +2925,7 @@ impl xcm_executor::traits::Enact for UnlockTicket { let mut maybe_remove_index = None; let mut locked = BalanceOf::::zero(); let mut found = false; - // We could just as well do with with an into_iter, filter_map and collect, however this way + // We could just as well do with an into_iter, filter_map and collect, however this way // avoids making an allocation. for (i, x) in locks.iter_mut().enumerate() { if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) { @@ -3268,7 +3306,7 @@ impl OnResponse for Pallet { }); return Weight::zero() } - return match maybe_notify { + match maybe_notify { Some((pallet_index, call_index)) => { // This is a bit horrible, but we happen to know that the `Call` will // be built by `(pallet_index: u8, call_index: u8, QueryId, Response)`. diff --git a/polkadot/xcm/pallet-xcm/src/migration.rs b/polkadot/xcm/pallet-xcm/src/migration.rs index 2c5b2620f53..80154f57ddf 100644 --- a/polkadot/xcm/pallet-xcm/src/migration.rs +++ b/polkadot/xcm/pallet-xcm/src/migration.rs @@ -15,7 +15,8 @@ // along with Polkadot. If not, see . use crate::{ - pallet::CurrentMigration, Config, Pallet, VersionMigrationStage, VersionNotifyTargets, + pallet::CurrentMigration, Config, CurrentXcmVersion, Pallet, VersionMigrationStage, + VersionNotifyTargets, }; use frame_support::{ pallet_prelude::*, @@ -25,6 +26,307 @@ use frame_support::{ const DEFAULT_PROOF_SIZE: u64 = 64 * 1024; +/// Utilities for handling XCM version migration for the relevant data. +pub mod data { + use crate::*; + + /// A trait for handling XCM versioned data migration for the requested `XcmVersion`. + pub(crate) trait NeedsMigration { + type MigratedData; + + /// Returns true if data does not match `minimal_allowed_xcm_version`. + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool; + + /// Attempts to migrate data. `Ok(None)` means no migration is needed. + /// `Ok(Some(Self::MigratedData))` should contain the migrated data. + fn try_migrate(self, to_xcm_version: XcmVersion) -> Result, ()>; + } + + /// Implementation of `NeedsMigration` for `LockedFungibles` data. + impl NeedsMigration for BoundedVec<(B, VersionedLocation), M> { + type MigratedData = Self; + + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool { + self.iter() + .any(|(_, unlocker)| unlocker.identify_version() < minimal_allowed_xcm_version) + } + + fn try_migrate( + mut self, + to_xcm_version: XcmVersion, + ) -> Result, ()> { + let mut was_modified = false; + for locked in self.iter_mut() { + if locked.1.identify_version() < to_xcm_version { + let Ok(new_unlocker) = locked.1.clone().into_version(to_xcm_version) else { + return Err(()) + }; + locked.1 = new_unlocker; + was_modified = true; + } + } + + if was_modified { + Ok(Some(self)) + } else { + Ok(None) + } + } + } + + /// Implementation of `NeedsMigration` for `Queries` data. + impl NeedsMigration for QueryStatus { + type MigratedData = Self; + + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool { + match &self { + QueryStatus::Pending { responder, maybe_match_querier, .. } => + responder.identify_version() < minimal_allowed_xcm_version || + maybe_match_querier + .as_ref() + .map(|v| v.identify_version() < minimal_allowed_xcm_version) + .unwrap_or(false), + QueryStatus::VersionNotifier { origin, .. } => + origin.identify_version() < minimal_allowed_xcm_version, + QueryStatus::Ready { response, .. } => + response.identify_version() < minimal_allowed_xcm_version, + } + } + + fn try_migrate(self, to_xcm_version: XcmVersion) -> Result, ()> { + if !self.needs_migration(to_xcm_version) { + return Ok(None) + } + + // do migration + match self { + QueryStatus::Pending { responder, maybe_match_querier, maybe_notify, timeout } => { + let Ok(responder) = responder.into_version(to_xcm_version) else { + return Err(()) + }; + let Ok(maybe_match_querier) = + maybe_match_querier.map(|mmq| mmq.into_version(to_xcm_version)).transpose() + else { + return Err(()) + }; + Ok(Some(QueryStatus::Pending { + responder, + maybe_match_querier, + maybe_notify, + timeout, + })) + }, + QueryStatus::VersionNotifier { origin, is_active } => origin + .into_version(to_xcm_version) + .map(|origin| Some(QueryStatus::VersionNotifier { origin, is_active })), + QueryStatus::Ready { response, at } => response + .into_version(to_xcm_version) + .map(|response| Some(QueryStatus::Ready { response, at })), + } + } + } + + /// Implementation of `NeedsMigration` for `RemoteLockedFungibles` key type. + impl NeedsMigration for (XcmVersion, A, VersionedAssetId) { + type MigratedData = Self; + + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool { + self.0 < minimal_allowed_xcm_version || + self.2.identify_version() < minimal_allowed_xcm_version + } + + fn try_migrate(self, to_xcm_version: XcmVersion) -> Result, ()> { + if !self.needs_migration(to_xcm_version) { + return Ok(None) + } + + let Ok(asset_id) = self.2.into_version(to_xcm_version) else { return Err(()) }; + Ok(Some((to_xcm_version, self.1, asset_id))) + } + } + + /// Implementation of `NeedsMigration` for `RemoteLockedFungibles` data. + impl> NeedsMigration + for RemoteLockedFungibleRecord + { + type MigratedData = Self; + + fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool { + self.owner.identify_version() < minimal_allowed_xcm_version || + self.locker.identify_version() < minimal_allowed_xcm_version + } + + fn try_migrate(self, to_xcm_version: XcmVersion) -> Result, ()> { + if !self.needs_migration(to_xcm_version) { + return Ok(None) + } + + let RemoteLockedFungibleRecord { amount, owner, locker, consumers } = self; + + let Ok(owner) = owner.into_version(to_xcm_version) else { return Err(()) }; + let Ok(locker) = locker.into_version(to_xcm_version) else { return Err(()) }; + + Ok(Some(RemoteLockedFungibleRecord { amount, owner, locker, consumers })) + } + } + + impl Pallet { + /// Migrates relevant data to the `required_xcm_version`. + pub(crate) fn migrate_data_to_xcm_version( + weight: &mut Weight, + required_xcm_version: XcmVersion, + ) { + const LOG_TARGET: &str = "runtime::xcm::pallet_xcm::migrate_data_to_xcm_version"; + + // check and migrate `Queries` + let queries_to_migrate = Queries::::iter().filter_map(|(id, data)| { + weight.saturating_add(T::DbWeight::get().reads(1)); + match data.try_migrate(required_xcm_version) { + Ok(Some(new_data)) => Some((id, new_data)), + Ok(None) => None, + Err(_) => { + tracing::error!( + target: LOG_TARGET, + ?id, + ?required_xcm_version, + "`Queries` cannot be migrated!" + ); + None + }, + } + }); + for (id, new_data) in queries_to_migrate { + tracing::info!( + target: LOG_TARGET, + query_id = ?id, + ?new_data, + "Migrating `Queries`" + ); + Queries::::insert(id, new_data); + weight.saturating_add(T::DbWeight::get().writes(1)); + } + + // check and migrate `LockedFungibles` + let locked_fungibles_to_migrate = + LockedFungibles::::iter().filter_map(|(id, data)| { + weight.saturating_add(T::DbWeight::get().reads(1)); + match data.try_migrate(required_xcm_version) { + Ok(Some(new_data)) => Some((id, new_data)), + Ok(None) => None, + Err(_) => { + tracing::error!( + target: LOG_TARGET, + ?id, + ?required_xcm_version, + "`LockedFungibles` cannot be migrated!" + ); + None + }, + } + }); + for (id, new_data) in locked_fungibles_to_migrate { + tracing::info!( + target: LOG_TARGET, + account_id = ?id, + ?new_data, + "Migrating `LockedFungibles`" + ); + LockedFungibles::::insert(id, new_data); + weight.saturating_add(T::DbWeight::get().writes(1)); + } + + // check and migrate `RemoteLockedFungibles` - 1. step - just data + let remote_locked_fungibles_to_migrate = + RemoteLockedFungibles::::iter().filter_map(|(id, data)| { + weight.saturating_add(T::DbWeight::get().reads(1)); + match data.try_migrate(required_xcm_version) { + Ok(Some(new_data)) => Some((id, new_data)), + Ok(None) => None, + Err(_) => { + tracing::error!( + target: LOG_TARGET, + ?id, + ?required_xcm_version, + "`RemoteLockedFungibles` data cannot be migrated!" + ); + None + }, + } + }); + for (id, new_data) in remote_locked_fungibles_to_migrate { + tracing::info!( + target: LOG_TARGET, + key = ?id, + amount = ?new_data.amount, + locker = ?new_data.locker, + owner = ?new_data.owner, + consumers_count = ?new_data.consumers.len(), + "Migrating `RemoteLockedFungibles` data" + ); + RemoteLockedFungibles::::insert(id, new_data); + weight.saturating_add(T::DbWeight::get().writes(1)); + } + + // check and migrate `RemoteLockedFungibles` - 2. step - key + let remote_locked_fungibles_keys_to_migrate = RemoteLockedFungibles::::iter_keys() + .filter_map(|key| { + if key.needs_migration(required_xcm_version) { + let old_key = key.clone(); + match key.try_migrate(required_xcm_version) { + Ok(Some(new_key)) => Some((old_key, new_key)), + Ok(None) => None, + Err(_) => { + tracing::error!( + target: LOG_TARGET, + id = ?old_key, + ?required_xcm_version, + "`RemoteLockedFungibles` key cannot be migrated!" + ); + None + }, + } + } else { + None + } + }); + for (old_key, new_key) in remote_locked_fungibles_keys_to_migrate { + weight.saturating_add(T::DbWeight::get().reads(1)); + // make sure, that we don't override accidentally other data + if RemoteLockedFungibles::::get(&new_key).is_some() { + tracing::error!( + target: LOG_TARGET, + ?old_key, + ?new_key, + "`RemoteLockedFungibles` already contains data for a `new_key`!" + ); + // let's just skip for now, could be potentially caused with missing this + // migration before (manual clean-up?). + continue; + } + + tracing::info!( + target: LOG_TARGET, + ?old_key, + ?new_key, + "Migrating `RemoteLockedFungibles` key" + ); + + // now we can swap the keys + RemoteLockedFungibles::::swap::< + ( + NMapKey, + NMapKey, + NMapKey, + ), + _, + _, + >(&old_key, &new_key); + weight.saturating_add(T::DbWeight::get().writes(1)); + } + } + } +} + pub mod v1 { use super::*; use crate::{CurrentMigration, VersionMigrationStage}; @@ -84,7 +386,80 @@ pub mod v1 { pub struct MigrateToLatestXcmVersion(core::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToLatestXcmVersion { fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + // trigger expensive/lazy migration (kind of multi-block) CurrentMigration::::put(VersionMigrationStage::default()); - T::DbWeight::get().writes(1) + weight.saturating_accrue(T::DbWeight::get().writes(1)); + + // migrate other operational data to the latest XCM version in-place + let latest = CurrentXcmVersion::get(); + Pallet::::migrate_data_to_xcm_version(&mut weight, latest); + + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: alloc::vec::Vec) -> Result<(), sp_runtime::TryRuntimeError> { + use data::NeedsMigration; + const LOG_TARGET: &str = "runtime::xcm::pallet_xcm::migrate_to_latest"; + + let latest = CurrentXcmVersion::get(); + + let number_of_queries_to_migrate = crate::Queries::::iter() + .filter(|(id, data)| { + let needs_migration = data.needs_migration(latest); + if needs_migration { + tracing::warn!( + target: LOG_TARGET, + query_id = ?id, + query = ?data, + "Query was not migrated!" + ) + } + needs_migration + }) + .count(); + + let number_of_locked_fungibles_to_migrate = crate::LockedFungibles::::iter() + .filter_map(|(id, data)| { + if data.needs_migration(latest) { + tracing::warn!( + target: LOG_TARGET, + ?id, + ?data, + "LockedFungibles item was not migrated!" + ); + Some(true) + } else { + None + } + }) + .count(); + + let number_of_remote_locked_fungibles_to_migrate = + crate::RemoteLockedFungibles::::iter() + .filter_map(|(key, data)| { + if key.needs_migration(latest) || data.needs_migration(latest) { + tracing::warn!( + target: LOG_TARGET, + ?key, + "RemoteLockedFungibles item was not migrated!" + ); + Some(true) + } else { + None + } + }) + .count(); + + ensure!(number_of_queries_to_migrate == 0, "must migrate all `Queries`."); + ensure!(number_of_locked_fungibles_to_migrate == 0, "must migrate all `LockedFungibles`."); + ensure!( + number_of_remote_locked_fungibles_to_migrate == 0, + "must migrate all `RemoteLockedFungibles`." + ); + + Ok(()) } } diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index c16c1a1ba98..e98a8f8d2ce 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -19,11 +19,15 @@ pub(crate) mod assets_transfer; use crate::{ - mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error, - ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus, - RecordedXcm, ShouldRecordXcm, VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, + migration::data::NeedsMigration, + mock::*, + pallet::{LockedFungibles, RemoteLockedFungibles, SupportedVersion}, + AssetTraps, Config, CurrentMigration, Error, ExecuteControllerWeightInfo, + LatestVersionedLocation, Pallet, Queries, QueryStatus, RecordedXcm, RemoteLockedFungibleRecord, + ShouldRecordXcm, VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, WeightInfo, }; +use bounded_collections::BoundedVec; use frame_support::{ assert_err_ignore_postinfo, assert_noop, assert_ok, traits::{Currency, Hooks}, @@ -1258,6 +1262,168 @@ fn multistage_migration_works() { }) } +#[test] +fn migrate_data_to_xcm_version_works() { + new_test_ext_with_balances(vec![]).execute_with(|| { + // check `try-state` + assert!(Pallet::::do_try_state().is_ok()); + + let latest_version = XCM_VERSION; + let previous_version = XCM_VERSION - 1; + + // `Queries` migration + { + let origin = VersionedLocation::from(Location::parent()); + let query_id1 = 0; + let query_id2 = 2; + let query_as_latest = + QueryStatus::VersionNotifier { origin: origin.clone(), is_active: true }; + let query_as_previous = QueryStatus::VersionNotifier { + origin: origin.into_version(previous_version).unwrap(), + is_active: true, + }; + assert_ne!(query_as_latest, query_as_previous); + assert!(!query_as_latest.needs_migration(latest_version)); + assert!(!query_as_latest.needs_migration(previous_version)); + assert!(query_as_previous.needs_migration(latest_version)); + assert!(!query_as_previous.needs_migration(previous_version)); + + // store two queries: migrated and not migrated + Queries::::insert(query_id1, query_as_latest.clone()); + Queries::::insert(query_id2, query_as_previous); + assert!(Pallet::::do_try_state().is_ok()); + + // trigger migration + Pallet::::migrate_data_to_xcm_version(&mut Weight::zero(), latest_version); + + // no change for query_id1 + assert_eq!(Queries::::get(query_id1), Some(query_as_latest.clone())); + // change for query_id2 + assert_eq!(Queries::::get(query_id2), Some(query_as_latest)); + assert!(Pallet::::do_try_state().is_ok()); + } + + // `LockedFungibles` migration + { + let account1 = AccountId::new([13u8; 32]); + let account2 = AccountId::new([58u8; 32]); + let unlocker = VersionedLocation::from(Location::parent()); + let lockeds_as_latest = BoundedVec::truncate_from(vec![(0, unlocker.clone())]); + let lockeds_as_previous = BoundedVec::truncate_from(vec![( + 0, + unlocker.into_version(previous_version).unwrap(), + )]); + assert_ne!(lockeds_as_latest, lockeds_as_previous); + assert!(!lockeds_as_latest.needs_migration(latest_version)); + assert!(!lockeds_as_latest.needs_migration(previous_version)); + assert!(lockeds_as_previous.needs_migration(latest_version)); + assert!(!lockeds_as_previous.needs_migration(previous_version)); + + // store two lockeds: migrated and not migrated + LockedFungibles::::insert(&account1, lockeds_as_latest.clone()); + LockedFungibles::::insert(&account2, lockeds_as_previous); + assert!(Pallet::::do_try_state().is_ok()); + + // trigger migration + Pallet::::migrate_data_to_xcm_version(&mut Weight::zero(), latest_version); + + // no change for account1 + assert_eq!(LockedFungibles::::get(&account1), Some(lockeds_as_latest.clone())); + // change for account2 + assert_eq!(LockedFungibles::::get(&account2), Some(lockeds_as_latest)); + assert!(Pallet::::do_try_state().is_ok()); + } + + // `RemoteLockedFungibles` migration + { + let account1 = AccountId::new([13u8; 32]); + let account2 = AccountId::new([58u8; 32]); + let account3 = AccountId::new([97u8; 32]); + let asset_id = VersionedAssetId::from(AssetId(Location::parent())); + let owner = VersionedLocation::from(Location::parent()); + let locker = VersionedLocation::from(Location::parent()); + let key1_as_latest = (latest_version, account1, asset_id.clone()); + let key2_as_latest = (latest_version, account2, asset_id.clone()); + let key3_as_previous = ( + previous_version, + account3.clone(), + asset_id.clone().into_version(previous_version).unwrap(), + ); + let expected_key3_as_latest = (latest_version, account3, asset_id); + let data_as_latest = RemoteLockedFungibleRecord { + amount: Default::default(), + owner: owner.clone(), + locker: locker.clone(), + consumers: Default::default(), + }; + let data_as_previous = RemoteLockedFungibleRecord { + amount: Default::default(), + owner: owner.into_version(previous_version).unwrap(), + locker: locker.into_version(previous_version).unwrap(), + consumers: Default::default(), + }; + assert_ne!(data_as_latest.owner, data_as_previous.owner); + assert_ne!(data_as_latest.locker, data_as_previous.locker); + assert!(!key1_as_latest.needs_migration(latest_version)); + assert!(!key1_as_latest.needs_migration(previous_version)); + assert!(!key2_as_latest.needs_migration(latest_version)); + assert!(!key2_as_latest.needs_migration(previous_version)); + assert!(key3_as_previous.needs_migration(latest_version)); + assert!(!key3_as_previous.needs_migration(previous_version)); + assert!(!expected_key3_as_latest.needs_migration(latest_version)); + assert!(!expected_key3_as_latest.needs_migration(previous_version)); + assert!(!data_as_latest.needs_migration(latest_version)); + assert!(!data_as_latest.needs_migration(previous_version)); + assert!(data_as_previous.needs_migration(latest_version)); + assert!(!data_as_previous.needs_migration(previous_version)); + + // store three lockeds: + // fully migrated + RemoteLockedFungibles::::insert(&key1_as_latest, data_as_latest.clone()); + // only key migrated + RemoteLockedFungibles::::insert(&key2_as_latest, data_as_previous.clone()); + // neither key nor data migrated + RemoteLockedFungibles::::insert(&key3_as_previous, data_as_previous); + assert!(Pallet::::do_try_state().is_ok()); + + // trigger migration + Pallet::::migrate_data_to_xcm_version(&mut Weight::zero(), latest_version); + + let assert_locked_eq = + |left: Option>, + right: Option>| { + match (left, right) { + (None, Some(_)) | (Some(_), None) => + assert!(false, "Received unexpected message"), + (None, None) => (), + (Some(l), Some(r)) => { + assert_eq!(l.owner, r.owner); + assert_eq!(l.locker, r.locker); + }, + } + }; + + // no change + assert_locked_eq( + RemoteLockedFungibles::::get(&key1_as_latest), + Some(data_as_latest.clone()), + ); + // change - data migrated + assert_locked_eq( + RemoteLockedFungibles::::get(&key2_as_latest), + Some(data_as_latest.clone()), + ); + // fully migrated + assert_locked_eq(RemoteLockedFungibles::::get(&key3_as_previous), None); + assert_locked_eq( + RemoteLockedFungibles::::get(&expected_key3_as_latest), + Some(data_as_latest.clone()), + ); + assert!(Pallet::::do_try_state().is_ok()); + } + }) +} + #[test] fn record_xcm_works() { let balances = vec![(ALICE, INITIAL_BALANCE)]; diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs index 4d9809e84f8..e6fe8e45c26 100644 --- a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs @@ -312,7 +312,7 @@ impl pallet_xcm::Config for Runtime { type UniversalLocation = UniversalLocation; // No version discovery needed const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 0; - type AdvertisedXcmVersion = frame_support::traits::ConstU32<3>; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type AdminOrigin = frame_system::EnsureRoot; // No locking type TrustedLockers = (); diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index d76ff21b859..26ea226313f 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -126,7 +126,6 @@ parameter_types! { pub const AnyNetwork: Option = None; pub UniversalLocation: InteriorLocation = (ByGenesis([0; 32]), Parachain(42)).into(); pub UnitWeightCost: u64 = 1_000; - pub static AdvertisedXcmVersion: u32 = 3; pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000); pub CurrencyPerSecondPerByte: (AssetId, u128, u128) = (AssetId(RelayLocation::get()), 1, 1); pub TrustedAssets: (AssetFilter, Location) = (All.into(), Here.into()); @@ -267,7 +266,7 @@ impl pallet_xcm::Config for Test { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = AdvertisedXcmVersion; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type TrustedLockers = (); type SovereignAccountOf = SovereignAccountOf; type Currency = Balances; diff --git a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs index b3afa23503e..4a5de887500 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs @@ -145,7 +145,6 @@ parameter_types! { 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 static AdvertisedXcmVersion: XcmVersion = 4; pub const HereLocation: Location = Location::here(); pub const RelayLocation: Location = Location::parent(); pub const MaxAssetsIntoHolding: u32 = 64; @@ -349,7 +348,7 @@ impl pallet_xcm::Config for TestRuntime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = AdvertisedXcmVersion; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type AdminOrigin = EnsureRoot; type TrustedLockers = (); type SovereignAccountOf = (); diff --git a/prdoc/pr_6148.prdoc b/prdoc/pr_6148.prdoc new file mode 100644 index 00000000000..430a58dfefb --- /dev/null +++ b/prdoc/pr_6148.prdoc @@ -0,0 +1,17 @@ +title: Fix migrations for pallet-xcm +doc: +- audience: Runtime Dev + description: |- + `pallet-xcm` stores some operational data that uses `Versioned*` XCM types. When we add a new XCM version (XV), we deprecate XV-2 and remove XV-3. + This PR extends the existing `MigrateToLatestXcmVersion` to include migration for the `Queries`, `LockedFungibles`, and `RemoteLockedFungibles` storage types. + Additionally, more checks were added to `try_state` for these types. + +crates: +- name: westend-runtime + bump: patch +- name: staging-xcm-builder + bump: none +- name: xcm-runtime-apis + bump: none +- name: pallet-xcm + bump: patch -- GitLab From a072ce81d2118a67e6ffbc417ae0815d0a56185f Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 25 Oct 2024 17:40:42 +0300 Subject: [PATCH 48/50] asset-hubs: simplify xcm-config (#6222) `ForeignCreatorsSovereignAccountOf` is used by `ForeignCreators` filter to convert location to `AccountId`, _after_ `ForeignCreators::IsForeign` filter passes for an (asset, location) pair. The `IsForeign` filter is the actual differentiator, so if a location passes it, we should support converting it to an `AccountId`. As such, this commit replaces `ForeignCreatorsSovereignAccountOf` converter with the more general `LocationToAccountId` converter. Signed-off-by: Adrian Catangiu --- .../runtimes/assets/asset-hub-rococo/src/lib.rs | 4 ++-- .../runtimes/assets/asset-hub-rococo/src/xcm_config.rs | 8 -------- .../runtimes/assets/asset-hub-rococo/tests/tests.rs | 8 ++++---- .../runtimes/assets/asset-hub-westend/src/lib.rs | 8 ++++---- .../runtimes/assets/asset-hub-westend/src/xcm_config.rs | 8 -------- .../runtimes/assets/asset-hub-westend/tests/tests.rs | 9 ++++----- 6 files changed, 14 insertions(+), 31 deletions(-) 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 f768f803aea..420fa67f41f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -82,7 +82,7 @@ use parachains_common::{ use sp_runtime::{Perbill, RuntimeDebug}; use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use xcm_config::{ - ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, GovernanceLocation, + ForeignAssetsConvertedConcreteId, GovernanceLocation, LocationToAccountId, PoolAssetsConvertedConcreteId, TokenLocation, TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, }; @@ -424,7 +424,7 @@ impl pallet_assets::Config for Runtime { FromNetwork, xcm_config::bridging::to_westend::WestendOrEthereumAssetFromAssetHubWestend, ), - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, AccountId, xcm::v4::Location, >; 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 32fbfb6d019..637c5900f7d 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 @@ -512,14 +512,6 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } -pub type ForeignCreatorsSovereignAccountOf = ( - SiblingParachainConvertsVia, - AccountId32Aliases, - ParentIsPreset, - GlobalConsensusEthereumConvertsFor, - GlobalConsensusParachainConvertsFor, -); - /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] 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 6e10f916899..81432313532 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -21,8 +21,8 @@ use asset_hub_rococo_runtime::{ xcm_config, xcm_config::{ bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - LocationToAccountId, StakingPot, TokenLocation, TrustBackedAssetsPalletLocation, XcmConfig, + ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, LocationToAccountId, StakingPot, + TokenLocation, TrustBackedAssetsPalletLocation, XcmConfig, }, AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, @@ -941,7 +941,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!( CheckingAccount, WeightToFee, ParachainSystem, - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, ForeignAssetsInstance, collator_session_keys(), slot_durations(), @@ -1015,7 +1015,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p Runtime, XcmConfig, WeightToFee, - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, ForeignAssetsInstance, Location, WithLatestLocationConverter, 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 adfa2b74df6..066a1c2bdfd 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -79,9 +79,9 @@ use testnet_parachains_constants::westend::{ consensus::*, currency::*, fee::WeightToFee, snowbridge::EthereumNetwork, time::*, }; use xcm_config::{ - ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, - PoolAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, - TrustBackedAssetsPalletLocation, WestendLocation, XcmOriginToTransactDispatchOrigin, + ForeignAssetsConvertedConcreteId, LocationToAccountId, PoolAssetsConvertedConcreteId, + TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, WestendLocation, + XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -423,7 +423,7 @@ impl pallet_assets::Config for Runtime { FromNetwork, xcm_config::bridging::to_rococo::RococoAssetFromAssetHubRococo, ), - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, AccountId, xcm::v4::Location, >; 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 cfd9fd2fd46..b554d00508b 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 @@ -535,14 +535,6 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } -pub type ForeignCreatorsSovereignAccountOf = ( - SiblingParachainConvertsVia, - AccountId32Aliases, - ParentIsPreset, - GlobalConsensusEthereumConvertsFor, - GlobalConsensusParachainConvertsFor, -); - /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] 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 ff84bdea69f..6a48b19ce94 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -21,9 +21,8 @@ use asset_hub_westend_runtime::{ xcm_config, xcm_config::{ bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - LocationToAccountId, StakingPot, TrustBackedAssetsPalletLocation, WestendLocation, - XcmConfig, + ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, LocationToAccountId, StakingPot, + TrustBackedAssetsPalletLocation, WestendLocation, XcmConfig, }, AllPalletsWithoutSystem, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, @@ -966,7 +965,7 @@ asset_test_utils::include_teleports_for_foreign_assets_works!( CheckingAccount, WeightToFee, ParachainSystem, - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, ForeignAssetsInstance, collator_session_keys(), slot_durations(), @@ -1044,7 +1043,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p Runtime, XcmConfig, WeightToFee, - ForeignCreatorsSovereignAccountOf, + LocationToAccountId, ForeignAssetsInstance, xcm::v4::Location, WithLatestLocationConverter, -- GitLab From 4c618a83d33281fe96f0e2b68a111ed227af22c0 Mon Sep 17 00:00:00 2001 From: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:21:29 +0300 Subject: [PATCH 49/50] Switch node side to v2 candidate receipts (#5679) on top of https://github.com/paritytech/polkadot-sdk/pull/5423 This PR implements the plumbing work required for https://github.com/paritytech/polkadot-sdk/issues/5047 . I also added additional helper methods gated by feature "test" in primitives. TODO: - [x] PRDoc --------- Signed-off-by: Andrei Sandu Co-authored-by: GitHub Action --- cumulus/client/consensus/common/src/tests.rs | 2 +- cumulus/client/network/src/lib.rs | 12 +- cumulus/client/network/src/tests.rs | 22 ++- cumulus/client/pov-recovery/src/lib.rs | 6 +- cumulus/client/pov-recovery/src/tests.rs | 5 +- .../src/lib.rs | 5 +- .../client/relay-chain-interface/src/lib.rs | 6 +- .../src/blockchain_rpc_client.rs | 28 ++- .../relay-chain-rpc-interface/src/lib.rs | 5 +- .../src/rpc_client.rs | 19 +- polkadot/node/collation-generation/Cargo.toml | 1 + polkadot/node/collation-generation/src/lib.rs | 10 +- .../node/collation-generation/src/tests.rs | 84 ++++----- polkadot/node/core/approval-voting/Cargo.toml | 1 + .../approval-voting/src/approval_checking.rs | 10 +- .../approval-voting/src/approval_db/v1/mod.rs | 4 +- .../approval-voting/src/approval_db/v2/mod.rs | 4 +- .../src/approval_db/v2/tests.rs | 14 +- .../approval-voting/src/approval_db/v3/mod.rs | 4 +- .../src/approval_db/v3/tests.rs | 17 +- .../node/core/approval-voting/src/import.rs | 41 +++-- polkadot/node/core/approval-voting/src/lib.rs | 25 +-- polkadot/node/core/approval-voting/src/ops.rs | 4 +- .../approval-voting/src/persisted_entries.rs | 4 +- .../node/core/approval-voting/src/tests.rs | 107 +++++------ polkadot/node/core/av-store/src/lib.rs | 4 +- polkadot/node/core/av-store/src/tests.rs | 4 +- polkadot/node/core/backing/Cargo.toml | 1 + polkadot/node/core/backing/src/error.rs | 2 +- polkadot/node/core/backing/src/lib.rs | 46 ++--- polkadot/node/core/backing/src/tests/mod.rs | 35 ++-- .../src/tests/prospective_parachains.rs | 22 +-- .../node/core/bitfield-signing/src/lib.rs | 2 +- .../node/core/bitfield-signing/src/tests.rs | 6 +- .../node/core/candidate-validation/src/lib.rs | 17 +- .../core/candidate-validation/src/tests.rs | 51 ++++-- .../node/core/dispute-coordinator/Cargo.toml | 1 + .../core/dispute-coordinator/src/db/v1.rs | 25 +-- .../core/dispute-coordinator/src/import.rs | 6 +- .../dispute-coordinator/src/initialized.rs | 15 +- .../node/core/dispute-coordinator/src/lib.rs | 2 +- .../src/participation/mod.rs | 9 +- .../src/participation/queues/mod.rs | 5 +- .../src/participation/queues/tests.rs | 4 +- .../src/participation/tests.rs | 3 +- .../dispute-coordinator/src/scraping/mod.rs | 5 +- .../dispute-coordinator/src/scraping/tests.rs | 8 +- .../core/dispute-coordinator/src/tests.rs | 30 +-- .../node/core/parachains-inherent/src/lib.rs | 2 +- .../core/prospective-parachains/Cargo.toml | 1 + .../src/fragment_chain/mod.rs | 20 +- .../src/fragment_chain/tests.rs | 20 +- .../core/prospective-parachains/src/lib.rs | 13 +- .../core/prospective-parachains/src/tests.rs | 25 +-- polkadot/node/core/provisioner/Cargo.toml | 2 + .../disputes/prioritized_selection/tests.rs | 14 +- polkadot/node/core/provisioner/src/lib.rs | 20 +- polkadot/node/core/provisioner/src/tests.rs | 58 +++--- polkadot/node/core/runtime-api/src/cache.rs | 22 ++- polkadot/node/core/runtime-api/src/tests.rs | 22 ++- polkadot/node/malus/src/variants/common.rs | 10 +- .../variants/dispute_finalized_candidates.rs | 2 +- .../src/variants/suggest_garbage_candidate.rs | 13 +- .../src/requester/fetch_task/mod.rs | 8 +- .../src/requester/mod.rs | 2 +- .../src/requester/tests.rs | 2 +- .../src/tests/mock.rs | 8 +- .../src/tests/mod.rs | 2 +- .../src/tests/state.rs | 2 +- .../network/availability-recovery/src/lib.rs | 8 +- .../availability-recovery/src/tests.rs | 34 ++-- .../src/collator_side/collation.rs | 4 +- .../src/collator_side/mod.rs | 13 +- .../src/validator_side/collation.rs | 13 +- .../src/validator_side/mod.rs | 26 +-- .../src/validator_side/tests/mod.rs | 44 +++-- .../tests/prospective_parachains.rs | 20 +- .../src/receiver/batches/batch.rs | 2 +- .../src/receiver/batches/mod.rs | 2 +- .../dispute-distribution/src/receiver/mod.rs | 2 +- .../src/sender/send_task.rs | 2 +- .../dispute-distribution/src/tests/mock.rs | 8 +- .../dispute-distribution/src/tests/mod.rs | 4 +- .../protocol/src/request_response/v1.rs | 7 +- .../protocol/src/request_response/v2.rs | 4 +- .../network/statement-distribution/Cargo.toml | 1 + .../src/legacy_v1/mod.rs | 9 +- .../src/legacy_v1/requester.rs | 4 +- .../src/legacy_v1/responder.rs | 4 +- .../src/legacy_v1/tests.rs | 41 +++-- .../src/v2/candidates.rs | 12 +- .../statement-distribution/src/v2/mod.rs | 16 +- .../statement-distribution/src/v2/requests.rs | 22 ++- .../src/v2/tests/mod.rs | 6 +- .../node/overseer/examples/minimal-example.rs | 6 +- polkadot/node/overseer/src/tests.rs | 19 +- .../node/primitives/src/disputes/message.rs | 3 +- polkadot/node/primitives/src/disputes/mod.rs | 6 +- polkadot/node/primitives/src/lib.rs | 8 +- .../node/service/src/parachains_db/upgrade.rs | 6 +- polkadot/node/subsystem-bench/Cargo.toml | 2 +- .../src/lib/approval/helpers.rs | 6 +- .../src/lib/approval/message_generator.rs | 4 +- .../subsystem-bench/src/lib/approval/mod.rs | 5 +- .../src/lib/availability/mod.rs | 2 +- .../src/lib/availability/test_state.rs | 14 +- .../src/lib/mock/runtime_api.rs | 7 +- .../src/lib/statement/test_state.rs | 21 ++- polkadot/node/subsystem-types/src/messages.rs | 37 ++-- .../subsystem-types/src/runtime_client.rs | 56 ++---- .../src/inclusion_emulator/mod.rs | 4 +- polkadot/node/subsystem-util/src/lib.rs | 15 +- .../node/subsystem-util/src/runtime/mod.rs | 12 +- .../test-parachains/adder/collator/src/lib.rs | 2 +- .../undying/collator/src/lib.rs | 2 +- polkadot/primitives/src/v8/mod.rs | 2 +- polkadot/primitives/src/vstaging/mod.rs | 173 ++++++++++++++++-- polkadot/primitives/test-helpers/src/lib.rs | 36 +++- .../runtime/parachains/src/inclusion/tests.rs | 2 +- .../parachains/src/paras_inherent/tests.rs | 4 +- polkadot/statement-table/src/lib.rs | 4 +- prdoc/pr_5679.prdoc | 80 ++++++++ 122 files changed, 1136 insertions(+), 721 deletions(-) create mode 100644 prdoc/pr_5679.prdoc diff --git a/cumulus/client/consensus/common/src/tests.rs b/cumulus/client/consensus/common/src/tests.rs index 94e2304011b..79e620db3bf 100644 --- a/cumulus/client/consensus/common/src/tests.rs +++ b/cumulus/client/consensus/common/src/tests.rs @@ -20,7 +20,7 @@ use async_trait::async_trait; use codec::Encode; use cumulus_client_pov_recovery::RecoveryKind; use cumulus_primitives_core::{ - relay_chain::{BlockId, BlockNumber, CoreState}, + relay_chain::{vstaging::CoreState, BlockId, BlockNumber}, CumulusDigestItem, InboundDownwardMessage, InboundHrmpMessage, }; use cumulus_relay_chain_interface::{ diff --git a/cumulus/client/network/src/lib.rs b/cumulus/client/network/src/lib.rs index 01ad15bed4d..3b9c0fc81ec 100644 --- a/cumulus/client/network/src/lib.rs +++ b/cumulus/client/network/src/lib.rs @@ -32,8 +32,8 @@ use polkadot_node_primitives::{CollationSecondedSignal, Statement}; use polkadot_node_subsystem::messages::RuntimeApiRequest; use polkadot_parachain_primitives::primitives::HeadData; use polkadot_primitives::{ - CandidateReceipt, CompactStatement, Hash as PHash, Id as ParaId, OccupiedCoreAssumption, - SigningContext, UncheckedSigned, + vstaging::CandidateReceiptV2 as CandidateReceipt, CompactStatement, Hash as PHash, + Id as ParaId, OccupiedCoreAssumption, SigningContext, UncheckedSigned, }; use codec::{Decode, DecodeAll, Encode}; @@ -79,7 +79,7 @@ impl Decode for BlockAnnounceData { let relay_parent = match PHash::decode(input) { Ok(p) => p, // For being backwards compatible, we support missing relay-chain parent. - Err(_) => receipt.descriptor.relay_parent, + Err(_) => receipt.descriptor.relay_parent(), }; Ok(Self { receipt, statement, relay_parent }) @@ -108,7 +108,7 @@ impl BlockAnnounceData { return Err(Validation::Failure { disconnect: true }) } - if HeadData(encoded_header).hash() != self.receipt.descriptor.para_head { + if HeadData(encoded_header).hash() != self.receipt.descriptor.para_head() { tracing::debug!( target: LOG_TARGET, "Receipt para head hash doesn't match the hash of the header in the block announcement", @@ -302,7 +302,7 @@ where } .map_err(|e| Box::new(BlockAnnounceError(format!("{:?}", e))) as Box<_>)?; - Ok(candidate_receipts.into_iter().map(|cr| cr.descriptor.para_head)) + Ok(candidate_receipts.into_iter().map(|cr| cr.descriptor.para_head())) } /// Handle a block announcement with empty data (no statement) attached to it. @@ -399,7 +399,7 @@ where return Ok(e) } - let relay_parent = block_announce_data.receipt.descriptor.relay_parent; + let relay_parent = block_announce_data.receipt.descriptor.relay_parent(); relay_chain_interface .wait_for_block(relay_parent) diff --git a/cumulus/client/network/src/tests.rs b/cumulus/client/network/src/tests.rs index 4b347364521..009f922008b 100644 --- a/cumulus/client/network/src/tests.rs +++ b/cumulus/client/network/src/tests.rs @@ -26,10 +26,11 @@ use futures::{executor::block_on, poll, task::Poll, FutureExt, Stream, StreamExt use parking_lot::Mutex; use polkadot_node_primitives::{SignedFullStatement, Statement}; use polkadot_primitives::{ + vstaging::{CommittedCandidateReceiptV2, CoreState}, BlockNumber, CandidateCommitments, CandidateDescriptor, CollatorPair, - CommittedCandidateReceipt, CoreState, Hash as PHash, HeadData, InboundDownwardMessage, - InboundHrmpMessage, OccupiedCoreAssumption, PersistedValidationData, SessionIndex, - SigningContext, ValidationCodeHash, ValidatorId, + CommittedCandidateReceipt, Hash as PHash, HeadData, InboundDownwardMessage, InboundHrmpMessage, + OccupiedCoreAssumption, PersistedValidationData, SessionIndex, SigningContext, + ValidationCodeHash, ValidatorId, }; use polkadot_test_client::{ Client as PClient, ClientBlockImportExt, DefaultTestClientBuilderExt, FullBackend as PBackend, @@ -166,7 +167,7 @@ impl RelayChainInterface for DummyRelayChainInterface { &self, _: PHash, _: ParaId, - ) -> RelayChainResult> { + ) -> RelayChainResult> { if self.data.lock().runtime_version >= RuntimeApiRequest::CANDIDATES_PENDING_AVAILABILITY_RUNTIME_REQUIREMENT { @@ -174,7 +175,7 @@ impl RelayChainInterface for DummyRelayChainInterface { } if self.data.lock().has_pending_availability { - Ok(Some(dummy_candidate())) + Ok(Some(dummy_candidate().into())) } else { Ok(None) } @@ -184,7 +185,7 @@ impl RelayChainInterface for DummyRelayChainInterface { &self, _: PHash, _: ParaId, - ) -> RelayChainResult> { + ) -> RelayChainResult> { if self.data.lock().runtime_version < RuntimeApiRequest::CANDIDATES_PENDING_AVAILABILITY_RUNTIME_REQUIREMENT { @@ -192,7 +193,7 @@ impl RelayChainInterface for DummyRelayChainInterface { } if self.data.lock().has_pending_availability { - Ok(vec![dummy_candidate()]) + Ok(vec![dummy_candidate().into()]) } else { Ok(vec![]) } @@ -412,7 +413,7 @@ async fn make_gossip_message_and_header( validation_code_hash: ValidationCodeHash::from(PHash::random()), }, }; - let statement = Statement::Seconded(candidate_receipt); + let statement = Statement::Seconded(candidate_receipt.into()); let signed = SignedFullStatement::sign( &keystore, statement, @@ -525,7 +526,7 @@ fn legacy_block_announce_data_handling() { let block_data = BlockAnnounceData::decode(&mut &data[..]).expect("Decoding works from legacy works"); - assert_eq!(receipt.descriptor.relay_parent, block_data.relay_parent); + assert_eq!(receipt.descriptor.relay_parent(), block_data.relay_parent); let data = block_data.encode(); LegacyBlockAnnounceData::decode(&mut &data[..]).expect("Decoding works"); @@ -600,7 +601,8 @@ async fn check_statement_seconded() { erasure_root: PHash::random(), signature: sp_core::sr25519::Signature::default().into(), validation_code_hash: ValidationCodeHash::from(PHash::random()), - }, + } + .into(), }, statement: signed_statement.convert_payload().into(), relay_parent, diff --git a/cumulus/client/pov-recovery/src/lib.rs b/cumulus/client/pov-recovery/src/lib.rs index 043cba12d19..87349aef0c9 100644 --- a/cumulus/client/pov-recovery/src/lib.rs +++ b/cumulus/client/pov-recovery/src/lib.rs @@ -56,7 +56,11 @@ use polkadot_node_primitives::{PoV, POV_BOMB_LIMIT}; use polkadot_node_subsystem::messages::{AvailabilityRecoveryMessage, RuntimeApiRequest}; use polkadot_overseer::Handle as OverseerHandle; use polkadot_primitives::{ - CandidateReceipt, CommittedCandidateReceipt, Id as ParaId, SessionIndex, + vstaging::{ + CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + }, + Id as ParaId, SessionIndex, }; use cumulus_primitives_core::ParachainBlockData; diff --git a/cumulus/client/pov-recovery/src/tests.rs b/cumulus/client/pov-recovery/src/tests.rs index 94dec32485c..d528a92a52a 100644 --- a/cumulus/client/pov-recovery/src/tests.rs +++ b/cumulus/client/pov-recovery/src/tests.rs @@ -18,7 +18,7 @@ use super::*; use assert_matches::assert_matches; use codec::{Decode, Encode}; use cumulus_primitives_core::relay_chain::{ - BlockId, CandidateCommitments, CandidateDescriptor, CoreIndex, CoreState, + vstaging::CoreState, BlockId, CandidateCommitments, CandidateDescriptor, CoreIndex, }; use cumulus_relay_chain_interface::{ InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, PHash, PHeader, @@ -532,7 +532,8 @@ fn make_candidate_chain(candidate_number_range: Range) -> Vec Result>, sp_api::ApiError> { + ) -> Result< + Vec>, + sp_api::ApiError, + > { Ok(self.rpc_client.parachain_host_availability_cores(at).await?) } @@ -212,8 +215,11 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn candidate_pending_availability( &self, at: Hash, - para_id: ParaId, - ) -> Result>, sp_api::ApiError> { + para_id: cumulus_primitives_core::ParaId, + ) -> Result< + Option>, + sp_api::ApiError, + > { Ok(self .rpc_client .parachain_host_candidate_pending_availability(at, para_id) @@ -223,7 +229,7 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn candidate_events( &self, at: Hash, - ) -> Result>, sp_api::ApiError> { + ) -> Result>, sp_api::ApiError> { Ok(self.rpc_client.parachain_host_candidate_events(at).await?) } @@ -266,7 +272,8 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn on_chain_votes( &self, at: Hash, - ) -> Result>, sp_api::ApiError> { + ) -> Result>, sp_api::ApiError> + { Ok(self.rpc_client.parachain_host_on_chain_votes(at).await?) } @@ -437,8 +444,11 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn candidates_pending_availability( &self, at: Hash, - para_id: ParaId, - ) -> Result>, sp_api::ApiError> { + para_id: cumulus_primitives_core::ParaId, + ) -> Result< + Vec>, + sp_api::ApiError, + > { Ok(self .rpc_client .parachain_host_candidates_pending_availability(at, para_id) diff --git a/cumulus/client/relay-chain-rpc-interface/src/lib.rs b/cumulus/client/relay-chain-rpc-interface/src/lib.rs index f53cdeffea9..0e2f6c054c4 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/lib.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/lib.rs @@ -18,8 +18,9 @@ use async_trait::async_trait; use core::time::Duration; use cumulus_primitives_core::{ relay_chain::{ - CommittedCandidateReceipt, Hash as RelayHash, Header as RelayHeader, InboundHrmpMessage, - OccupiedCoreAssumption, SessionIndex, ValidationCodeHash, ValidatorId, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, Hash as RelayHash, + Header as RelayHeader, InboundHrmpMessage, OccupiedCoreAssumption, SessionIndex, + ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; diff --git a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs index d8e5abaddc6..d7785d92c73 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs @@ -32,13 +32,18 @@ use codec::{Decode, Encode}; use cumulus_primitives_core::{ relay_chain::{ - async_backing::{AsyncBackingParams, BackingState}, - slashing, ApprovalVotingParams, BlockNumber, CandidateCommitments, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, - ExecutorParams, GroupRotationInfo, Hash as RelayHash, Header as RelayHeader, - InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + async_backing::AsyncBackingParams, + slashing, + vstaging::{ + async_backing::BackingState, CandidateEvent, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, BlockNumber, CandidateCommitments, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash as RelayHash, Header as RelayHeader, + InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PvfCheckStatement, SessionIndex, + SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, + ValidatorSignature, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; diff --git a/polkadot/node/collation-generation/Cargo.toml b/polkadot/node/collation-generation/Cargo.toml index 4b0a5f7248a..855b6b0e86e 100644 --- a/polkadot/node/collation-generation/Cargo.toml +++ b/polkadot/node/collation-generation/Cargo.toml @@ -28,3 +28,4 @@ polkadot-primitives-test-helpers = { workspace = true } assert_matches = { workspace = true } rstest = { workspace = true } sp-keyring = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, features = ["test"] } diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index 50adbddea41..f04f69cbd38 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -48,9 +48,10 @@ use polkadot_node_subsystem_util::{ request_validators, runtime::fetch_claim_queue, }; use polkadot_primitives::{ - collator_signature_payload, CandidateCommitments, CandidateDescriptor, CandidateReceipt, - CollatorPair, CoreIndex, CoreState, Hash, Id as ParaId, OccupiedCoreAssumption, - PersistedValidationData, ScheduledCore, ValidationCodeHash, + collator_signature_payload, + vstaging::{CandidateReceiptV2 as CandidateReceipt, CoreState}, + CandidateCommitments, CandidateDescriptor, CollatorPair, CoreIndex, Hash, Id as ParaId, + OccupiedCoreAssumption, PersistedValidationData, ScheduledCore, ValidationCodeHash, }; use sp_core::crypto::Pair; use std::sync::Arc; @@ -607,7 +608,8 @@ async fn construct_and_distribute_receipt( erasure_root, para_head: commitments.head_data.hash(), validation_code_hash, - }, + } + .into(), }; gum::debug!( diff --git a/polkadot/node/collation-generation/src/tests.rs b/polkadot/node/collation-generation/src/tests.rs index 7f76988bb03..78b35fde0ea 100644 --- a/polkadot/node/collation-generation/src/tests.rs +++ b/polkadot/node/collation-generation/src/tests.rs @@ -30,12 +30,12 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers::{subsystem_test_harness, TestSubsystemContextHandle}; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{ - async_backing::{BackingState, CandidatePendingAvailability}, + vstaging::async_backing::{BackingState, CandidatePendingAvailability}, AsyncBackingParams, BlockNumber, CollatorPair, HeadData, PersistedValidationData, ScheduledCore, ValidationCode, }; use polkadot_primitives_test_helpers::{ - dummy_candidate_descriptor, dummy_hash, dummy_head_data, dummy_validator, make_candidate, + dummy_candidate_descriptor_v2, dummy_hash, dummy_head_data, dummy_validator, make_candidate, }; use rstest::rstest; use sp_keyring::sr25519::Keyring as Sr25519Keyring; @@ -504,22 +504,22 @@ fn sends_distribute_collation_message(#[case] runtime_version: u32) { // 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, + &descriptor.signature().unwrap(), &collator_signature_payload( - &descriptor.relay_parent, - &descriptor.para_id, - &descriptor.persisted_validation_data_hash, - &descriptor.pov_hash, - &descriptor.validation_code_hash, + &descriptor.relay_parent(), + &descriptor.para_id(), + &descriptor.persisted_validation_data_hash(), + &descriptor.pov_hash(), + &descriptor.validation_code_hash(), ) .as_ref(), - &descriptor.collator, + &descriptor.collator().unwrap(), )); let expect_descriptor = { let mut expect_descriptor = expect_descriptor; - expect_descriptor.signature = descriptor.signature.clone(); - expect_descriptor.erasure_root = descriptor.erasure_root; - expect_descriptor + expect_descriptor.signature = descriptor.signature().clone().unwrap(); + expect_descriptor.erasure_root = descriptor.erasure_root(); + expect_descriptor.into() }; assert_eq!(descriptor, expect_descriptor); }, @@ -658,7 +658,7 @@ fn fallback_when_no_validation_code_hash_api(#[case] runtime_version: u32) { .. }) => { let CandidateReceipt { descriptor, .. } = candidate_receipt; - assert_eq!(expect_validation_code_hash, descriptor.validation_code_hash); + assert_eq!(expect_validation_code_hash, descriptor.validation_code_hash()); }, _ => panic!("received wrong message type"), } @@ -752,9 +752,9 @@ fn submit_collation_leads_to_distribution() { }) => { 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); + 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); } ); @@ -772,16 +772,17 @@ fn distribute_collation_for_occupied_core_with_async_backing_enabled(#[case] run 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::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(dummy_hash()), - })]; + 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 { @@ -882,7 +883,7 @@ fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elasti let cores = (0..3) .into_iter() .map(|idx| { - CoreState::Occupied(polkadot_primitives::OccupiedCore { + CoreState::Occupied(polkadot_primitives::vstaging::OccupiedCore { next_up_on_available: Some(ScheduledCore { para_id, collator: None }), occupied_since: 0, time_out_at: 10, @@ -890,7 +891,7 @@ fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elasti availability: Default::default(), // doesn't matter group_responsible: polkadot_primitives::GroupIndex(idx as u32), candidate_hash: Default::default(), - candidate_descriptor: dummy_candidate_descriptor(dummy_hash()), + candidate_descriptor: dummy_candidate_descriptor_v2(dummy_hash()), }) }) .collect::>(); @@ -1008,16 +1009,17 @@ fn no_collation_is_distributed_for_occupied_core_with_async_backing_disabled( 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::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(dummy_hash()), - })]; + 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 { @@ -1248,9 +1250,9 @@ mod helpers { .. }) => { 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()); - assert_eq!(candidate_receipt.descriptor().validation_code_hash, validation_code_hash); + assert_eq!(candidate_receipt.descriptor().persisted_validation_data_hash(), pvd.hash()); + assert_eq!(candidate_receipt.descriptor().para_head(), dummy_head_data().hash()); + assert_eq!(candidate_receipt.descriptor().validation_code_hash(), validation_code_hash); } ); } diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml index 2c3db866566..f9754d2babc 100644 --- a/polkadot/node/core/approval-voting/Cargo.toml +++ b/polkadot/node/core/approval-voting/Cargo.toml @@ -53,6 +53,7 @@ kvdb-memorydb = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } log = { workspace = true, default-features = true } sp-tracing = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } polkadot-subsystem-bench = { workspace = true } diff --git a/polkadot/node/core/approval-voting/src/approval_checking.rs b/polkadot/node/core/approval-voting/src/approval_checking.rs index 3774edc6998..3b7262a4682 100644 --- a/polkadot/node/core/approval-voting/src/approval_checking.rs +++ b/polkadot/node/core/approval-voting/src/approval_checking.rs @@ -509,13 +509,13 @@ mod tests { use crate::{approval_db, BTreeMap}; use bitvec::{bitvec, order::Lsb0 as BitOrderLsb0, vec::BitVec}; use polkadot_primitives::GroupIndex; - use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; + use polkadot_primitives_test_helpers::{dummy_candidate_receipt_v2, dummy_hash}; #[test] fn pending_is_not_approved() { let candidate = CandidateEntry::from_v1( approval_db::v1::CandidateEntry { - candidate: dummy_candidate_receipt(dummy_hash()), + candidate: dummy_candidate_receipt_v2(dummy_hash()), session: 0, block_assignments: BTreeMap::default(), approvals: BitVec::default(), @@ -550,7 +550,7 @@ mod tests { fn exact_takes_only_assignments_up_to() { let mut candidate: CandidateEntry = CandidateEntry::from_v1( approval_db::v1::CandidateEntry { - candidate: dummy_candidate_receipt(dummy_hash()), + candidate: dummy_candidate_receipt_v2(dummy_hash()), session: 0, block_assignments: BTreeMap::default(), approvals: bitvec![u8, BitOrderLsb0; 0; 10], @@ -624,7 +624,7 @@ mod tests { fn one_honest_node_always_approves() { let mut candidate: CandidateEntry = CandidateEntry::from_v1( approval_db::v1::CandidateEntry { - candidate: dummy_candidate_receipt(dummy_hash()), + candidate: dummy_candidate_receipt_v2(dummy_hash()), session: 0, block_assignments: BTreeMap::default(), approvals: bitvec![u8, BitOrderLsb0; 0; 10], @@ -1097,7 +1097,7 @@ mod tests { let mut candidate: CandidateEntry = CandidateEntry::from_v1( approval_db::v1::CandidateEntry { - candidate: dummy_candidate_receipt(dummy_hash()), + candidate: dummy_candidate_receipt_v2(dummy_hash()), session: 0, block_assignments: BTreeMap::default(), approvals: bitvec![u8, BitOrderLsb0; 0; 3], diff --git a/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs b/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs index 53e9db64f63..87a1d20b92f 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs @@ -25,8 +25,8 @@ use codec::{Decode, Encode}; use polkadot_node_primitives::approval::v1::{AssignmentCert, DelayTranche}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex, - ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, CoreIndex, + GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; use std::collections::BTreeMap; diff --git a/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs b/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs index cd9256a5d47..63c6cbf40b8 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs @@ -21,8 +21,8 @@ use polkadot_node_primitives::approval::{v1::DelayTranche, v2::AssignmentCertV2} use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; use polkadot_node_subsystem_util::database::{DBTransaction, Database}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, CoreIndex, GroupIndex, Hash, - SessionIndex, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, CandidateIndex, + CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; diff --git a/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs b/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs index 06a3cc1e306..866702f861c 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs @@ -26,7 +26,8 @@ use crate::{ ops::{add_block_entry, canonicalize, force_approve, NewCandidateInfo}, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, + vstaging::{CandidateReceiptV2 as CandidateReceipt, MutateDescriptorV2}, + BlockNumber, CandidateHash, CoreIndex, GroupIndex, Hash, }; use polkadot_node_subsystem_util::database::Database; @@ -35,7 +36,8 @@ use sp_consensus_slots::Slot; use std::{collections::HashMap, sync::Arc}; use polkadot_primitives_test_helpers::{ - dummy_candidate_receipt, dummy_candidate_receipt_bad_sig, dummy_hash, + dummy_candidate_receipt_bad_sig, dummy_candidate_receipt_v2, + dummy_candidate_receipt_v2_bad_sig, dummy_hash, }; const DATA_COL: u32 = 0; @@ -72,10 +74,10 @@ fn make_block_entry( } fn make_candidate(para_id: ParaId, relay_parent: Hash) -> CandidateReceipt { - let mut c = dummy_candidate_receipt(dummy_hash()); + let mut c = dummy_candidate_receipt_v2(dummy_hash()); - c.descriptor.para_id = para_id; - c.descriptor.relay_parent = relay_parent; + c.descriptor.set_para_id(para_id); + c.descriptor.set_relay_parent(relay_parent); c } @@ -95,7 +97,7 @@ fn read_write() { make_block_entry(hash_a, Default::default(), 1, vec![(CoreIndex(0), candidate_hash)]); let candidate_entry = CandidateEntry { - candidate: dummy_candidate_receipt_bad_sig(dummy_hash(), None), + candidate: dummy_candidate_receipt_v2_bad_sig(dummy_hash(), None), session: 5, block_assignments: vec![( hash_a, diff --git a/polkadot/node/core/approval-voting/src/approval_db/v3/mod.rs b/polkadot/node/core/approval-voting/src/approval_db/v3/mod.rs index 7118fb6770f..bc34f88af80 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v3/mod.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v3/mod.rs @@ -25,8 +25,8 @@ use polkadot_node_subsystem::SubsystemResult; use polkadot_node_subsystem_util::database::{DBTransaction, Database}; use polkadot_overseer::SubsystemError; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, CoreIndex, GroupIndex, Hash, - SessionIndex, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, CandidateIndex, + CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; diff --git a/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs b/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs index d2a1d7d400b..372dd49803c 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v3/tests.rs @@ -25,7 +25,8 @@ use crate::{ ops::{add_block_entry, canonicalize, force_approve, NewCandidateInfo}, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, + vstaging::{CandidateReceiptV2 as CandidateReceipt, MutateDescriptorV2}, + BlockNumber, CandidateHash, CoreIndex, GroupIndex, Hash, }; use polkadot_node_subsystem_util::database::Database; @@ -34,7 +35,7 @@ use sp_consensus_slots::Slot; use std::{collections::HashMap, sync::Arc}; use polkadot_primitives_test_helpers::{ - dummy_candidate_receipt, dummy_candidate_receipt_bad_sig, dummy_hash, + dummy_candidate_receipt_v2, dummy_candidate_receipt_v2_bad_sig, dummy_hash, }; const DATA_COL: u32 = 0; @@ -72,12 +73,12 @@ fn make_block_entry( } fn make_candidate(para_id: ParaId, relay_parent: Hash) -> CandidateReceipt { - let mut c = dummy_candidate_receipt(dummy_hash()); + let mut c = dummy_candidate_receipt_v2(dummy_hash()); - c.descriptor.para_id = para_id; - c.descriptor.relay_parent = relay_parent; + c.descriptor.set_para_id(para_id); + c.descriptor.set_relay_parent(relay_parent); - c + c.into() } #[test] @@ -86,7 +87,7 @@ fn read_write() { let hash_a = Hash::repeat_byte(1); let hash_b = Hash::repeat_byte(2); - let candidate_hash = dummy_candidate_receipt_bad_sig(dummy_hash(), None).hash(); + let candidate_hash = dummy_candidate_receipt_v2_bad_sig(dummy_hash(), None).hash(); let range = StoredBlockRange(10, 20); let at_height = vec![hash_a, hash_b]; @@ -95,7 +96,7 @@ fn read_write() { make_block_entry(hash_a, Default::default(), 1, vec![(CoreIndex(0), candidate_hash)]); let candidate_entry = CandidateEntry { - candidate: dummy_candidate_receipt_bad_sig(dummy_hash(), None), + candidate: dummy_candidate_receipt_v2_bad_sig(dummy_hash(), None), session: 5, block_assignments: vec![( hash_a, diff --git a/polkadot/node/core/approval-voting/src/import.rs b/polkadot/node/core/approval-voting/src/import.rs index e50a2f91148..be7b3103ab1 100644 --- a/polkadot/node/core/approval-voting/src/import.rs +++ b/polkadot/node/core/approval-voting/src/import.rs @@ -45,8 +45,9 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{determine_new_blocks, runtime::RuntimeInfo}; use polkadot_overseer::SubsystemSender; use polkadot_primitives::{ - node_features, BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, ConsensusLog, - CoreIndex, GroupIndex, Hash, Header, SessionIndex, + node_features, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlockNumber, CandidateHash, ConsensusLog, CoreIndex, GroupIndex, Hash, Header, SessionIndex, }; use sc_keystore::LocalKeystore; use sp_consensus_slots::Slot; @@ -618,10 +619,10 @@ pub(crate) mod tests { use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_node_subsystem_util::database::Database; use polkadot_primitives::{ - node_features::FeatureIndex, ExecutorParams, Id as ParaId, IndexedVec, NodeFeatures, - SessionInfo, ValidatorId, ValidatorIndex, + node_features::FeatureIndex, vstaging::MutateDescriptorV2, ExecutorParams, Id as ParaId, + IndexedVec, NodeFeatures, SessionInfo, ValidatorId, ValidatorIndex, }; - use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; + use polkadot_primitives_test_helpers::{dummy_candidate_receipt_v2, dummy_hash}; use schnellru::{ByLength, LruMap}; pub(crate) use sp_consensus_babe::{ digests::{CompatibleDigestItem, PreDigest, SecondaryVRFPreDigest}, @@ -764,9 +765,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ @@ -917,9 +918,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ @@ -1056,9 +1057,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ @@ -1150,9 +1151,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ @@ -1340,9 +1341,9 @@ pub(crate) mod tests { let hash = header.hash(); let make_candidate = |para_id| { - let mut r = dummy_candidate_receipt(dummy_hash()); - r.descriptor.para_id = para_id; - r.descriptor.relay_parent = hash; + let mut r = dummy_candidate_receipt_v2(dummy_hash()); + r.descriptor.set_para_id(para_id); + r.descriptor.set_relay_parent(hash); r }; let candidates = vec![ diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index 0cb977c5802..2176cc7675b 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -52,9 +52,10 @@ use polkadot_node_subsystem_util::{ TimeoutExt, }; use polkadot_primitives::{ - ApprovalVoteMultipleCandidates, ApprovalVotingParams, BlockNumber, CandidateHash, - CandidateIndex, CandidateReceipt, CoreIndex, ExecutorParams, GroupIndex, Hash, SessionIndex, - SessionInfo, ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, ApprovalVoteMultipleCandidates, + ApprovalVotingParams, BlockNumber, CandidateHash, CandidateIndex, CoreIndex, ExecutorParams, + GroupIndex, Hash, SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorPair, + ValidatorSignature, }; use sc_keystore::LocalKeystore; use sp_application_crypto::Pair; @@ -2824,7 +2825,7 @@ where target: LOG_TARGET, validator_index = approval.validator.0, candidate_hash = ?approved_candidate_hash, - para_id = ?candidate_entry.candidate_receipt().descriptor.para_id, + para_id = ?candidate_entry.candidate_receipt().descriptor.para_id(), "Importing approval vote", ); @@ -2923,7 +2924,7 @@ where let block_hash = block_entry.block_hash(); let block_number = block_entry.block_number(); let session_index = block_entry.session(); - let para_id = candidate_entry.candidate_receipt().descriptor().para_id; + let para_id = candidate_entry.candidate_receipt().descriptor().para_id(); let tick_now = state.clock.tick_now(); let (is_approved, status) = if let Some((approval_entry, status)) = state @@ -3221,7 +3222,7 @@ async fn process_wakeup>( gum::trace!( target: LOG_TARGET, ?candidate_hash, - para_id = ?candidate_receipt.descriptor.para_id, + para_id = ?candidate_receipt.descriptor.para_id(), block_hash = ?relay_block, "Launching approval work.", ); @@ -3352,7 +3353,7 @@ async fn launch_approval< } let candidate_hash = candidate.hash(); - let para_id = candidate.descriptor.para_id; + let para_id = candidate.descriptor.para_id(); gum::trace!(target: LOG_TARGET, ?candidate_hash, ?para_id, "Recovering data."); let timer = metrics.time_recover_and_approve(); @@ -3370,7 +3371,7 @@ async fn launch_approval< .send_message(RuntimeApiMessage::Request( block_hash, RuntimeApiRequest::ValidationCodeByHash( - candidate.descriptor.validation_code_hash, + candidate.descriptor.validation_code_hash(), code_tx, ), )) @@ -3393,7 +3394,7 @@ async fn launch_approval< ?para_id, ?candidate_hash, "Data unavailable for candidate {:?}", - (candidate_hash, candidate.descriptor.para_id), + (candidate_hash, candidate.descriptor.para_id()), ); // do nothing. we'll just be a no-show and that'll cause others to rise up. metrics_guard.take().on_approval_unavailable(); @@ -3404,7 +3405,7 @@ async fn launch_approval< ?para_id, ?candidate_hash, "Channel closed while recovering data for candidate {:?}", - (candidate_hash, candidate.descriptor.para_id), + (candidate_hash, candidate.descriptor.para_id()), ); // do nothing. we'll just be a no-show and that'll cause others to rise up. metrics_guard.take().on_approval_unavailable(); @@ -3415,7 +3416,7 @@ async fn launch_approval< ?para_id, ?candidate_hash, "Data recovery invalid for candidate {:?}", - (candidate_hash, candidate.descriptor.para_id), + (candidate_hash, candidate.descriptor.para_id()), ); issue_local_invalid_statement( &mut sender, @@ -3438,7 +3439,7 @@ async fn launch_approval< gum::warn!( target: LOG_TARGET, "Validation code unavailable for block {:?} in the state of block {:?} (a recent descendant)", - candidate.descriptor.relay_parent, + candidate.descriptor.relay_parent(), block_hash, ); diff --git a/polkadot/node/core/approval-voting/src/ops.rs b/polkadot/node/core/approval-voting/src/ops.rs index 2a8fdba5aa3..f105580009f 100644 --- a/polkadot/node/core/approval-voting/src/ops.rs +++ b/polkadot/node/core/approval-voting/src/ops.rs @@ -20,7 +20,9 @@ use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; use bitvec::order::Lsb0 as BitOrderLsb0; -use polkadot_primitives::{BlockNumber, CandidateHash, CandidateReceipt, GroupIndex, Hash}; +use polkadot_primitives::{ + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, GroupIndex, Hash, +}; use std::collections::{hash_map::Entry, BTreeMap, HashMap}; diff --git a/polkadot/node/core/approval-voting/src/persisted_entries.rs b/polkadot/node/core/approval-voting/src/persisted_entries.rs index 16e231aa1a2..d891af01c3a 100644 --- a/polkadot/node/core/approval-voting/src/persisted_entries.rs +++ b/polkadot/node/core/approval-voting/src/persisted_entries.rs @@ -26,8 +26,8 @@ use polkadot_node_primitives::approval::{ v2::{AssignmentCertV2, CandidateBitfield}, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, CoreIndex, GroupIndex, Hash, - SessionIndex, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, CandidateIndex, + CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index db5ffd441c0..099ab419dfb 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -42,8 +42,9 @@ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_overseer::SpawnGlue; use polkadot_primitives::{ - ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, DisputeStatement, GroupIndex, - Header, Id as ParaId, IndexedVec, NodeFeatures, ValidDisputeStatementKind, ValidationCode, + vstaging::{CandidateEvent, MutateDescriptorV2}, + ApprovalVote, CandidateCommitments, CoreIndex, DisputeStatement, GroupIndex, Header, + Id as ParaId, IndexedVec, NodeFeatures, ValidDisputeStatementKind, ValidationCode, ValidatorSignature, }; use std::{cmp::max, time::Duration}; @@ -69,7 +70,9 @@ use super::{ }, }; -use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_candidate_receipt_bad_sig}; +use polkadot_primitives_test_helpers::{ + dummy_candidate_receipt_v2, dummy_candidate_receipt_v2_bad_sig, +}; const SLOT_DURATION_MILLIS: u64 = 5000; @@ -638,8 +641,8 @@ where } fn make_candidate(para_id: ParaId, hash: &Hash) -> CandidateReceipt { - let mut r = dummy_candidate_receipt_bad_sig(*hash, Some(Default::default())); - r.descriptor.para_id = para_id; + let mut r = dummy_candidate_receipt_v2_bad_sig(*hash, Some(Default::default())); + r.descriptor.set_para_id(para_id); r } @@ -1282,7 +1285,7 @@ fn subsystem_rejects_approval_if_no_block_entry() { let block_hash = Hash::repeat_byte(0x01); let candidate_index = 0; let validator = ValidatorIndex(0); - let candidate_hash = dummy_candidate_receipt(block_hash).hash(); + let candidate_hash = dummy_candidate_receipt_v2(block_hash).hash(); let session_index = 1; let rx = import_approval( @@ -1324,9 +1327,9 @@ fn subsystem_rejects_approval_before_assignment() { let candidate_hash = { let mut candidate_receipt = - dummy_candidate_receipt_bad_sig(block_hash, Some(Default::default())); - candidate_receipt.descriptor.para_id = ParaId::from(0_u32); - candidate_receipt.descriptor.relay_parent = block_hash; + dummy_candidate_receipt_v2_bad_sig(block_hash, Some(Default::default())); + candidate_receipt.descriptor.set_para_id(ParaId::from(0_u32)); + candidate_receipt.descriptor.set_relay_parent(block_hash); candidate_receipt.hash() }; @@ -1390,15 +1393,17 @@ fn subsystem_accepts_duplicate_assignment() { let block_hash = Hash::repeat_byte(0x01); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt - }; + } + .into(); let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt - }; + } + .into(); let candidate_index1 = 0; let candidate_index2 = 1; @@ -1572,9 +1577,9 @@ fn subsystem_accepts_and_imports_approval_after_assignment() { let candidate_hash = { let mut candidate_receipt = - dummy_candidate_receipt_bad_sig(block_hash, Some(Default::default())); - candidate_receipt.descriptor.para_id = ParaId::from(0_u32); - candidate_receipt.descriptor.relay_parent = block_hash; + dummy_candidate_receipt_v2_bad_sig(block_hash, Some(Default::default())); + candidate_receipt.descriptor.set_para_id(ParaId::from(0_u32)); + candidate_receipt.descriptor.set_relay_parent(block_hash); candidate_receipt.hash() }; @@ -1643,9 +1648,9 @@ fn subsystem_second_approval_import_only_schedules_wakeups() { let candidate_hash = { let mut candidate_receipt = - dummy_candidate_receipt_bad_sig(block_hash, Some(Default::default())); - candidate_receipt.descriptor.para_id = ParaId::from(0_u32); - candidate_receipt.descriptor.relay_parent = block_hash; + dummy_candidate_receipt_v2_bad_sig(block_hash, Some(Default::default())); + candidate_receipt.descriptor.set_para_id(ParaId::from(0_u32)); + candidate_receipt.descriptor.set_relay_parent(block_hash); candidate_receipt.hash() }; @@ -2402,13 +2407,13 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { let block_hash = Hash::repeat_byte(0x01); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt }; let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt }; let candidate_hash1 = candidate_receipt1.hash(); @@ -2566,18 +2571,18 @@ fn inclusion_events_can_be_unordered_by_core_index() { let block_hash = Hash::repeat_byte(0x01); let candidate_receipt0 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(0_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(0_u32)); receipt }; let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt }; let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt }; let candidate_index0 = 0; @@ -2710,8 +2715,8 @@ fn approved_ancestor_test( .iter() .enumerate() .map(|(i, hash)| { - let mut candidate_receipt = dummy_candidate_receipt(*hash); - candidate_receipt.descriptor.para_id = i.into(); + let mut candidate_receipt = dummy_candidate_receipt_v2(*hash); + candidate_receipt.descriptor.set_para_id(i.into()); candidate_receipt }) .collect(); @@ -2882,7 +2887,7 @@ fn subsystem_validate_approvals_cache() { let block_hash = Hash::repeat_byte(0x01); let fork_block_hash = Hash::repeat_byte(0x02); let candidate_commitments = CandidateCommitments::default(); - let mut candidate_receipt = dummy_candidate_receipt(block_hash); + let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash); candidate_receipt.commitments_hash = candidate_commitments.hash(); let candidate_hash = candidate_receipt.hash(); let slot = Slot::from(1); @@ -3012,13 +3017,13 @@ fn subsystem_doesnt_distribute_duplicate_compact_assignments() { let block_hash = Hash::repeat_byte(0x01); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt }; let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt }; let candidate_index1 = 0; @@ -3263,7 +3268,7 @@ where ); let block_hash = Hash::repeat_byte(0x01); - let candidate_receipt = dummy_candidate_receipt(block_hash); + let candidate_receipt = dummy_candidate_receipt_v2(block_hash); let candidate_hash = candidate_receipt.hash(); let slot = Slot::from(1); let candidate_index = 0; @@ -3965,8 +3970,8 @@ fn test_approval_is_sent_on_max_approval_coalesce_count() { let candidate_commitments = CandidateCommitments::default(); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt.commitments_hash = candidate_commitments.hash(); receipt }; @@ -3974,8 +3979,8 @@ fn test_approval_is_sent_on_max_approval_coalesce_count() { let candidate_hash1 = candidate_receipt1.hash(); let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt.commitments_hash = candidate_commitments.hash(); receipt }; @@ -4266,8 +4271,8 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { let candidate_commitments = CandidateCommitments::default(); let candidate_receipt1 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(1_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(1_u32)); receipt.commitments_hash = candidate_commitments.hash(); receipt }; @@ -4275,8 +4280,8 @@ fn test_approval_is_sent_on_max_approval_coalesce_wait() { let candidate_hash1 = candidate_receipt1.hash(); let candidate_receipt2 = { - let mut receipt = dummy_candidate_receipt(block_hash); - receipt.descriptor.para_id = ParaId::from(2_u32); + let mut receipt = dummy_candidate_receipt_v2(block_hash); + receipt.descriptor.set_para_id(ParaId::from(2_u32)); receipt.commitments_hash = candidate_commitments.hash(); receipt }; @@ -4420,7 +4425,7 @@ async fn setup_overseer_with_two_blocks_each_with_one_assignment_triggered( let block_hash = Hash::repeat_byte(0x01); let fork_block_hash = Hash::repeat_byte(0x02); let candidate_commitments = CandidateCommitments::default(); - let mut candidate_receipt = dummy_candidate_receipt(block_hash); + let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash); candidate_receipt.commitments_hash = candidate_commitments.hash(); let candidate_hash = candidate_receipt.hash(); let slot = Slot::from(1); @@ -4549,7 +4554,7 @@ fn subsystem_relaunches_approval_work_on_restart() { let block_hash = Hash::repeat_byte(0x01); let fork_block_hash = Hash::repeat_byte(0x02); let candidate_commitments = CandidateCommitments::default(); - let mut candidate_receipt = dummy_candidate_receipt(block_hash); + let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash); candidate_receipt.commitments_hash = candidate_commitments.hash(); let slot = Slot::from(1); clock.inner.lock().set_tick(slot_to_tick(slot + 2)); @@ -4805,7 +4810,7 @@ fn subsystem_sends_pending_approvals_on_approval_restart() { let block_hash = Hash::repeat_byte(0x01); let fork_block_hash = Hash::repeat_byte(0x02); let candidate_commitments = CandidateCommitments::default(); - let mut candidate_receipt = dummy_candidate_receipt(block_hash); + let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash); candidate_receipt.commitments_hash = candidate_commitments.hash(); let slot = Slot::from(1); @@ -4815,7 +4820,7 @@ fn subsystem_sends_pending_approvals_on_approval_restart() { fork_block_hash, slot, sync_oracle_handle, - candidate_receipt, + candidate_receipt.into(), ) .await; chain_builder.build(&mut virtual_overseer).await; diff --git a/polkadot/node/core/av-store/src/lib.rs b/polkadot/node/core/av-store/src/lib.rs index 9473040e8f5..9da2973773a 100644 --- a/polkadot/node/core/av-store/src/lib.rs +++ b/polkadot/node/core/av-store/src/lib.rs @@ -47,8 +47,8 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_util as util; use polkadot_primitives::{ - BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, ChunkIndex, CoreIndex, Hash, - Header, NodeFeatures, ValidatorIndex, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlockNumber, CandidateHash, ChunkIndex, CoreIndex, Hash, Header, NodeFeatures, ValidatorIndex, }; use util::availability_chunks::availability_chunk_indices; diff --git a/polkadot/node/core/av-store/src/tests.rs b/polkadot/node/core/av-store/src/tests.rs index 958917a3104..80043e56976 100644 --- a/polkadot/node/core/av-store/src/tests.rs +++ b/polkadot/node/core/av-store/src/tests.rs @@ -31,8 +31,8 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::{database::Database, TimeoutExt}; use polkadot_primitives::{ - node_features, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, HeadData, Header, - PersistedValidationData, ValidatorId, + node_features, vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, CoreIndex, + GroupIndex, HeadData, Header, PersistedValidationData, ValidatorId, }; use polkadot_primitives_test_helpers::TestCandidateBuilder; use sp_keyring::Sr25519Keyring; diff --git a/polkadot/node/core/backing/Cargo.toml b/polkadot/node/core/backing/Cargo.toml index bd56a3ad693..cd1acf9daa9 100644 --- a/polkadot/node/core/backing/Cargo.toml +++ b/polkadot/node/core/backing/Cargo.toml @@ -36,3 +36,4 @@ assert_matches = { workspace = true } rstest = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } diff --git a/polkadot/node/core/backing/src/error.rs b/polkadot/node/core/backing/src/error.rs index 568f7140264..e09d8425f78 100644 --- a/polkadot/node/core/backing/src/error.rs +++ b/polkadot/node/core/backing/src/error.rs @@ -24,7 +24,7 @@ use polkadot_node_subsystem::{ RuntimeApiError, SubsystemError, }; use polkadot_node_subsystem_util::{runtime, Error as UtilError}; -use polkadot_primitives::{BackedCandidate, ValidationCodeHash}; +use polkadot_primitives::{vstaging::BackedCandidate, ValidationCodeHash}; use crate::{ParaId, LOG_TARGET}; diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index 4463fb34b51..b5362d32ad8 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -108,10 +108,14 @@ use polkadot_node_subsystem_util::{ }; use polkadot_parachain_primitives::primitives::IsSystem; use polkadot_primitives::{ - node_features::FeatureIndex, BackedCandidate, CandidateCommitments, CandidateHash, - CandidateReceipt, CommittedCandidateReceipt, CoreIndex, CoreState, ExecutorParams, GroupIndex, - GroupRotationInfo, Hash, Id as ParaId, IndexedVec, NodeFeatures, PersistedValidationData, - SessionIndex, SigningContext, ValidationCode, ValidatorId, ValidatorIndex, ValidatorSignature, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + }, + CandidateCommitments, CandidateHash, CoreIndex, ExecutorParams, GroupIndex, GroupRotationInfo, + Hash, Id as ParaId, IndexedVec, NodeFeatures, PersistedValidationData, SessionIndex, + SigningContext, ValidationCode, ValidatorId, ValidatorIndex, ValidatorSignature, ValidityAttestation, }; use polkadot_statement_table::{ @@ -627,7 +631,7 @@ async fn request_candidate_validation( executor_params: ExecutorParams, ) -> Result { let (tx, rx) = oneshot::channel(); - let is_system = candidate_receipt.descriptor.para_id.is_system(); + let is_system = candidate_receipt.descriptor.para_id().is_system(); sender .send_message(CandidateValidationMessage::ValidateFromExhaustive { @@ -692,7 +696,7 @@ async fn validate_and_make_available( } = params; let validation_code = { - let validation_code_hash = candidate.descriptor().validation_code_hash; + let validation_code_hash = candidate.descriptor().validation_code_hash(); let (tx, rx) = oneshot::channel(); sender .send_message(RuntimeApiMessage::Request( @@ -725,7 +729,7 @@ async fn validate_and_make_available( &mut sender, relay_parent, from_validator, - candidate.descriptor.para_id, + candidate.descriptor.para_id(), candidate_hash, pov_hash, ) @@ -772,7 +776,7 @@ async fn validate_and_make_available( pov.clone(), candidate.hash(), validation_data.clone(), - candidate.descriptor.erasure_root, + candidate.descriptor.erasure_root(), core_index, node_features, ) @@ -1054,7 +1058,7 @@ fn core_index_from_statement( } if let StatementWithPVD::Seconded(candidate, _pvd) = statement.payload() { - let candidate_para_id = candidate.descriptor.para_id; + let candidate_para_id = candidate.descriptor.para_id(); let mut assigned_paras = claim_queue.iter_claims_for_core(&core_index); if !assigned_paras.any(|id| id == &candidate_para_id) { @@ -1445,14 +1449,14 @@ async fn handle_validated_candidate_command( let candidate_hash = candidate.hash(); gum::debug!( target: LOG_TARGET, - relay_parent = ?candidate.descriptor().relay_parent, + relay_parent = ?candidate.descriptor().relay_parent(), ?candidate_hash, "Attempted to second candidate but was rejected by prospective parachains", ); // Ensure the collator is reported. ctx.send_message(CollatorProtocolMessage::Invalid( - candidate.descriptor().relay_parent, + candidate.descriptor().relay_parent(), candidate, )) .await; @@ -1487,7 +1491,7 @@ async fn handle_validated_candidate_command( Some(d) => d, }; - leaf_data.add_seconded_candidate(candidate.descriptor().para_id); + leaf_data.add_seconded_candidate(candidate.descriptor().para_id()); } rp_state.issued_statements.insert(candidate_hash); @@ -1636,7 +1640,7 @@ async fn import_statement( let (tx, rx) = oneshot::channel(); ctx.send_message(ProspectiveParachainsMessage::IntroduceSecondedCandidate( IntroduceSecondedCandidateRequest { - candidate_para: candidate.descriptor().para_id, + candidate_para: candidate.descriptor.para_id(), candidate_receipt: candidate.clone(), persisted_validation_data: pvd.clone(), }, @@ -1665,7 +1669,7 @@ async fn import_statement( persisted_validation_data: pvd.clone(), // This is set after importing when seconding locally. seconded_locally: false, - relay_parent: candidate.descriptor().relay_parent, + relay_parent: candidate.descriptor.relay_parent(), }, ); } @@ -1709,7 +1713,7 @@ async fn post_import_statement_actions( &rp_state.table_context, rp_state.inject_core_index, ) { - let para_id = backed.candidate().descriptor.para_id; + let para_id = backed.candidate().descriptor.para_id(); gum::debug!( target: LOG_TARGET, candidate_hash = ?candidate_hash, @@ -1967,7 +1971,7 @@ async fn maybe_validate_and_import( .get_candidate(&candidate_hash) .ok_or(Error::CandidateNotFound)? .to_plain(), - pov_hash: receipt.descriptor.pov_hash, + pov_hash: receipt.descriptor.pov_hash(), from_validator: statement.validator_index(), backing: Vec::new(), }; @@ -2068,9 +2072,9 @@ async fn handle_second_message( let _timer = metrics.time_process_second(); let candidate_hash = candidate.hash(); - let relay_parent = candidate.descriptor().relay_parent; + let relay_parent = candidate.descriptor().relay_parent(); - if candidate.descriptor().persisted_validation_data_hash != persisted_validation_data.hash() { + if candidate.descriptor().persisted_validation_data_hash() != persisted_validation_data.hash() { gum::warn!( target: LOG_TARGET, ?candidate_hash, @@ -2104,12 +2108,12 @@ async fn handle_second_message( let assigned_paras = rp_state.assigned_core.and_then(|core| rp_state.claim_queue.0.get(&core)); // Sanity check that candidate is from our assignment. - if !matches!(assigned_paras, Some(paras) if paras.contains(&candidate.descriptor().para_id)) { + if !matches!(assigned_paras, Some(paras) if paras.contains(&candidate.descriptor().para_id())) { gum::debug!( target: LOG_TARGET, our_assignment_core = ?rp_state.assigned_core, our_assignment_paras = ?assigned_paras, - collation = ?candidate.descriptor().para_id, + collation = ?candidate.descriptor().para_id(), "Subsystem asked to second for para outside of our assignment", ); return Ok(()); @@ -2119,7 +2123,7 @@ async fn handle_second_message( target: LOG_TARGET, our_assignment_core = ?rp_state.assigned_core, our_assignment_paras = ?assigned_paras, - collation = ?candidate.descriptor().para_id, + collation = ?candidate.descriptor().para_id(), "Current assignments vs collation", ); diff --git a/polkadot/node/core/backing/src/tests/mod.rs b/polkadot/node/core/backing/src/tests/mod.rs index d9c1fc9499e..dbb974a634f 100644 --- a/polkadot/node/core/backing/src/tests/mod.rs +++ b/polkadot/node/core/backing/src/tests/mod.rs @@ -22,19 +22,19 @@ use polkadot_node_primitives::{BlockData, InvalidCandidate, SignedFullStatement, use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{ - AllMessages, CollatorProtocolMessage, RuntimeApiMessage, RuntimeApiRequest, + AllMessages, CollatorProtocolMessage, PvfExecKind, RuntimeApiMessage, RuntimeApiRequest, ValidationFailed, }, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, TimeoutExt, }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_primitives::{ - node_features, CandidateDescriptor, GroupRotationInfo, HeadData, PersistedValidationData, - ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, + node_features, vstaging::MutateDescriptorV2, CandidateDescriptor, GroupRotationInfo, HeadData, + PersistedValidationData, ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, }; use polkadot_primitives_test_helpers::{ dummy_candidate_receipt_bad_sig, dummy_collator, dummy_collator_signature, - dummy_committed_candidate_receipt, dummy_hash, validator_pubkeys, + dummy_committed_candidate_receipt_v2, dummy_hash, validator_pubkeys, }; use polkadot_statement_table::v2::Misbehavior; use rstest::rstest; @@ -236,7 +236,8 @@ impl TestCandidateBuilder { para_head: self.head_data.hash(), validation_code_hash: ValidationCode(self.validation_code).hash(), persisted_validation_data_hash: self.persisted_validation_data_hash, - }, + } + .into(), commitments: CandidateCommitments { head_data: self.head_data, upward_messages: Default::default(), @@ -433,7 +434,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() && + *pov == *assert_pov && candidate_receipt.descriptor == assert_candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_receipt.commitments_hash == assert_candidate.commitments.hash() => { @@ -650,7 +651,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() && + *pov == pov_ab && candidate_receipt.descriptor == candidate_a.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_receipt.commitments_hash == candidate_a_commitments_hash => { @@ -1121,7 +1122,7 @@ fn extract_core_index_from_statement_works() { .flatten() .expect("should be signed"); - candidate.descriptor.para_id = test_state.chain_ids[1]; + candidate.descriptor.set_para_id(test_state.chain_ids[1]); let signed_statement_3 = SignedFullStatementWithPVD::sign( &test_state.keystore, @@ -1286,7 +1287,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() && + *pov == pov_abc && candidate_receipt.descriptor == candidate_a.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_a_commitments_hash == candidate_receipt.commitments_hash => { @@ -1453,7 +1454,7 @@ fn backing_misbehavior_works() { }, ) if validation_data == pvd_a && validation_code == validation_code_a && - *pov == pov_a && &candidate_receipt.descriptor == candidate_a.descriptor() && + *pov == pov_a && candidate_receipt.descriptor == candidate_a.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_a_commitments_hash == candidate_receipt.commitments_hash => { @@ -1620,7 +1621,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() && + *pov == pov_block_a && candidate_receipt.descriptor == candidate_a.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_a.commitments.hash() == candidate_receipt.commitments_hash => { @@ -1660,7 +1661,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() && + *pov == pov_block_b && candidate_receipt.descriptor == candidate_b.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_b.commitments.hash() == candidate_receipt.commitments_hash => { @@ -1787,7 +1788,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() && + *pov == pov_a && candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash => { @@ -1931,7 +1932,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() && + *pov == pov_a && candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash => { @@ -1999,7 +2000,7 @@ fn candidate_backing_reorders_votes() { }; let attested = TableAttestedCandidate { - candidate: dummy_committed_candidate_receipt(dummy_hash()), + candidate: dummy_committed_candidate_receipt_v2(dummy_hash()), validity_votes: vec![ (ValidatorIndex(5), fake_attestation(5)), (ValidatorIndex(3), fake_attestation(3)), @@ -2210,7 +2211,7 @@ fn retry_works() { }, ) if validation_data == pvd_a && validation_code == validation_code_a && - *pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() && + *pov == pov_a && candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash ); @@ -2752,7 +2753,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() && + *pov == expected_pov && candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate_commitments_hash == candidate_receipt.commitments_hash => { diff --git a/polkadot/node/core/backing/src/tests/prospective_parachains.rs b/polkadot/node/core/backing/src/tests/prospective_parachains.rs index 57b2fabd43b..caddd240805 100644 --- a/polkadot/node/core/backing/src/tests/prospective_parachains.rs +++ b/polkadot/node/core/backing/src/tests/prospective_parachains.rs @@ -20,7 +20,7 @@ use polkadot_node_subsystem::{ messages::{ChainApiMessage, HypotheticalMembership}, ActivatedLeaf, TimeoutExt, }; -use polkadot_primitives::{AsyncBackingParams, BlockNumber, Header, OccupiedCore}; +use polkadot_primitives::{vstaging::OccupiedCore, AsyncBackingParams, BlockNumber, Header}; use super::*; @@ -275,7 +275,7 @@ async fn assert_validate_seconded_candidate( }) if &validation_data == assert_pvd && &validation_code == assert_validation_code && &*pov == assert_pov && - &candidate_receipt.descriptor == candidate.descriptor() && + candidate_receipt.descriptor == candidate.descriptor && exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash => { @@ -890,7 +890,7 @@ fn prospective_parachains_reject_candidate() { AllMessages::CollatorProtocol(CollatorProtocolMessage::Invalid( relay_parent, candidate_receipt, - )) if candidate_receipt.descriptor() == candidate.descriptor() && + )) if candidate_receipt.descriptor == candidate.descriptor && candidate_receipt.commitments_hash == candidate.commitments.hash() && relay_parent == leaf_a_parent ); @@ -1011,7 +1011,7 @@ fn second_multiple_candidates_per_relay_parent() { assert_validate_seconded_candidate( &mut virtual_overseer, - candidate.descriptor().relay_parent, + candidate.descriptor.relay_parent(), &candidate, &pov, &pvd, @@ -1064,13 +1064,13 @@ fn second_multiple_candidates_per_relay_parent() { parent_hash, _signed_statement, ) - ) if parent_hash == candidate.descriptor().relay_parent => {} + ) if parent_hash == candidate.descriptor.relay_parent() => {} ); assert_matches!( virtual_overseer.recv().await, AllMessages::CollatorProtocol(CollatorProtocolMessage::Seconded(hash, statement)) => { - assert_eq!(candidate.descriptor().relay_parent, hash); + assert_eq!(candidate.descriptor.relay_parent(), hash); assert_matches!(statement.payload(), Statement::Seconded(_)); } ); @@ -1179,7 +1179,7 @@ fn backing_works() { assert_validate_seconded_candidate( &mut virtual_overseer, - candidate_a.descriptor().relay_parent, + candidate_a.descriptor.relay_parent(), &candidate_a, &pov, &pvd, @@ -1544,7 +1544,7 @@ fn seconding_sanity_check_occupy_same_depth() { assert_validate_seconded_candidate( &mut virtual_overseer, - candidate.descriptor().relay_parent, + candidate.descriptor.relay_parent(), &candidate, &pov, &pvd, @@ -1599,13 +1599,13 @@ fn seconding_sanity_check_occupy_same_depth() { parent_hash, _signed_statement, ) - ) if parent_hash == candidate.descriptor().relay_parent => {} + ) if parent_hash == candidate.descriptor.relay_parent() => {} ); assert_matches!( virtual_overseer.recv().await, AllMessages::CollatorProtocol(CollatorProtocolMessage::Seconded(hash, statement)) => { - assert_eq!(candidate.descriptor().relay_parent, hash); + assert_eq!(candidate.descriptor.relay_parent(), hash); assert_matches!(statement.payload(), Statement::Seconded(_)); } ); @@ -1637,7 +1637,7 @@ fn occupied_core_assignment() { time_out_at: 200_u32, next_up_on_time_out: None, availability: Default::default(), - candidate_descriptor, + candidate_descriptor: candidate_descriptor.into(), candidate_hash: Default::default(), }); diff --git a/polkadot/node/core/bitfield-signing/src/lib.rs b/polkadot/node/core/bitfield-signing/src/lib.rs index 474de1c66ab..7c67853503f 100644 --- a/polkadot/node/core/bitfield-signing/src/lib.rs +++ b/polkadot/node/core/bitfield-signing/src/lib.rs @@ -34,7 +34,7 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_util::{ self as util, request_availability_cores, runtime::recv_runtime, Validator, }; -use polkadot_primitives::{AvailabilityBitfield, CoreState, Hash, ValidatorIndex}; +use polkadot_primitives::{vstaging::CoreState, AvailabilityBitfield, Hash, ValidatorIndex}; use sp_keystore::{Error as KeystoreError, KeystorePtr}; use std::{collections::HashMap, time::Duration}; use wasm_timer::{Delay, Instant}; diff --git a/polkadot/node/core/bitfield-signing/src/tests.rs b/polkadot/node/core/bitfield-signing/src/tests.rs index c08018375cf..9123414844a 100644 --- a/polkadot/node/core/bitfield-signing/src/tests.rs +++ b/polkadot/node/core/bitfield-signing/src/tests.rs @@ -17,8 +17,8 @@ use super::*; use futures::{executor::block_on, pin_mut, StreamExt}; use polkadot_node_subsystem::messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest}; -use polkadot_primitives::{CandidateHash, OccupiedCore}; -use polkadot_primitives_test_helpers::dummy_candidate_descriptor; +use polkadot_primitives::{vstaging::OccupiedCore, CandidateHash}; +use polkadot_primitives_test_helpers::dummy_candidate_descriptor_v2; fn occupied_core(para_id: u32, candidate_hash: CandidateHash) -> CoreState { CoreState::Occupied(OccupiedCore { @@ -29,7 +29,7 @@ fn occupied_core(para_id: u32, candidate_hash: CandidateHash) -> CoreState { next_up_on_time_out: None, availability: Default::default(), candidate_hash, - candidate_descriptor: dummy_candidate_descriptor(Hash::zero()), + candidate_descriptor: dummy_candidate_descriptor_v2(Hash::zero()), }) } diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index e875be9b5df..a48669c2482 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -45,8 +45,11 @@ use polkadot_primitives::{ DEFAULT_APPROVAL_EXECUTION_TIMEOUT, DEFAULT_BACKING_EXECUTION_TIMEOUT, DEFAULT_LENIENT_PREPARATION_TIMEOUT, DEFAULT_PRECHECK_PREPARATION_TIMEOUT, }, - AuthorityDiscoveryId, CandidateCommitments, CandidateDescriptor, CandidateEvent, - CandidateReceipt, ExecutorParams, Hash, PersistedValidationData, + vstaging::{ + CandidateDescriptorV2 as CandidateDescriptor, CandidateEvent, + CandidateReceiptV2 as CandidateReceipt, + }, + AuthorityDiscoveryId, CandidateCommitments, ExecutorParams, Hash, PersistedValidationData, PvfExecKind as RuntimePvfExecKind, PvfPrepKind, SessionIndex, ValidationCode, ValidationCodeHash, ValidatorId, }; @@ -437,7 +440,7 @@ where .into_iter() .filter_map(|e| match e { CandidateEvent::CandidateBacked(receipt, ..) => { - let h = receipt.descriptor.validation_code_hash; + let h = receipt.descriptor.validation_code_hash(); if already_prepared.contains(&h) { None } else { @@ -646,7 +649,7 @@ async fn validate_candidate_exhaustive( let _timer = metrics.time_validate_candidate_exhaustive(); let validation_code_hash = validation_code.hash(); - let para_id = candidate_receipt.descriptor.para_id; + let para_id = candidate_receipt.descriptor.para_id(); gum::debug!( target: LOG_TARGET, ?validation_code_hash, @@ -747,7 +750,7 @@ async fn validate_candidate_exhaustive( Err(ValidationFailed(e.to_string())) }, Ok(res) => - if res.head_data.hash() != candidate_receipt.descriptor.para_head { + if res.head_data.hash() != candidate_receipt.descriptor.para_head() { gum::info!(target: LOG_TARGET, ?para_id, "Invalid candidate (para_head)"); Ok(ValidationResult::Invalid(InvalidCandidate::ParaHeadHashMismatch)) } else { @@ -992,11 +995,11 @@ fn perform_basic_checks( return Err(InvalidCandidate::ParamsTooLarge(encoded_pov_size as u64)) } - if pov_hash != candidate.pov_hash { + if pov_hash != candidate.pov_hash() { return Err(InvalidCandidate::PoVHashMismatch) } - if *validation_code_hash != candidate.validation_code_hash { + if *validation_code_hash != candidate.validation_code_hash() { return Err(InvalidCandidate::CodeHashMismatch) } diff --git a/polkadot/node/core/candidate-validation/src/tests.rs b/polkadot/node/core/candidate-validation/src/tests.rs index 2f7baf4abb6..997a347631a 100644 --- a/polkadot/node/core/candidate-validation/src/tests.rs +++ b/polkadot/node/core/candidate-validation/src/tests.rs @@ -26,8 +26,8 @@ use polkadot_node_subsystem::messages::AllMessages; use polkadot_node_subsystem_util::reexports::SubsystemContext; use polkadot_overseer::ActivatedLeaf; use polkadot_primitives::{ - CoreIndex, GroupIndex, HeadData, Id as ParaId, OccupiedCoreAssumption, SessionInfo, - UpwardMessage, ValidatorId, + vstaging::CandidateDescriptorV2, CandidateDescriptor, CoreIndex, GroupIndex, HeadData, + Id as ParaId, OccupiedCoreAssumption, SessionInfo, UpwardMessage, ValidatorId, }; use polkadot_primitives_test_helpers::{ dummy_collator, dummy_collator_signature, dummy_hash, make_valid_candidate_descriptor, @@ -106,7 +106,8 @@ fn correctly_checks_included_assumption() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -180,7 +181,8 @@ fn correctly_checks_timed_out_assumption() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -252,7 +254,8 @@ fn check_is_bad_request_if_no_validation_data() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -308,7 +311,8 @@ fn check_is_bad_request_if_no_validation_code() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -376,7 +380,8 @@ fn check_does_not_match() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let pool = TaskExecutor::new(); let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< @@ -478,7 +483,8 @@ fn candidate_validation_ok_is_ok() { head_data.hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -546,7 +552,8 @@ fn candidate_validation_bad_return_is_invalid() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -580,7 +587,7 @@ fn perform_basic_checks_on_valid_candidate( validation_code: &ValidationCode, validation_data: &PersistedValidationData, head_data_hash: Hash, -) -> CandidateDescriptor { +) -> CandidateDescriptorV2 { let descriptor = make_valid_candidate_descriptor( ParaId::from(1_u32), dummy_hash(), @@ -590,7 +597,8 @@ fn perform_basic_checks_on_valid_candidate( head_data_hash, head_data_hash, Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -784,7 +792,8 @@ fn candidate_validation_retry_on_error_helper( dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -824,7 +833,8 @@ fn candidate_validation_timeout_is_internal_error() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -869,9 +879,11 @@ fn candidate_validation_commitment_hash_mismatch_is_invalid() { head_data.hash(), dummy_hash(), Sr25519Keyring::Alice, - ), + ) + .into(), commitments_hash: Hash::zero(), - }; + } + .into(); // This will result in different commitments for this candidate. let validation_result = WasmValidationResult { @@ -915,7 +927,8 @@ fn candidate_validation_code_mismatch_is_invalid() { dummy_hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let check = perform_basic_checks( &descriptor, @@ -970,7 +983,8 @@ fn compressed_code_works() { head_data.hash(), dummy_hash(), Sr25519Keyring::Alice, - ); + ) + .into(); let validation_result = WasmValidationResult { head_data, @@ -1239,7 +1253,8 @@ fn dummy_candidate_backed( signature: dummy_collator_signature(), para_head: zeros, validation_code_hash, - }; + } + .into(); CandidateEvent::CandidateBacked( CandidateReceipt { descriptor, commitments_hash: zeros }, diff --git a/polkadot/node/core/dispute-coordinator/Cargo.toml b/polkadot/node/core/dispute-coordinator/Cargo.toml index eb4600b235b..344b66af193 100644 --- a/polkadot/node/core/dispute-coordinator/Cargo.toml +++ b/polkadot/node/core/dispute-coordinator/Cargo.toml @@ -37,6 +37,7 @@ polkadot-primitives-test-helpers = { workspace = true } futures-timer = { workspace = true } sp-application-crypto = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, features = ["test"] } [features] # If not enabled, the dispute coordinator will do nothing. diff --git a/polkadot/node/core/dispute-coordinator/src/db/v1.rs b/polkadot/node/core/dispute-coordinator/src/db/v1.rs index 0101791550e..962dfcbbcfa 100644 --- a/polkadot/node/core/dispute-coordinator/src/db/v1.rs +++ b/polkadot/node/core/dispute-coordinator/src/db/v1.rs @@ -25,8 +25,9 @@ use polkadot_node_primitives::DisputeStatus; use polkadot_node_subsystem_util::database::{DBTransaction, Database}; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, Hash, InvalidDisputeStatementKind, SessionIndex, - ValidDisputeStatementKind, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, Hash, + InvalidDisputeStatementKind, SessionIndex, ValidDisputeStatementKind, ValidatorIndex, + ValidatorSignature, }; use std::sync::Arc; @@ -377,7 +378,9 @@ mod tests { use super::*; use polkadot_node_primitives::DISPUTE_WINDOW; use polkadot_primitives::{Hash, Id as ParaId}; - use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; + use polkadot_primitives_test_helpers::{ + dummy_candidate_receipt, dummy_candidate_receipt_v2, dummy_hash, + }; fn make_db() -> DbBackend { let db = kvdb_memorydb::create(1); @@ -403,7 +406,7 @@ mod tests { session, candidate_hash, CandidateVotes { - candidate_receipt: dummy_candidate_receipt(dummy_hash()), + candidate_receipt: dummy_candidate_receipt_v2(dummy_hash()), valid: Vec::new(), invalid: Vec::new(), }, @@ -495,7 +498,7 @@ mod tests { 1, CandidateHash(Hash::repeat_byte(1)), CandidateVotes { - candidate_receipt: dummy_candidate_receipt(dummy_hash()), + candidate_receipt: dummy_candidate_receipt_v2(dummy_hash()), valid: Vec::new(), invalid: Vec::new(), }, @@ -508,7 +511,7 @@ mod tests { let mut receipt = dummy_candidate_receipt(dummy_hash()); receipt.descriptor.para_id = ParaId::from(5_u32); - receipt + receipt.into() }, valid: Vec::new(), invalid: Vec::new(), @@ -532,7 +535,7 @@ mod tests { .unwrap() .candidate_receipt .descriptor - .para_id, + .para_id(), ParaId::from(5), ); @@ -556,7 +559,7 @@ mod tests { .unwrap() .candidate_receipt .descriptor - .para_id, + .para_id(), ParaId::from(5), ); } @@ -571,13 +574,13 @@ mod tests { 1, CandidateHash(Hash::repeat_byte(1)), CandidateVotes { - candidate_receipt: dummy_candidate_receipt(Hash::random()), + candidate_receipt: dummy_candidate_receipt_v2(Hash::random()), valid: Vec::new(), invalid: Vec::new(), }, ); - let receipt = dummy_candidate_receipt(dummy_hash()); + let receipt = dummy_candidate_receipt_v2(dummy_hash()); overlay_db.write_candidate_votes( 1, @@ -621,7 +624,7 @@ mod tests { let very_recent = current_session - 1; let blank_candidate_votes = || CandidateVotes { - candidate_receipt: dummy_candidate_receipt(dummy_hash()), + candidate_receipt: dummy_candidate_receipt_v2(dummy_hash()), valid: Vec::new(), invalid: Vec::new(), }; diff --git a/polkadot/node/core/dispute-coordinator/src/import.rs b/polkadot/node/core/dispute-coordinator/src/import.rs index d3a4625f0d2..4263dda54b9 100644 --- a/polkadot/node/core/dispute-coordinator/src/import.rs +++ b/polkadot/node/core/dispute-coordinator/src/import.rs @@ -34,9 +34,9 @@ use polkadot_node_primitives::{ use polkadot_node_subsystem::overseer; use polkadot_node_subsystem_util::runtime::RuntimeInfo; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, DisputeStatement, ExecutorParams, Hash, IndexedVec, - SessionIndex, SessionInfo, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, - ValidatorPair, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, DisputeStatement, + ExecutorParams, Hash, IndexedVec, SessionIndex, SessionInfo, ValidDisputeStatementKind, + ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, }; use sc_keystore::LocalKeystore; diff --git a/polkadot/node/core/dispute-coordinator/src/initialized.rs b/polkadot/node/core/dispute-coordinator/src/initialized.rs index 9cf9047b727..7fc22d5904c 100644 --- a/polkadot/node/core/dispute-coordinator/src/initialized.rs +++ b/polkadot/node/core/dispute-coordinator/src/initialized.rs @@ -44,9 +44,10 @@ use polkadot_node_subsystem_util::runtime::{ self, key_ownership_proof, submit_report_dispute_lost, RuntimeInfo, }; use polkadot_primitives::{ - slashing, BlockNumber, CandidateHash, CandidateReceipt, CompactStatement, DisputeStatement, - DisputeStatementSet, Hash, ScrapedOnChainVotes, SessionIndex, ValidDisputeStatementKind, - ValidatorId, ValidatorIndex, + slashing, + vstaging::{CandidateReceiptV2 as CandidateReceipt, ScrapedOnChainVotes}, + BlockNumber, CandidateHash, CompactStatement, DisputeStatement, DisputeStatementSet, Hash, + SessionIndex, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, }; use schnellru::{LruMap, UnlimitedCompact}; @@ -607,7 +608,7 @@ impl Initialized { // the new active leaf as if we received them via gossip. for (candidate_receipt, backers) in backing_validators_per_candidate { // Obtain the session info, for sake of `ValidatorId`s - let relay_parent = candidate_receipt.descriptor.relay_parent; + let relay_parent = candidate_receipt.descriptor.relay_parent(); let session_info = match self .runtime_info .get_session_info_by_index(ctx.sender(), relay_parent, session) @@ -958,9 +959,9 @@ impl Initialized { let votes_in_db = overlay_db.load_candidate_votes(session, &candidate_hash)?; let relay_parent = match &candidate_receipt { MaybeCandidateReceipt::Provides(candidate_receipt) => - candidate_receipt.descriptor().relay_parent, + candidate_receipt.descriptor().relay_parent(), MaybeCandidateReceipt::AssumeBackingVotePresent(candidate_hash) => match &votes_in_db { - Some(votes) => votes.candidate_receipt.descriptor().relay_parent, + Some(votes) => votes.candidate_receipt.descriptor().relay_parent(), None => { gum::warn!( target: LOG_TARGET, @@ -1451,7 +1452,7 @@ impl Initialized { ctx, &mut self.runtime_info, session, - candidate_receipt.descriptor.relay_parent, + candidate_receipt.descriptor.relay_parent(), self.offchain_disabled_validators.iter(session), ) .await diff --git a/polkadot/node/core/dispute-coordinator/src/lib.rs b/polkadot/node/core/dispute-coordinator/src/lib.rs index 84408eb9630..3078ada5d53 100644 --- a/polkadot/node/core/dispute-coordinator/src/lib.rs +++ b/polkadot/node/core/dispute-coordinator/src/lib.rs @@ -46,7 +46,7 @@ use polkadot_node_subsystem_util::{ runtime::{Config as RuntimeInfoConfig, RuntimeInfo}, }; use polkadot_primitives::{ - DisputeStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidatorIndex, + vstaging::ScrapedOnChainVotes, DisputeStatement, SessionIndex, SessionInfo, ValidatorIndex, }; use crate::{ diff --git a/polkadot/node/core/dispute-coordinator/src/participation/mod.rs b/polkadot/node/core/dispute-coordinator/src/participation/mod.rs index 2220f65e20a..770c44f7d60 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/mod.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/mod.rs @@ -31,7 +31,10 @@ use polkadot_node_subsystem::{ overseer, ActiveLeavesUpdate, RecoveryError, }; use polkadot_node_subsystem_util::runtime::get_validation_code_by_hash; -use polkadot_primitives::{BlockNumber, CandidateHash, CandidateReceipt, Hash, SessionIndex}; +use polkadot_primitives::{ + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, Hash, + SessionIndex, +}; use crate::LOG_TARGET; @@ -348,7 +351,7 @@ async fn participate( let validation_code = match get_validation_code_by_hash( &mut sender, block_hash, - req.candidate_receipt().descriptor.validation_code_hash, + req.candidate_receipt().descriptor.validation_code_hash(), ) .await { @@ -357,7 +360,7 @@ async fn participate( gum::warn!( target: LOG_TARGET, "Validation code unavailable for code hash {:?} in the state of block {:?}", - req.candidate_receipt().descriptor.validation_code_hash, + req.candidate_receipt().descriptor.validation_code_hash(), block_hash, ); diff --git a/polkadot/node/core/dispute-coordinator/src/participation/queues/mod.rs b/polkadot/node/core/dispute-coordinator/src/participation/queues/mod.rs index d9e86def168..4d317d38590 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/queues/mod.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/queues/mod.rs @@ -22,7 +22,8 @@ use std::{ use futures::channel::oneshot; use polkadot_node_subsystem::{messages::ChainApiMessage, overseer}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, ExecutorParams, Hash, SessionIndex, + vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, ExecutorParams, + Hash, SessionIndex, }; use crate::{ @@ -405,7 +406,7 @@ impl CandidateComparator { candidate: &CandidateReceipt, ) -> FatalResult { let candidate_hash = candidate.hash(); - let n = get_block_number(sender, candidate.descriptor().relay_parent).await?; + let n = get_block_number(sender, candidate.descriptor().relay_parent()).await?; if n.is_none() { gum::warn!( diff --git a/polkadot/node/core/dispute-coordinator/src/participation/queues/tests.rs b/polkadot/node/core/dispute-coordinator/src/participation/queues/tests.rs index 9176d00b2f5..a25387a7eb5 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/queues/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/queues/tests.rs @@ -17,13 +17,13 @@ use crate::{metrics::Metrics, ParticipationPriority}; use assert_matches::assert_matches; use polkadot_primitives::{BlockNumber, Hash}; -use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; +use polkadot_primitives_test_helpers::{dummy_candidate_receipt_v2, dummy_hash}; use super::{CandidateComparator, ParticipationRequest, QueueError, Queues}; /// Make a `ParticipationRequest` based on the given commitments hash. fn make_participation_request(hash: Hash) -> ParticipationRequest { - let mut receipt = dummy_candidate_receipt(dummy_hash()); + let mut receipt = dummy_candidate_receipt_v2(dummy_hash()); // make it differ: receipt.commitments_hash = hash; let request_timer = Metrics::default().time_participation_pipeline(); diff --git a/polkadot/node/core/dispute-coordinator/src/participation/tests.rs b/polkadot/node/core/dispute-coordinator/src/participation/tests.rs index a6ab6f16df0..23f7984965b 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/tests.rs @@ -68,7 +68,8 @@ async fn participate_with_commitments_hash( let mut receipt = dummy_candidate_receipt_bad_sig(dummy_hash(), dummy_hash()); receipt.commitments_hash = commitments_hash; receipt - }; + } + .into(); let session = 1; let request_timer = participation.metrics.time_participation_pipeline(); diff --git a/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs b/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs index 4c45d9dcc22..9aaad9d1c52 100644 --- a/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs +++ b/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs @@ -28,8 +28,9 @@ use polkadot_node_subsystem_util::runtime::{ self, get_candidate_events, get_on_chain_votes, get_unapplied_slashes, }; use polkadot_primitives::{ - slashing::PendingSlashes, BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, Hash, - ScrapedOnChainVotes, SessionIndex, + slashing::PendingSlashes, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt, ScrapedOnChainVotes}, + BlockNumber, CandidateHash, Hash, SessionIndex, }; use crate::{ diff --git a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs index ed2400387ef..fe04193014c 100644 --- a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs @@ -36,8 +36,9 @@ use polkadot_node_subsystem_test_helpers::{ }; use polkadot_node_subsystem_util::{reexports::SubsystemContext, TimeoutExt}; use polkadot_primitives::{ - BlakeTwo256, BlockNumber, CandidateDescriptor, CandidateEvent, CandidateReceipt, CoreIndex, - GroupIndex, Hash, HashT, HeadData, Id as ParaId, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlakeTwo256, BlockNumber, CandidateDescriptor, CoreIndex, GroupIndex, Hash, HashT, HeadData, + Id as ParaId, }; use polkadot_primitives_test_helpers::{dummy_collator, dummy_collator_signature, dummy_hash}; @@ -135,7 +136,8 @@ fn make_candidate_receipt(relay_parent: Hash) -> CandidateReceipt { signature: dummy_collator_signature(), para_head: zeros, validation_code_hash: zeros.into(), - }; + } + .into(); CandidateReceipt { descriptor, commitments_hash: zeros } } diff --git a/polkadot/node/core/dispute-coordinator/src/tests.rs b/polkadot/node/core/dispute-coordinator/src/tests.rs index 48762a1d80b..9383f71804e 100644 --- a/polkadot/node/core/dispute-coordinator/src/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/tests.rs @@ -60,13 +60,18 @@ use polkadot_node_subsystem_test_helpers::{ make_buffered_subsystem_context, mock::new_leaf, TestSubsystemContextHandle, }; use polkadot_primitives::{ - ApprovalVote, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, - CandidateReceipt, CoreIndex, DisputeStatement, ExecutorParams, GroupIndex, Hash, HeadData, - Header, IndexedVec, MultiDisputeStatementSet, NodeFeatures, ScrapedOnChainVotes, SessionIndex, - SessionInfo, SigningContext, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, - ValidatorSignature, + vstaging::{ + CandidateEvent, CandidateReceiptV2 as CandidateReceipt, MutateDescriptorV2, + ScrapedOnChainVotes, + }, + ApprovalVote, BlockNumber, CandidateCommitments, CandidateHash, CoreIndex, DisputeStatement, + ExecutorParams, GroupIndex, Hash, HeadData, Header, IndexedVec, MultiDisputeStatementSet, + NodeFeatures, SessionIndex, SessionInfo, SigningContext, ValidDisputeStatementKind, + ValidatorId, ValidatorIndex, ValidatorSignature, +}; +use polkadot_primitives_test_helpers::{ + dummy_candidate_receipt_v2_bad_sig, dummy_digest, dummy_hash, }; -use polkadot_primitives_test_helpers::{dummy_candidate_receipt_bad_sig, dummy_digest, dummy_hash}; use crate::{ backend::Backend, @@ -648,11 +653,11 @@ fn make_valid_candidate_receipt() -> CandidateReceipt { } fn make_invalid_candidate_receipt() -> CandidateReceipt { - dummy_candidate_receipt_bad_sig(Default::default(), Some(Default::default())) + dummy_candidate_receipt_v2_bad_sig(Default::default(), Some(Default::default())) } fn make_another_valid_candidate_receipt(relay_parent: Hash) -> CandidateReceipt { - let mut candidate_receipt = dummy_candidate_receipt_bad_sig(relay_parent, dummy_hash()); + let mut candidate_receipt = dummy_candidate_receipt_v2_bad_sig(relay_parent, dummy_hash()); candidate_receipt.commitments_hash = CandidateCommitments::default().hash(); candidate_receipt } @@ -3858,14 +3863,15 @@ fn participation_requests_reprioritized_for_newly_included() { for repetition in 1..=3u8 { // Building candidate receipts let mut candidate_receipt = make_valid_candidate_receipt(); - candidate_receipt.descriptor.pov_hash = Hash::from( + candidate_receipt.descriptor.set_pov_hash(Hash::from( [repetition; 32], // Altering this receipt so its hash will be changed - ); + )); // Set consecutive parents (starting from zero). They will order the candidates for // participation. let parent_block_num: BlockNumber = repetition as BlockNumber - 1; - candidate_receipt.descriptor.relay_parent = - *test_state.block_num_to_header.get(&parent_block_num).unwrap(); + candidate_receipt.descriptor.set_relay_parent( + *test_state.block_num_to_header.get(&parent_block_num).unwrap(), + ); receipts.push(candidate_receipt.clone()); } diff --git a/polkadot/node/core/parachains-inherent/src/lib.rs b/polkadot/node/core/parachains-inherent/src/lib.rs index 1de3cab32be..5f3092f6a88 100644 --- a/polkadot/node/core/parachains-inherent/src/lib.rs +++ b/polkadot/node/core/parachains-inherent/src/lib.rs @@ -29,7 +29,7 @@ use futures::{select, FutureExt}; use polkadot_node_subsystem::{ errors::SubsystemError, messages::ProvisionerMessage, overseer::Handle, }; -use polkadot_primitives::{Block, Hash, InherentData as ParachainsInherentData}; +use polkadot_primitives::{vstaging::InherentData as ParachainsInherentData, Block, Hash}; use std::{sync::Arc, time}; pub(crate) const LOG_TARGET: &str = "parachain::parachains-inherent"; diff --git a/polkadot/node/core/prospective-parachains/Cargo.toml b/polkadot/node/core/prospective-parachains/Cargo.toml index 705014e67a0..5629e4ef7fb 100644 --- a/polkadot/node/core/prospective-parachains/Cargo.toml +++ b/polkadot/node/core/prospective-parachains/Cargo.toml @@ -23,6 +23,7 @@ polkadot-node-subsystem-util = { workspace = true, default-features = true } assert_matches = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } sp-tracing = { workspace = true } sp-core = { workspace = true, default-features = true } rand = { workspace = true } diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs index b060897d439..265d1498ee9 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs @@ -136,8 +136,9 @@ use polkadot_node_subsystem_util::inclusion_emulator::{ ProspectiveCandidate, RelayChainBlockInfo, }; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CandidateHash, CommittedCandidateReceipt, Hash, HeadData, - PersistedValidationData, ValidationCodeHash, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, BlockNumber, + CandidateCommitments, CandidateHash, Hash, HeadData, PersistedValidationData, + ValidationCodeHash, }; use thiserror::Error; @@ -371,7 +372,8 @@ impl CandidateEntry { persisted_validation_data: PersistedValidationData, state: CandidateState, ) -> Result { - if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash { + if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash() + { return Err(CandidateEntryError::PersistedValidationDataMismatch) } @@ -386,13 +388,13 @@ impl CandidateEntry { candidate_hash, parent_head_data_hash, output_head_data_hash, - relay_parent: candidate.descriptor.relay_parent, + relay_parent: candidate.descriptor.relay_parent(), state, candidate: Arc::new(ProspectiveCandidate { commitments: candidate.commitments, persisted_validation_data, - pov_hash: candidate.descriptor.pov_hash, - validation_code_hash: candidate.descriptor.validation_code_hash, + pov_hash: candidate.descriptor.pov_hash(), + validation_code_hash: candidate.descriptor.validation_code_hash(), }), }) } @@ -407,8 +409,8 @@ impl HypotheticalOrConcreteCandidate for CandidateEntry { Some(&self.candidate.persisted_validation_data) } - fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { - Some(&self.candidate.validation_code_hash) + fn validation_code_hash(&self) -> Option { + Some(self.candidate.validation_code_hash) } fn parent_head_data_hash(&self) -> Hash { @@ -1090,7 +1092,7 @@ impl FragmentChain { &relay_parent, &constraints, commitments, - validation_code_hash, + &validation_code_hash, pvd, ) .map_err(Error::CheckAgainstConstraints)?; 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 3332cbeb03c..9708b0871c2 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs @@ -18,7 +18,8 @@ use super::*; use assert_matches::assert_matches; use polkadot_node_subsystem_util::inclusion_emulator::InboundHrmpLimitations; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData, Id as ParaId, + vstaging::MutateDescriptorV2, BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData, + Id as ParaId, }; use polkadot_primitives_test_helpers as test_helpers; use rand::{seq::SliceRandom, thread_rng}; @@ -73,7 +74,8 @@ fn make_committed_candidate( signature: test_helpers::dummy_collator_signature(), para_head: para_head.hash(), validation_code_hash: Hash::repeat_byte(42).into(), - }, + } + .into(), commitments: CandidateCommitments { upward_messages: Default::default(), horizontal_messages: Default::default(), @@ -283,7 +285,7 @@ fn candidate_storage_methods() { candidate.commitments.head_data = HeadData(vec![1; 10]); let mut pvd = pvd.clone(); pvd.parent_head = HeadData(vec![1; 10]); - candidate.descriptor.persisted_validation_data_hash = pvd.hash(); + candidate.descriptor.set_persisted_validation_data_hash(pvd.hash()); assert_matches!( CandidateEntry::new_seconded(candidate_hash, candidate, pvd), Err(CandidateEntryError::ZeroLengthCycle) @@ -291,7 +293,7 @@ fn candidate_storage_methods() { } assert!(!storage.contains(&candidate_hash)); assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); + assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head()), None); assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); // Add a valid candidate. @@ -305,9 +307,9 @@ fn candidate_storage_methods() { storage.add_candidate_entry(candidate_entry.clone()).unwrap(); assert!(storage.contains(&candidate_hash)); assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head()).count(), 0); assert_eq!( - storage.head_data_by_hash(&candidate.descriptor.para_head).unwrap(), + storage.head_data_by_hash(&candidate.descriptor.para_head()).unwrap(), &candidate.commitments.head_data ); assert_eq!(storage.head_data_by_hash(&parent_head_hash).unwrap(), &pvd.parent_head); @@ -323,7 +325,7 @@ fn candidate_storage_methods() { .collect::>(), vec![candidate_hash] ); - assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head()).count(), 0); // Re-adding a candidate fails. assert_matches!( @@ -339,7 +341,7 @@ fn candidate_storage_methods() { storage.remove_candidate(&candidate_hash); assert!(!storage.contains(&candidate_hash)); assert_eq!(storage.possible_backed_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); + assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head()), None); assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); storage @@ -354,7 +356,7 @@ fn candidate_storage_methods() { .collect::>(), vec![candidate_hash] ); - assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head()).count(), 0); // Now add a second candidate in Seconded state. This will be a fork. let (pvd_2, candidate_2) = make_committed_candidate( diff --git a/polkadot/node/core/prospective-parachains/src/lib.rs b/polkadot/node/core/prospective-parachains/src/lib.rs index b8b5f159e71..34c1d8823bf 100644 --- a/polkadot/node/core/prospective-parachains/src/lib.rs +++ b/polkadot/node/core/prospective-parachains/src/lib.rs @@ -49,9 +49,11 @@ use polkadot_node_subsystem_util::{ runtime::{fetch_claim_queue, prospective_parachains_mode, ProspectiveParachainsMode}, }; use polkadot_primitives::{ - async_backing::CandidatePendingAvailability, BlockNumber, CandidateHash, - CommittedCandidateReceipt, CoreState, Hash, HeadData, Header, Id as ParaId, - PersistedValidationData, + vstaging::{ + async_backing::CandidatePendingAvailability, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + }, + BlockNumber, CandidateHash, Hash, HeadData, Header, Id as ParaId, PersistedValidationData, }; use crate::{ @@ -453,12 +455,13 @@ async fn preprocess_candidates_pending_availability( for (i, pending) in pending_availability.into_iter().enumerate() { let Some(relay_parent) = - fetch_block_info(ctx, cache, pending.descriptor.relay_parent).await? + fetch_block_info(ctx, cache, pending.descriptor.relay_parent()).await? else { + let para_id = pending.descriptor.para_id(); gum::debug!( target: LOG_TARGET, ?pending.candidate_hash, - ?pending.descriptor.para_id, + ?para_id, index = ?i, ?expected_count, "Had to stop processing pending candidates early due to missing info.", diff --git a/polkadot/node/core/prospective-parachains/src/tests.rs b/polkadot/node/core/prospective-parachains/src/tests.rs index 14a093239e8..3f1eaa4e41e 100644 --- a/polkadot/node/core/prospective-parachains/src/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/tests.rs @@ -25,9 +25,12 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_primitives::{ - async_backing::{AsyncBackingParams, BackingState, Constraints, InboundHrmpLimitations}, - CommittedCandidateReceipt, CoreIndex, HeadData, Header, PersistedValidationData, ScheduledCore, - ValidationCodeHash, + async_backing::{AsyncBackingParams, Constraints, InboundHrmpLimitations}, + vstaging::{ + async_backing::BackingState, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + MutateDescriptorV2, + }, + CoreIndex, HeadData, Header, PersistedValidationData, ScheduledCore, ValidationCodeHash, }; use polkadot_primitives_test_helpers::make_candidate; use rstest::rstest; @@ -393,15 +396,15 @@ async fn handle_leaf_activation( ); for pending in pending_availability { - if !used_relay_parents.contains(&pending.descriptor.relay_parent) { + if !used_relay_parents.contains(&pending.descriptor.relay_parent()) { send_block_header( virtual_overseer, - pending.descriptor.relay_parent, + pending.descriptor.relay_parent(), pending.relay_parent_number, ) .await; - used_relay_parents.insert(pending.descriptor.relay_parent); + used_relay_parents.insert(pending.descriptor.relay_parent()); } } } @@ -436,7 +439,7 @@ async fn introduce_seconded_candidate( pvd: PersistedValidationData, ) { let req = IntroduceSecondedCandidateRequest { - candidate_para: candidate.descriptor().para_id, + candidate_para: candidate.descriptor.para_id(), candidate_receipt: candidate, persisted_validation_data: pvd, }; @@ -455,7 +458,7 @@ async fn introduce_seconded_candidate_failed( pvd: PersistedValidationData, ) { let req = IntroduceSecondedCandidateRequest { - candidate_para: candidate.descriptor().para_id, + candidate_para: candidate.descriptor.para_id(), candidate_receipt: candidate, persisted_validation_data: pvd, }; @@ -476,7 +479,7 @@ async fn back_candidate( virtual_overseer .send(overseer::FromOrchestra::Communication { msg: ProspectiveParachainsMessage::CandidateBacked( - candidate.descriptor.para_id, + candidate.descriptor.para_id(), candidate_hash, ), }) @@ -568,7 +571,7 @@ macro_rules! make_and_back_candidate { $test_state.validation_code_hash, ); // Set a field to make this candidate unique. - candidate.descriptor.para_head = Hash::from_low_u64_le($index); + candidate.descriptor.set_para_head(Hash::from_low_u64_le($index)); let candidate_hash = candidate.hash(); introduce_seconded_candidate(&mut $virtual_overseer, candidate.clone(), pvd).await; back_candidate(&mut $virtual_overseer, &candidate, candidate_hash).await; @@ -1378,7 +1381,7 @@ fn check_backable_query_single_candidate() { test_state.validation_code_hash, ); // Set a field to make this candidate unique. - candidate_b.descriptor.para_head = Hash::from_low_u64_le(1000); + candidate_b.descriptor.set_para_head(Hash::from_low_u64_le(1000)); let candidate_hash_b = candidate_b.hash(); // Introduce candidates. diff --git a/polkadot/node/core/provisioner/Cargo.toml b/polkadot/node/core/provisioner/Cargo.toml index 5869e494c70..64a598b420f 100644 --- a/polkadot/node/core/provisioner/Cargo.toml +++ b/polkadot/node/core/provisioner/Cargo.toml @@ -27,4 +27,6 @@ sp-application-crypto = { workspace = true, default-features = true } sp-keystore = { workspace = true, default-features = true } polkadot-node-subsystem-test-helpers = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } + rstest = { workspace = true } diff --git a/polkadot/node/core/provisioner/src/disputes/prioritized_selection/tests.rs b/polkadot/node/core/provisioner/src/disputes/prioritized_selection/tests.rs index ecb7aac7839..8c0d478b67d 100644 --- a/polkadot/node/core/provisioner/src/disputes/prioritized_selection/tests.rs +++ b/polkadot/node/core/provisioner/src/disputes/prioritized_selection/tests.rs @@ -427,7 +427,7 @@ impl TestDisputes { let onchain_votes_count = self.validators_count * 80 / 100; let session_idx = 0; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Active); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -445,7 +445,7 @@ impl TestDisputes { let onchain_votes_count = self.validators_count * 40 / 100; let session_idx = 1; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Active); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -462,7 +462,7 @@ impl TestDisputes { let local_votes_count = self.validators_count * 90 / 100; let session_idx = 2; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Confirmed); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -478,7 +478,7 @@ impl TestDisputes { let onchain_votes_count = self.validators_count * 75 / 100; let session_idx = 3; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::ConcludedFor(0)); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -494,7 +494,7 @@ impl TestDisputes { let local_votes_count = self.validators_count * 90 / 100; let session_idx = 4; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::ConcludedFor(0)); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -510,7 +510,7 @@ impl TestDisputes { let onchain_votes_count = self.validators_count * 10 / 100; let session_idx = 5; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Active); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); @@ -527,7 +527,7 @@ impl TestDisputes { let local_votes_count = self.validators_count * 10 / 100; let session_idx = 6; let lf = leaf(); - let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt(lf.hash); + let dummy_receipt = polkadot_primitives_test_helpers::dummy_candidate_receipt_v2(lf.hash); for _ in 0..dispute_count { let d = (session_idx, CandidateHash(Hash::random()), DisputeStatus::Active); self.add_offchain_dispute(d, local_votes_count, dummy_receipt.clone()); diff --git a/polkadot/node/core/provisioner/src/lib.rs b/polkadot/node/core/provisioner/src/lib.rs index 9a06d9cff0c..a95df6c5f88 100644 --- a/polkadot/node/core/provisioner/src/lib.rs +++ b/polkadot/node/core/provisioner/src/lib.rs @@ -41,9 +41,10 @@ use polkadot_node_subsystem_util::{ TimeoutExt, }; use polkadot_primitives::{ - node_features::FeatureIndex, BackedCandidate, BlockNumber, CandidateHash, CandidateReceipt, - CoreIndex, CoreState, Hash, Id as ParaId, NodeFeatures, OccupiedCoreAssumption, SessionIndex, - SignedAvailabilityBitfield, ValidatorIndex, + node_features::FeatureIndex, + vstaging::{BackedCandidate, CandidateReceiptV2 as CandidateReceipt, CoreState}, + BlockNumber, CandidateHash, CoreIndex, Hash, Id as ParaId, NodeFeatures, + OccupiedCoreAssumption, SessionIndex, SignedAvailabilityBitfield, ValidatorIndex, }; use std::collections::{BTreeMap, HashMap}; @@ -361,10 +362,9 @@ fn note_provisionable_data( gum::trace!( target: LOG_TARGET, ?candidate_hash, - para = ?backed_candidate.descriptor().para_id, + para = ?backed_candidate.descriptor().para_id(), "noted backed candidate", ); - per_relay_parent.backed_candidates.push(backed_candidate); }, // We choose not to punish these forms of misbehavior for the time being. @@ -650,22 +650,22 @@ async fn select_candidate_hashes_from_tracked( // selection criteria if let Some(candidate) = candidates.iter().find(|backed_candidate| { let descriptor = &backed_candidate.descriptor; - descriptor.para_id == scheduled_core.para_id && - descriptor.persisted_validation_data_hash == computed_validation_data_hash + descriptor.para_id() == scheduled_core.para_id && + descriptor.persisted_validation_data_hash() == computed_validation_data_hash }) { let candidate_hash = candidate.hash(); gum::trace!( target: LOG_TARGET, leaf_hash=?relay_parent, ?candidate_hash, - para = ?candidate.descriptor.para_id, + para = ?candidate.descriptor.para_id(), core = core_idx, "Selected candidate receipt", ); selected_candidates.insert( - candidate.descriptor.para_id, - vec![(candidate_hash, candidate.descriptor.relay_parent)], + candidate.descriptor.para_id(), + vec![(candidate_hash, candidate.descriptor.relay_parent())], ); } } diff --git a/polkadot/node/core/provisioner/src/tests.rs b/polkadot/node/core/provisioner/src/tests.rs index b38459302c8..a09b243f3ab 100644 --- a/polkadot/node/core/provisioner/src/tests.rs +++ b/polkadot/node/core/provisioner/src/tests.rs @@ -16,14 +16,17 @@ use super::*; use bitvec::bitvec; -use polkadot_primitives::{OccupiedCore, ScheduledCore}; -use polkadot_primitives_test_helpers::{dummy_candidate_descriptor, dummy_hash}; +use polkadot_primitives::{ + vstaging::{MutateDescriptorV2, OccupiedCore}, + ScheduledCore, +}; +use polkadot_primitives_test_helpers::{dummy_candidate_descriptor_v2, dummy_hash}; const MOCK_GROUP_SIZE: usize = 5; pub fn occupied_core(para_id: u32) -> CoreState { - let mut candidate_descriptor = dummy_candidate_descriptor(dummy_hash()); - candidate_descriptor.para_id = para_id.into(); + let mut candidate_descriptor = dummy_candidate_descriptor_v2(dummy_hash()); + candidate_descriptor.set_para_id(para_id.into()); CoreState::Occupied(OccupiedCore { group_responsible: para_id.into(), @@ -32,7 +35,7 @@ pub fn occupied_core(para_id: u32) -> CoreState { time_out_at: 200_u32, next_up_on_time_out: None, availability: bitvec![u8, bitvec::order::Lsb0; 0; 32], - candidate_descriptor, + candidate_descriptor: candidate_descriptor.into(), candidate_hash: Default::default(), }) } @@ -254,9 +257,10 @@ mod select_candidates { use polkadot_node_subsystem_test_helpers::TestSubsystemSender; use polkadot_node_subsystem_util::runtime::ProspectiveParachainsMode; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CommittedCandidateReceipt, PersistedValidationData, + vstaging::{CommittedCandidateReceiptV2 as CommittedCandidateReceipt, MutateDescriptorV2}, + BlockNumber, CandidateCommitments, PersistedValidationData, }; - use polkadot_primitives_test_helpers::{dummy_candidate_descriptor, dummy_hash}; + use polkadot_primitives_test_helpers::{dummy_candidate_descriptor_v2, dummy_hash}; use rstest::rstest; use std::ops::Not; use CoreState::{Free, Scheduled}; @@ -266,8 +270,8 @@ mod select_candidates { fn dummy_candidate_template() -> CandidateReceipt { let empty_hash = PersistedValidationData::::default().hash(); - let mut descriptor_template = dummy_candidate_descriptor(dummy_hash()); - descriptor_template.persisted_validation_data_hash = empty_hash; + let mut descriptor_template = dummy_candidate_descriptor_v2(dummy_hash()); + descriptor_template.set_persisted_validation_data_hash(empty_hash); CandidateReceipt { descriptor: descriptor_template, commitments_hash: CandidateCommitments::default().hash(), @@ -283,7 +287,7 @@ mod select_candidates { .take(core_count) .enumerate() .map(|(idx, mut candidate)| { - candidate.descriptor.para_id = idx.into(); + candidate.descriptor.set_para_id(idx.into()); candidate }) .collect(); @@ -559,14 +563,14 @@ mod select_candidates { use RuntimeApiMessage::Request; let mut backed = expected.clone().into_iter().fold(HashMap::new(), |mut acc, candidate| { - acc.entry(candidate.descriptor().para_id).or_insert(vec![]).push(candidate); + acc.entry(candidate.descriptor().para_id()).or_insert(vec![]).push(candidate); acc }); - expected.sort_by_key(|c| c.candidate().descriptor.para_id); + expected.sort_by_key(|c| c.candidate().descriptor.para_id()); let mut candidates_iter = expected .iter() - .map(|candidate| (candidate.hash(), candidate.descriptor().relay_parent)); + .map(|candidate| (candidate.hash(), candidate.descriptor().relay_parent())); while let Some(from_job) = receiver.next().await { match from_job { @@ -601,7 +605,7 @@ mod select_candidates { candidates .iter() .map(|candidate| { - (candidate.hash(), candidate.descriptor().relay_parent) + (candidate.hash(), candidate.descriptor().relay_parent()) }) .collect(), ) @@ -707,7 +711,7 @@ mod select_candidates { .take(mock_cores.len()) .enumerate() .map(|(idx, mut candidate)| { - candidate.descriptor.para_id = idx.into(); + candidate.descriptor.set_para_id(idx.into()); candidate }) .cycle() @@ -719,11 +723,11 @@ mod select_candidates { candidate } else if idx < mock_cores.len() * 2 { // for the second repetition of the candidates, give them the wrong hash - candidate.descriptor.persisted_validation_data_hash = Default::default(); + candidate.descriptor.set_persisted_validation_data_hash(Default::default()); candidate } else { // third go-around: right hash, wrong para_id - candidate.descriptor.para_id = idx.into(); + candidate.descriptor.set_para_id(idx.into()); candidate } }) @@ -807,9 +811,9 @@ mod select_candidates { let committed_receipts: Vec<_> = (0..=mock_cores.len()) .map(|i| { - let mut descriptor = dummy_candidate_descriptor(dummy_hash()); - descriptor.para_id = i.into(); - descriptor.persisted_validation_data_hash = empty_hash; + let mut descriptor = dummy_candidate_descriptor_v2(dummy_hash()); + descriptor.set_para_id(i.into()); + descriptor.set_persisted_validation_data_hash(empty_hash); CommittedCandidateReceipt { descriptor, commitments: CandidateCommitments { @@ -917,14 +921,14 @@ mod select_candidates { let committed_receipts: Vec<_> = (0..mock_cores.len()) .map(|i| { - let mut descriptor = dummy_candidate_descriptor(dummy_hash()); - descriptor.para_id = if let Scheduled(scheduled_core) = &mock_cores[i] { + let mut descriptor = dummy_candidate_descriptor_v2(dummy_hash()); + descriptor.set_para_id(if let Scheduled(scheduled_core) = &mock_cores[i] { scheduled_core.para_id } else { panic!("`mock_cores` is not initialized with `Scheduled`?") - }; - descriptor.persisted_validation_data_hash = empty_hash; - descriptor.pov_hash = Hash::from_low_u64_be(i as u64); + }); + descriptor.set_persisted_validation_data_hash(empty_hash); + descriptor.set_pov_hash(Hash::from_low_u64_be(i as u64)); CommittedCandidateReceipt { descriptor, commitments: CandidateCommitments { @@ -1222,8 +1226,8 @@ mod select_candidates { .take(mock_cores.len() + 1) .enumerate() .map(|(idx, mut candidate)| { - candidate.descriptor.para_id = idx.into(); - candidate.descriptor.relay_parent = Hash::repeat_byte(idx as u8); + candidate.descriptor.set_para_id(idx.into()); + candidate.descriptor.set_relay_parent(Hash::repeat_byte(idx as u8)); candidate }) .collect(); diff --git a/polkadot/node/core/runtime-api/src/cache.rs b/polkadot/node/core/runtime-api/src/cache.rs index 05efbc533d0..7246010711e 100644 --- a/polkadot/node/core/runtime-api/src/cache.rs +++ b/polkadot/node/core/runtime-api/src/cache.rs @@ -20,12 +20,16 @@ use schnellru::{ByLength, LruMap}; use sp_consensus_babe::Epoch; use polkadot_primitives::{ - async_backing, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, + async_backing, slashing, vstaging, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateHash, + CoreIndex, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, - PersistedValidationData, ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, - ValidationCodeHash, ValidatorId, ValidatorIndex, + PersistedValidationData, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, + ValidatorId, ValidatorIndex, }; /// For consistency we have the same capacity for all caches. We use 128 as we'll only need that @@ -66,7 +70,7 @@ pub(crate) struct RequestResultCache { key_ownership_proof: LruMap<(Hash, ValidatorId), Option>, minimum_backing_votes: LruMap, disabled_validators: LruMap>, - para_backing_state: LruMap<(Hash, ParaId), Option>, + para_backing_state: LruMap<(Hash, ParaId), Option>, async_backing_params: LruMap, node_features: LruMap, approval_voting_params: LruMap, @@ -499,14 +503,14 @@ impl RequestResultCache { pub(crate) fn para_backing_state( &mut self, key: (Hash, ParaId), - ) -> Option<&Option> { + ) -> Option<&Option> { self.para_backing_state.get(&key).map(|v| &*v) } pub(crate) fn cache_para_backing_state( &mut self, key: (Hash, ParaId), - value: Option, + value: Option, ) { self.para_backing_state.insert(key, value); } @@ -601,7 +605,7 @@ pub(crate) enum RequestResult { SubmitReportDisputeLost(Option<()>), ApprovalVotingParams(Hash, SessionIndex, ApprovalVotingParams), DisabledValidators(Hash, Vec), - ParaBackingState(Hash, ParaId, Option), + ParaBackingState(Hash, ParaId, Option), AsyncBackingParams(Hash, async_backing::AsyncBackingParams), NodeFeatures(SessionIndex, NodeFeatures), ClaimQueue(Hash, BTreeMap>), diff --git a/polkadot/node/core/runtime-api/src/tests.rs b/polkadot/node/core/runtime-api/src/tests.rs index 7c382707264..d4fa0732388 100644 --- a/polkadot/node/core/runtime-api/src/tests.rs +++ b/polkadot/node/core/runtime-api/src/tests.rs @@ -20,14 +20,20 @@ use polkadot_node_primitives::{BabeAllowedSlots, BabeEpoch, BabeEpochConfigurati use polkadot_node_subsystem::SpawnGlue; use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_primitives::{ - async_backing, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Id as ParaId, + async_backing, slashing, vstaging, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateHash, + CoreIndex, DisputeState, ExecutorParams, GroupRotationInfo, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, - PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, - Slot, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, Slot, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, +}; +use polkadot_primitives_test_helpers::{ + dummy_committed_candidate_receipt_v2, dummy_validation_code, }; -use polkadot_primitives_test_helpers::{dummy_committed_candidate_receipt, dummy_validation_code}; use sp_api::ApiError; use sp_core::testing::TaskExecutor; use std::{ @@ -279,7 +285,7 @@ impl RuntimeApiSubsystemClient for MockSubsystemClient { &self, _: Hash, _: ParaId, - ) -> Result, ApiError> { + ) -> Result, ApiError> { todo!("Not required for tests") } @@ -699,7 +705,7 @@ fn requests_candidate_pending_availability() { let para_a = ParaId::from(5_u32); let para_b = ParaId::from(6_u32); let spawner = sp_core::testing::TaskExecutor::new(); - let candidate_receipt = dummy_committed_candidate_receipt(relay_parent); + let candidate_receipt = dummy_committed_candidate_receipt_v2(relay_parent); let mut subsystem_client = MockSubsystemClient::default(); subsystem_client diff --git a/polkadot/node/malus/src/variants/common.rs b/polkadot/node/malus/src/variants/common.rs index 66926f48c5e..7415e6c79df 100644 --- a/polkadot/node/malus/src/variants/common.rs +++ b/polkadot/node/malus/src/variants/common.rs @@ -24,8 +24,10 @@ use crate::{ use polkadot_node_primitives::{InvalidCandidate, ValidationResult}; use polkadot_primitives::{ - CandidateCommitments, CandidateDescriptor, CandidateReceipt, PersistedValidationData, - PvfExecKind, + vstaging::{ + CandidateDescriptorV2 as CandidateDescriptor, CandidateReceiptV2 as CandidateReceipt, + }, + CandidateCommitments, PersistedValidationData, PvfExecKind, }; use futures::channel::oneshot; @@ -203,7 +205,7 @@ fn create_validation_response( gum::debug!( target: MALUS, - para_id = ?candidate_receipt.descriptor.para_id, + para_id = ?candidate_receipt.descriptor.para_id(), candidate_hash = ?candidate_receipt.hash(), "ValidationResult: {:?}", &result @@ -308,7 +310,7 @@ where gum::info!( target: MALUS, ?behave_maliciously, - para_id = ?candidate_receipt.descriptor.para_id, + para_id = ?candidate_receipt.descriptor.para_id(), "😈 Maliciously sending invalid validation result: {:?}.", &validation_result, ); diff --git a/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs b/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs index 7a95bdaead2..309be9e46d8 100644 --- a/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs +++ b/polkadot/node/malus/src/variants/dispute_finalized_candidates.rs @@ -42,7 +42,7 @@ use polkadot_cli::{ use polkadot_node_subsystem::SpawnGlue; use polkadot_node_subsystem_types::{ChainApiBackend, OverseerSignal, RuntimeApiSubsystemClient}; use polkadot_node_subsystem_util::request_candidate_events; -use polkadot_primitives::CandidateEvent; +use polkadot_primitives::vstaging::CandidateEvent; use sp_core::traits::SpawnNamed; // Filter wrapping related types. diff --git a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs index ab2d380fbaf..2fe08c8a1c4 100644 --- a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs @@ -32,7 +32,7 @@ use polkadot_cli::{ }; use polkadot_node_primitives::{AvailableData, BlockData, PoV}; use polkadot_node_subsystem_types::{ChainApiBackend, RuntimeApiSubsystemClient}; -use polkadot_primitives::{CandidateDescriptor, CandidateReceipt}; +use polkadot_primitives::{vstaging::CandidateReceiptV2, CandidateDescriptor}; use polkadot_node_subsystem_util::request_validators; use sp_core::traits::SpawnNamed; @@ -127,7 +127,7 @@ where let validation_code = { let validation_code_hash = - _candidate.descriptor().validation_code_hash; + _candidate.descriptor().validation_code_hash(); let (tx, rx) = oneshot::channel(); new_sender .send_message(RuntimeApiMessage::Request( @@ -214,7 +214,7 @@ where let collator_pair = CollatorPair::generate().0; let signature_payload = polkadot_primitives::collator_signature_payload( &relay_parent, - &candidate.descriptor().para_id, + &candidate.descriptor().para_id(), &validation_data_hash, &pov_hash, &validation_code_hash, @@ -227,9 +227,9 @@ where &malicious_available_data.validation_data, ); - let malicious_candidate = CandidateReceipt { + let malicious_candidate = CandidateReceiptV2 { descriptor: CandidateDescriptor { - para_id: candidate.descriptor().para_id, + para_id: candidate.descriptor.para_id(), relay_parent, collator: collator_id, persisted_validation_data_hash: validation_data_hash, @@ -238,7 +238,8 @@ where signature: collator_signature, para_head: malicious_commitments.head_data.hash(), validation_code_hash, - }, + } + .into(), commitments_hash: malicious_commitments.hash(), }; let malicious_candidate_hash = malicious_candidate.hash(); diff --git a/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs b/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs index 5be6f2d223a..c4654b843c4 100644 --- a/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs @@ -35,8 +35,8 @@ use polkadot_node_subsystem::{ overseer, }; use polkadot_primitives::{ - AuthorityDiscoveryId, BlakeTwo256, CandidateHash, ChunkIndex, GroupIndex, Hash, HashT, - OccupiedCore, SessionIndex, + vstaging::OccupiedCore, AuthorityDiscoveryId, BlakeTwo256, CandidateHash, ChunkIndex, + GroupIndex, Hash, HashT, SessionIndex, }; use sc_network::ProtocolName; @@ -170,8 +170,8 @@ impl FetchTaskConfig { candidate_hash: core.candidate_hash, index: session_info.our_index, }, - erasure_root: core.candidate_descriptor.erasure_root, - relay_parent: core.candidate_descriptor.relay_parent, + erasure_root: core.candidate_descriptor.erasure_root(), + relay_parent: core.candidate_descriptor.relay_parent(), metrics, sender, chunk_index, diff --git a/polkadot/node/network/availability-distribution/src/requester/mod.rs b/polkadot/node/network/availability-distribution/src/requester/mod.rs index 23382503272..613a514269e 100644 --- a/polkadot/node/network/availability-distribution/src/requester/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/mod.rs @@ -38,7 +38,7 @@ use polkadot_node_subsystem_util::{ availability_chunks::availability_chunk_index, runtime::{get_occupied_cores, RuntimeInfo}, }; -use polkadot_primitives::{CandidateHash, CoreIndex, Hash, OccupiedCore, SessionIndex}; +use polkadot_primitives::{vstaging::OccupiedCore, CandidateHash, CoreIndex, Hash, SessionIndex}; use super::{FatalError, Metrics, Result, LOG_TARGET}; diff --git a/polkadot/node/network/availability-distribution/src/requester/tests.rs b/polkadot/node/network/availability-distribution/src/requester/tests.rs index 021f6da7e2e..ebcba2a038b 100644 --- a/polkadot/node/network/availability-distribution/src/requester/tests.rs +++ b/polkadot/node/network/availability-distribution/src/requester/tests.rs @@ -21,7 +21,7 @@ use polkadot_node_network_protocol::request_response::ReqProtocolNames; use polkadot_node_primitives::{BlockData, ErasureChunk, PoV}; use polkadot_node_subsystem_util::runtime::RuntimeInfo; use polkadot_primitives::{ - BlockNumber, ChunkIndex, CoreState, ExecutorParams, GroupIndex, Hash, Id as ParaId, + vstaging::CoreState, BlockNumber, ChunkIndex, ExecutorParams, GroupIndex, Hash, Id as ParaId, ScheduledCore, SessionIndex, SessionInfo, }; use sp_core::{testing::TaskExecutor, traits::SpawnNamed}; diff --git a/polkadot/node/network/availability-distribution/src/tests/mock.rs b/polkadot/node/network/availability-distribution/src/tests/mock.rs index b41c493a107..f900cb6e615 100644 --- a/polkadot/node/network/availability-distribution/src/tests/mock.rs +++ b/polkadot/node/network/availability-distribution/src/tests/mock.rs @@ -23,8 +23,9 @@ use sp_keyring::Sr25519Keyring; use polkadot_erasure_coding::{branches, obtain_chunks_v1 as obtain_chunks}; use polkadot_node_primitives::{AvailableData, BlockData, ErasureChunk, PoV, Proof}; use polkadot_primitives::{ + vstaging::{CommittedCandidateReceiptV2, OccupiedCore}, CandidateCommitments, CandidateDescriptor, CandidateHash, ChunkIndex, - CommittedCandidateReceipt, GroupIndex, Hash, HeadData, Id as ParaId, IndexedVec, OccupiedCore, + CommittedCandidateReceipt, GroupIndex, Hash, HeadData, Id as ParaId, IndexedVec, PersistedValidationData, SessionInfo, ValidatorIndex, }; use polkadot_primitives_test_helpers::{ @@ -101,7 +102,7 @@ impl OccupiedCoreBuilder { availability: Default::default(), group_responsible: self.group_responsible, candidate_hash: candidate_receipt.hash(), - candidate_descriptor: candidate_receipt.descriptor().clone(), + candidate_descriptor: candidate_receipt.descriptor.clone(), }; (core, (candidate_receipt.hash(), chunk)) } @@ -117,7 +118,7 @@ pub struct TestCandidateBuilder { } impl TestCandidateBuilder { - pub fn build(self) -> CommittedCandidateReceipt { + pub fn build(self) -> CommittedCandidateReceiptV2 { CommittedCandidateReceipt { descriptor: CandidateDescriptor { para_id: self.para_id, @@ -132,6 +133,7 @@ impl TestCandidateBuilder { }, commitments: CandidateCommitments { head_data: self.head_data, ..Default::default() }, } + .into() } } diff --git a/polkadot/node/network/availability-distribution/src/tests/mod.rs b/polkadot/node/network/availability-distribution/src/tests/mod.rs index 078220607c3..d4abd4e32d9 100644 --- a/polkadot/node/network/availability-distribution/src/tests/mod.rs +++ b/polkadot/node/network/availability-distribution/src/tests/mod.rs @@ -22,7 +22,7 @@ use rstest::rstest; use polkadot_node_network_protocol::request_response::{ IncomingRequest, Protocol, ReqProtocolNames, }; -use polkadot_primitives::{node_features, Block, CoreState, Hash, NodeFeatures}; +use polkadot_primitives::{node_features, vstaging::CoreState, Block, Hash, NodeFeatures}; use sp_keystore::KeystorePtr; use super::*; diff --git a/polkadot/node/network/availability-distribution/src/tests/state.rs b/polkadot/node/network/availability-distribution/src/tests/state.rs index 53d6fd2c530..c6dd17a344e 100644 --- a/polkadot/node/network/availability-distribution/src/tests/state.rs +++ b/polkadot/node/network/availability-distribution/src/tests/state.rs @@ -47,7 +47,7 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_primitives::{ - CandidateHash, ChunkIndex, CoreIndex, CoreState, ExecutorParams, GroupIndex, Hash, + vstaging::CoreState, CandidateHash, ChunkIndex, CoreIndex, ExecutorParams, GroupIndex, Hash, Id as ParaId, NodeFeatures, ScheduledCore, SessionInfo, ValidatorIndex, }; use test_helpers::mock::{make_ferdie_keystore, new_leaf}; diff --git a/polkadot/node/network/availability-recovery/src/lib.rs b/polkadot/node/network/availability-recovery/src/lib.rs index 114faa2859c..eb54d9657d8 100644 --- a/polkadot/node/network/availability-recovery/src/lib.rs +++ b/polkadot/node/network/availability-recovery/src/lib.rs @@ -66,8 +66,8 @@ use polkadot_node_subsystem_util::{ runtime::{ExtendedSessionInfo, RuntimeInfo}, }; use polkadot_primitives::{ - node_features, BlockNumber, CandidateHash, CandidateReceipt, ChunkIndex, CoreIndex, GroupIndex, - Hash, SessionIndex, ValidatorIndex, + node_features, vstaging::CandidateReceiptV2 as CandidateReceipt, BlockNumber, CandidateHash, + ChunkIndex, CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, }; mod error; @@ -540,11 +540,11 @@ async fn handle_recover( threshold: recovery_threshold(n_validators)?, systematic_threshold, candidate_hash, - erasure_root: receipt.descriptor.erasure_root, + erasure_root: receipt.descriptor.erasure_root(), metrics: metrics.clone(), bypass_availability_store, post_recovery_check, - pov_hash: receipt.descriptor.pov_hash, + pov_hash: receipt.descriptor.pov_hash(), req_v1_protocol_name, req_v2_protocol_name, chunk_mapping_enabled, diff --git a/polkadot/node/network/availability-recovery/src/tests.rs b/polkadot/node/network/availability-recovery/src/tests.rs index 4fd9ede40ff..9a46d542078 100644 --- a/polkadot/node/network/availability-recovery/src/tests.rs +++ b/polkadot/node/network/availability-recovery/src/tests.rs @@ -41,8 +41,8 @@ use polkadot_node_subsystem_test_helpers::{ }; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{ - node_features, AuthorityDiscoveryId, Block, ExecutorParams, Hash, HeadData, IndexedVec, - NodeFeatures, PersistedValidationData, SessionInfo, ValidatorId, + node_features, vstaging::MutateDescriptorV2, AuthorityDiscoveryId, Block, ExecutorParams, Hash, + HeadData, IndexedVec, NodeFeatures, PersistedValidationData, SessionInfo, ValidatorId, }; use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; use sc_network::{IfDisconnected, OutboundFailure, ProtocolName, RequestFailure}; @@ -346,7 +346,7 @@ impl TestState { ) .unwrap(), current, - candidate, + candidate: candidate.into(), session_index, core_index, node_features, @@ -800,12 +800,12 @@ fn availability_is_recovered_from_chunks_if_no_group_provided(#[case] systematic // Test another candidate, send no chunks. let mut new_candidate = dummy_candidate_receipt(dummy_hash()); - new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent; + new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent(); overseer_send( &mut virtual_overseer, AvailabilityRecoveryMessage::RecoverAvailableData( - new_candidate.clone(), + new_candidate.clone().into(), test_state.session_index, None, Some(test_state.core_index), @@ -929,12 +929,12 @@ fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunk // Test another candidate, send no chunks. let mut new_candidate = dummy_candidate_receipt(dummy_hash()); - new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent; + new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent(); overseer_send( &mut virtual_overseer, AvailabilityRecoveryMessage::RecoverAvailableData( - new_candidate.clone(), + new_candidate.clone().into(), test_state.session_index, Some(GroupIndex(1)), Some(test_state.core_index), @@ -1218,7 +1218,7 @@ fn invalid_erasure_coding_leads_to_invalid_error(#[case] systematic_recovery: bo test_state.validators.len(), test_state.core_index, ); - test_state.candidate.descriptor.erasure_root = bad_erasure_root; + test_state.candidate.descriptor.set_erasure_root(bad_erasure_root); let candidate_hash = test_state.candidate.hash(); @@ -1283,7 +1283,7 @@ fn invalid_pov_hash_leads_to_invalid_error() { test_harness(subsystem, |mut virtual_overseer| async move { let pov = PoV { block_data: BlockData(vec![69; 64]) }; - test_state.candidate.descriptor.pov_hash = pov.hash(); + test_state.candidate.descriptor.set_pov_hash(pov.hash()); let candidate_hash = test_state.candidate.hash(); @@ -1420,7 +1420,10 @@ fn recovers_from_only_chunks_if_pov_large( test_state.threshold(), ), (false, true) => { - test_state.candidate.descriptor.pov_hash = test_state.available_data.pov.hash(); + test_state + .candidate + .descriptor + .set_pov_hash(test_state.available_data.pov.hash()); ( AvailabilityRecoverySubsystem::for_collator( None, @@ -1497,12 +1500,12 @@ fn recovers_from_only_chunks_if_pov_large( // Test another candidate, send no chunks. let mut new_candidate = dummy_candidate_receipt(dummy_hash()); - new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent; + new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent(); overseer_send( &mut virtual_overseer, AvailabilityRecoveryMessage::RecoverAvailableData( - new_candidate.clone(), + new_candidate.clone().into(), test_state.session_index, Some(GroupIndex(1)), Some(test_state.core_index), @@ -1593,7 +1596,10 @@ fn fast_path_backing_group_recovers_if_pov_small( Metrics::new_dummy(), ), (false, true) => { - test_state.candidate.descriptor.pov_hash = test_state.available_data.pov.hash(); + test_state + .candidate + .descriptor + .set_pov_hash(test_state.available_data.pov.hash()); AvailabilityRecoverySubsystem::for_collator( None, request_receiver(&req_protocol_names), @@ -2635,7 +2641,7 @@ fn number_of_request_retries_is_bounded( ); test_state.chunks = map_chunks(chunks, &test_state.node_features, n_validators, test_state.core_index); - test_state.candidate.descriptor.erasure_root = erasure_root; + test_state.candidate.descriptor.set_erasure_root(erasure_root); let (subsystem, retry_limit) = match systematic_recovery { false => ( diff --git a/polkadot/node/network/collator-protocol/src/collator_side/collation.rs b/polkadot/node/network/collator-protocol/src/collator_side/collation.rs index 57e1479a449..6a570331f71 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/collation.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/collation.rs @@ -28,7 +28,9 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::PoV; use polkadot_node_subsystem::messages::ParentHeadData; -use polkadot_primitives::{CandidateHash, CandidateReceipt, Hash, Id as ParaId}; +use polkadot_primitives::{ + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, Hash, Id as ParaId, +}; /// The status of a collation as seen from the collator. pub enum CollationStatus { diff --git a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs index af9beb535f4..504b0d71604 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs @@ -54,8 +54,9 @@ use polkadot_node_subsystem_util::{ TimeoutExt, }; use polkadot_primitives::{ - AuthorityDiscoveryId, CandidateHash, CandidateReceipt, CollatorPair, CoreIndex, CoreState, - GroupIndex, Hash, HeadData, Id as ParaId, SessionIndex, + vstaging::{CandidateReceiptV2 as CandidateReceipt, CoreState}, + AuthorityDiscoveryId, CandidateHash, CollatorPair, CoreIndex, GroupIndex, Hash, HeadData, + Id as ParaId, SessionIndex, }; use super::LOG_TARGET; @@ -374,7 +375,7 @@ async fn distribute_collation( result_sender: Option>, core_index: CoreIndex, ) -> Result<()> { - let candidate_relay_parent = receipt.descriptor.relay_parent; + let candidate_relay_parent = receipt.descriptor.relay_parent(); let candidate_hash = receipt.hash(); let per_relay_parent = match state.per_relay_parent.get_mut(&candidate_relay_parent) { @@ -850,12 +851,12 @@ async fn process_msg( core_index, } => { match state.collating_on { - Some(id) if candidate_receipt.descriptor.para_id != id => { + Some(id) if candidate_receipt.descriptor.para_id() != id => { // If the ParaId of a collation requested to be distributed does not match // the one we expect, we ignore the message. gum::warn!( target: LOG_TARGET, - para_id = %candidate_receipt.descriptor.para_id, + para_id = %candidate_receipt.descriptor.para_id(), collating_on = %id, "DistributeCollation for unexpected para_id", ); @@ -879,7 +880,7 @@ async fn process_msg( None => { gum::warn!( target: LOG_TARGET, - para_id = %candidate_receipt.descriptor.para_id, + para_id = %candidate_receipt.descriptor.para_id(), "DistributeCollation message while not collating on any", ); }, 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 58d9ebc5772..0b3e9f4b343 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/collation.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/collation.rs @@ -40,8 +40,8 @@ use polkadot_node_subsystem_util::{ metrics::prometheus::prometheus::HistogramTimer, runtime::ProspectiveParachainsMode, }; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CollatorId, Hash, HeadData, Id as ParaId, - PersistedValidationData, + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, CollatorId, Hash, HeadData, + Id as ParaId, PersistedValidationData, }; use tokio_util::sync::CancellationToken; @@ -71,18 +71,15 @@ pub struct FetchedCollation { pub para_id: ParaId, /// Candidate hash. pub candidate_hash: CandidateHash, - /// Id of the collator the collation was fetched from. - pub collator_id: CollatorId, } impl From<&CandidateReceipt> for FetchedCollation { fn from(receipt: &CandidateReceipt) -> Self { let descriptor = receipt.descriptor(); Self { - relay_parent: descriptor.relay_parent, - para_id: descriptor.para_id, + relay_parent: descriptor.relay_parent(), + para_id: descriptor.para_id(), candidate_hash: receipt.hash(), - collator_id: descriptor.collator.clone(), } } } @@ -141,7 +138,7 @@ pub fn fetched_collation_sanity_check( persisted_validation_data: &PersistedValidationData, maybe_parent_head_and_hash: Option<(HeadData, Hash)>, ) -> Result<(), SecondingError> { - if persisted_validation_data.hash() != fetched.descriptor().persisted_validation_data_hash { + if persisted_validation_data.hash() != fetched.descriptor().persisted_validation_data_hash() { Err(SecondingError::PersistedValidationDataMismatch) } else if advertised .prospective_candidate 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 deb6ce03f43..51e987d59ce 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs @@ -52,8 +52,8 @@ use polkadot_node_subsystem_util::{ runtime::{fetch_claim_queue, prospective_parachains_mode, ProspectiveParachainsMode}, }; use polkadot_primitives::{ - CandidateHash, CollatorId, CoreState, Hash, HeadData, Id as ParaId, OccupiedCoreAssumption, - PersistedValidationData, + vstaging::CoreState, CandidateHash, CollatorId, Hash, HeadData, Id as ParaId, + OccupiedCoreAssumption, PersistedValidationData, }; use crate::error::{Error, FetchError, Result, SecondingError}; @@ -1021,7 +1021,7 @@ async fn second_unblocked_collations( for mut unblocked_collation in unblocked_collations { unblocked_collation.maybe_parent_head_data = Some(head_data.clone()); let peer_id = unblocked_collation.collation_event.pending_collation.peer_id; - let relay_parent = unblocked_collation.candidate_receipt.descriptor.relay_parent; + let relay_parent = unblocked_collation.candidate_receipt.descriptor.relay_parent(); if let Err(err) = kick_off_seconding(ctx, state, unblocked_collation).await { gum::warn!( @@ -1328,7 +1328,7 @@ where collations.retain(|collation| { state .per_relay_parent - .contains_key(&collation.candidate_receipt.descriptor.relay_parent) + .contains_key(&collation.candidate_receipt.descriptor.relay_parent()) }); !collations.is_empty() @@ -1470,7 +1470,7 @@ async fn process_msg( }, }; let output_head_data = receipt.commitments.head_data.clone(); - let output_head_data_hash = receipt.descriptor.para_head; + let output_head_data_hash = receipt.descriptor.para_head(); let fetched_collation = FetchedCollation::from(&receipt.to_plain()); if let Some(CollationEvent { collator_id, pending_collation, .. }) = state.fetched_candidates.remove(&fetched_collation) @@ -1531,8 +1531,8 @@ async fn process_msg( Invalid(parent, candidate_receipt) => { // Remove collations which were blocked from seconding and had this candidate as parent. state.blocked_from_seconding.remove(&BlockedCollationId { - para_id: candidate_receipt.descriptor.para_id, - parent_head_data_hash: candidate_receipt.descriptor.para_head, + para_id: candidate_receipt.descriptor.para_id(), + parent_head_data_hash: candidate_receipt.descriptor.para_head(), }); let fetched_collation = FetchedCollation::from(&candidate_receipt); @@ -1843,8 +1843,8 @@ async fn kick_off_seconding( (CollationVersion::V2, Some(_)) | (CollationVersion::V1, _) => { let pvd = request_persisted_validation_data( ctx.sender(), - candidate_receipt.descriptor().relay_parent, - candidate_receipt.descriptor().para_id, + candidate_receipt.descriptor().relay_parent(), + candidate_receipt.descriptor().para_id(), ) .await?; ( @@ -1874,14 +1874,14 @@ async fn kick_off_seconding( gum::debug!( target: LOG_TARGET, candidate_hash = ?blocked_collation.candidate_receipt.hash(), - relay_parent = ?blocked_collation.candidate_receipt.descriptor.relay_parent, + relay_parent = ?blocked_collation.candidate_receipt.descriptor.relay_parent(), "Collation having parent head data hash {} is blocked from seconding. Waiting on its parent to be validated.", parent_head_data_hash ); state .blocked_from_seconding .entry(BlockedCollationId { - para_id: blocked_collation.candidate_receipt.descriptor.para_id, + para_id: blocked_collation.candidate_receipt.descriptor.para_id(), parent_head_data_hash, }) .or_insert_with(Vec::new) @@ -2025,11 +2025,11 @@ async fn handle_collation_fetch_response( Ok( request_v1::CollationFetchingResponse::Collation(receipt, _) | request_v1::CollationFetchingResponse::CollationWithParentHeadData { receipt, .. }, - ) if receipt.descriptor().para_id != pending_collation.para_id => { + ) if receipt.descriptor().para_id() != pending_collation.para_id => { gum::debug!( target: LOG_TARGET, expected_para_id = ?pending_collation.para_id, - got_para_id = ?receipt.descriptor().para_id, + got_para_id = ?receipt.descriptor().para_id(), peer_id = ?pending_collation.peer_id, "Got wrong para ID for requested collation." ); 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 86c8bcb6bdc..290c4db901d 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,8 +42,9 @@ 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::{ - CandidateReceipt, CollatorPair, CoreIndex, CoreState, GroupIndex, GroupRotationInfo, HeadData, - OccupiedCore, PersistedValidationData, ScheduledCore, ValidatorId, ValidatorIndex, + vstaging::{CandidateReceiptV2 as CandidateReceipt, CoreState, OccupiedCore}, + CollatorPair, CoreIndex, GroupIndex, GroupRotationInfo, HeadData, PersistedValidationData, + ScheduledCore, ValidatorId, ValidatorIndex, }; use polkadot_primitives_test_helpers::{ dummy_candidate_descriptor, dummy_candidate_receipt_bad_sig, dummy_hash, @@ -121,7 +122,7 @@ impl Default for TestState { let mut d = dummy_candidate_descriptor(dummy_hash()); d.para_id = chain_ids[1]; - d + d.into() }, }), ]; @@ -341,7 +342,7 @@ async fn assert_candidate_backing_second( incoming_pov, )) => { assert_eq!(expected_relay_parent, relay_parent); - assert_eq!(expected_para_id, candidate_receipt.descriptor.para_id); + assert_eq!(expected_para_id, candidate_receipt.descriptor.para_id()); assert_eq!(*expected_pov, incoming_pov); assert_eq!(pvd, received_pvd); candidate_receipt @@ -591,8 +592,11 @@ fn act_on_advertisement_v2() { response_channel .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); @@ -793,8 +797,11 @@ fn fetch_one_collation_at_a_time() { candidate_a.descriptor.persisted_validation_data_hash = dummy_pvd().hash(); response_channel .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); @@ -917,16 +924,22 @@ fn fetches_next_collation() { // First request finishes now: response_channel_non_exclusive .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); response_channel .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); @@ -1055,8 +1068,11 @@ fn fetch_next_collation_on_invalid_collation() { candidate_a.descriptor.persisted_validation_data_hash = dummy_pvd().hash(); response_channel .send(Ok(( - request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()) - .encode(), + request_v1::CollationFetchingResponse::Collation( + candidate_a.clone().into(), + pov.clone(), + ) + .encode(), ProtocolName::from(""), ))) .expect("Sending response should succeed"); 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 dff98e22e3d..e040163cd90 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,8 +20,8 @@ use super::*; use polkadot_node_subsystem::messages::ChainApiMessage; use polkadot_primitives::{ - AsyncBackingParams, BlockNumber, CandidateCommitments, CommittedCandidateReceipt, Header, - SigningContext, ValidatorId, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, AsyncBackingParams, + BlockNumber, CandidateCommitments, Header, SigningContext, ValidatorId, }; use rstest::rstest; @@ -225,7 +225,7 @@ async fn send_seconded_statement( overseer_send( virtual_overseer, - CollatorProtocolMessage::Seconded(candidate.descriptor.relay_parent, stmt), + CollatorProtocolMessage::Seconded(candidate.descriptor.relay_parent(), stmt), ) .await; } @@ -374,7 +374,7 @@ fn v1_advertisement_accepted_and_seconded() { hrmp_watermark: 0, }; candidate.commitments_hash = commitments.hash(); - + let candidate: CandidateReceipt = candidate.into(); let pov = PoV { block_data: BlockData(vec![1]) }; response_channel @@ -595,6 +595,7 @@ fn second_multiple_candidates_per_relay_parent() { hrmp_watermark: 0, }; candidate.commitments_hash = commitments.hash(); + let candidate: CandidateReceipt = candidate.into(); let candidate_hash = candidate.hash(); let parent_head_data_hash = Hash::zero(); @@ -750,7 +751,7 @@ fn fetched_collation_sanity_check() { hrmp_watermark: 0, }; candidate.commitments_hash = commitments.hash(); - + let candidate: CandidateReceipt = candidate.into(); let candidate_hash = CandidateHash(Hash::zero()); let parent_head_data_hash = Hash::zero(); @@ -845,7 +846,6 @@ fn sanity_check_invalid_parent_head_data() { let mut candidate = dummy_candidate_receipt_bad_sig(head_c, Some(Default::default())); candidate.descriptor.para_id = test_state.chain_ids[0]; - let commitments = CandidateCommitments { head_data: HeadData(vec![1, 2, 3]), horizontal_messages: Default::default(), @@ -864,6 +864,7 @@ fn sanity_check_invalid_parent_head_data() { pvd.parent_head = parent_head_data; candidate.descriptor.persisted_validation_data_hash = pvd.hash(); + let candidate: CandidateReceipt = candidate.into(); let candidate_hash = candidate.hash(); @@ -1068,6 +1069,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { processed_downward_messages: 0, hrmp_watermark: 0, }; + let mut candidate_b: CandidateReceipt = candidate_b.into(); candidate_b.commitments_hash = candidate_b_commitments.hash(); let candidate_b_hash = candidate_b.hash(); @@ -1134,6 +1136,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { relay_parent_storage_root: Default::default(), } .hash(); + let mut candidate_a: CandidateReceipt = candidate_a.into(); let candidate_a_commitments = CandidateCommitments { head_data: HeadData(vec![1]), horizontal_messages: Default::default(), @@ -1144,6 +1147,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { }; candidate_a.commitments_hash = candidate_a_commitments.hash(); + let candidate_a: CandidateReceipt = candidate_a.into(); let candidate_a_hash = candidate_a.hash(); advertise_collation( @@ -1208,7 +1212,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { incoming_pov, )) => { assert_eq!(head_c, relay_parent); - assert_eq!(test_state.chain_ids[0], candidate_receipt.descriptor.para_id); + assert_eq!(test_state.chain_ids[0], candidate_receipt.descriptor.para_id()); assert_eq!(PoV { block_data: BlockData(vec![2]) }, incoming_pov); assert_eq!(PersistedValidationData:: { parent_head: HeadData(vec![0]), @@ -1261,7 +1265,7 @@ fn child_blocked_from_seconding_by_parent(#[case] valid_parent: bool) { incoming_pov, )) => { assert_eq!(head_c, relay_parent); - assert_eq!(test_state.chain_ids[0], candidate_receipt.descriptor.para_id); + assert_eq!(test_state.chain_ids[0], candidate_receipt.descriptor.para_id()); assert_eq!(PoV { block_data: BlockData(vec![1]) }, incoming_pov); assert_eq!(PersistedValidationData:: { parent_head: HeadData(vec![1]), diff --git a/polkadot/node/network/dispute-distribution/src/receiver/batches/batch.rs b/polkadot/node/network/dispute-distribution/src/receiver/batches/batch.rs index 11380b7c072..c911b4bc4ae 100644 --- a/polkadot/node/network/dispute-distribution/src/receiver/batches/batch.rs +++ b/polkadot/node/network/dispute-distribution/src/receiver/batches/batch.rs @@ -22,7 +22,7 @@ use polkadot_node_network_protocol::{ PeerId, }; use polkadot_node_primitives::SignedDisputeStatement; -use polkadot_primitives::{CandidateReceipt, ValidatorIndex}; +use polkadot_primitives::{vstaging::CandidateReceiptV2 as CandidateReceipt, ValidatorIndex}; use crate::receiver::{BATCH_COLLECTING_INTERVAL, MIN_KEEP_BATCH_ALIVE_VOTES}; diff --git a/polkadot/node/network/dispute-distribution/src/receiver/batches/mod.rs b/polkadot/node/network/dispute-distribution/src/receiver/batches/mod.rs index 76c7683d157..13b42aff1f3 100644 --- a/polkadot/node/network/dispute-distribution/src/receiver/batches/mod.rs +++ b/polkadot/node/network/dispute-distribution/src/receiver/batches/mod.rs @@ -22,7 +22,7 @@ use std::{ use futures::future::pending; use polkadot_node_network_protocol::request_response::DISPUTE_REQUEST_TIMEOUT; -use polkadot_primitives::{CandidateHash, CandidateReceipt}; +use polkadot_primitives::{vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash}; use crate::{ receiver::batches::{batch::TickResult, waiting_queue::PendingWake}, diff --git a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs index 77c1e41aac0..b21965fc700 100644 --- a/polkadot/node/network/dispute-distribution/src/receiver/mod.rs +++ b/polkadot/node/network/dispute-distribution/src/receiver/mod.rs @@ -334,7 +334,7 @@ where .runtime .get_session_info_by_index( &mut self.sender, - payload.0.candidate_receipt.descriptor.relay_parent, + payload.0.candidate_receipt.descriptor.relay_parent(), payload.0.session_index, ) .await?; diff --git a/polkadot/node/network/dispute-distribution/src/sender/send_task.rs b/polkadot/node/network/dispute-distribution/src/sender/send_task.rs index 54ccd10789d..f607c943151 100644 --- a/polkadot/node/network/dispute-distribution/src/sender/send_task.rs +++ b/polkadot/node/network/dispute-distribution/src/sender/send_task.rs @@ -234,7 +234,7 @@ impl SendTask { runtime: &mut RuntimeInfo, active_sessions: &HashMap, ) -> Result> { - let ref_head = self.request.0.candidate_receipt.descriptor.relay_parent; + let ref_head = self.request.0.candidate_receipt.descriptor.relay_parent(); // Retrieve all authorities which participated in the parachain consensus of the session // in which the candidate was backed. let info = runtime diff --git a/polkadot/node/network/dispute-distribution/src/tests/mock.rs b/polkadot/node/network/dispute-distribution/src/tests/mock.rs index baa857e2eb6..52659ae9e00 100644 --- a/polkadot/node/network/dispute-distribution/src/tests/mock.rs +++ b/polkadot/node/network/dispute-distribution/src/tests/mock.rs @@ -33,10 +33,10 @@ use sp_keystore::{Keystore, KeystorePtr}; use polkadot_node_primitives::{DisputeMessage, SignedDisputeStatement}; use polkadot_primitives::{ - AuthorityDiscoveryId, CandidateHash, CandidateReceipt, Hash, SessionIndex, SessionInfo, - ValidatorId, ValidatorIndex, + vstaging::CandidateReceiptV2 as CandidateReceipt, AuthorityDiscoveryId, CandidateHash, Hash, + SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, }; -use polkadot_primitives_test_helpers::dummy_candidate_descriptor; +use polkadot_primitives_test_helpers::dummy_candidate_descriptor_v2; use crate::LOG_TARGET; @@ -116,7 +116,7 @@ pub static MOCK_NEXT_SESSION_INFO: LazyLock = LazyLock::new(|| Sess pub fn make_candidate_receipt(relay_parent: Hash) -> CandidateReceipt { CandidateReceipt { - descriptor: dummy_candidate_descriptor(relay_parent), + descriptor: dummy_candidate_descriptor_v2(relay_parent), commitments_hash: Hash::random(), } } diff --git a/polkadot/node/network/dispute-distribution/src/tests/mod.rs b/polkadot/node/network/dispute-distribution/src/tests/mod.rs index 60820e62ca2..5306b22828c 100644 --- a/polkadot/node/network/dispute-distribution/src/tests/mod.rs +++ b/polkadot/node/network/dispute-distribution/src/tests/mod.rs @@ -57,8 +57,8 @@ use polkadot_node_subsystem_test_helpers::{ subsystem_test_harness, TestSubsystemContextHandle, }; use polkadot_primitives::{ - AuthorityDiscoveryId, Block, CandidateHash, CandidateReceipt, ExecutorParams, Hash, - NodeFeatures, SessionIndex, SessionInfo, + vstaging::CandidateReceiptV2 as CandidateReceipt, AuthorityDiscoveryId, Block, CandidateHash, + ExecutorParams, Hash, NodeFeatures, SessionIndex, SessionInfo, }; use self::mock::{ diff --git a/polkadot/node/network/protocol/src/request_response/v1.rs b/polkadot/node/network/protocol/src/request_response/v1.rs index 80721f1884a..4f28d4cbf2d 100644 --- a/polkadot/node/network/protocol/src/request_response/v1.rs +++ b/polkadot/node/network/protocol/src/request_response/v1.rs @@ -22,8 +22,11 @@ use polkadot_node_primitives::{ AvailableData, DisputeMessage, ErasureChunk, PoV, Proof, UncheckedDisputeMessage, }; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CommittedCandidateReceipt, Hash, HeadData, Id as ParaId, - ValidatorIndex, + vstaging::{ + CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + }, + CandidateHash, Hash, HeadData, Id as ParaId, ValidatorIndex, }; use super::{IsRequest, Protocol}; diff --git a/polkadot/node/network/protocol/src/request_response/v2.rs b/polkadot/node/network/protocol/src/request_response/v2.rs index ae65b39cd40..834870e5b90 100644 --- a/polkadot/node/network/protocol/src/request_response/v2.rs +++ b/polkadot/node/network/protocol/src/request_response/v2.rs @@ -20,8 +20,8 @@ use codec::{Decode, Encode}; use polkadot_node_primitives::ErasureChunk; use polkadot_primitives::{ - CandidateHash, CommittedCandidateReceipt, Hash, Id as ParaId, PersistedValidationData, - UncheckedSignedStatement, ValidatorIndex, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, Hash, + Id as ParaId, PersistedValidationData, UncheckedSignedStatement, ValidatorIndex, }; use super::{v1, IsRequest, Protocol}; diff --git a/polkadot/node/network/statement-distribution/Cargo.toml b/polkadot/node/network/statement-distribution/Cargo.toml index 2a9773ddde4..08059353033 100644 --- a/polkadot/node/network/statement-distribution/Cargo.toml +++ b/polkadot/node/network/statement-distribution/Cargo.toml @@ -40,6 +40,7 @@ sp-tracing = { workspace = true, default-features = true } sc-keystore = { workspace = true, default-features = true } sc-network = { workspace = true, default-features = true } futures-timer = { workspace = true } +polkadot-primitives = { workspace = true, features = ["test"] } polkadot-primitives-test-helpers = { workspace = true } rand_chacha = { workspace = true, default-features = true } polkadot-subsystem-bench = { workspace = true } diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs index 8270b980919..bd6d4ebe755 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs @@ -37,9 +37,10 @@ use polkadot_node_subsystem::{ overseer, ActivatedLeaf, StatementDistributionSenderTrait, }; use polkadot_primitives::{ - AuthorityDiscoveryId, CandidateHash, CommittedCandidateReceipt, CompactStatement, Hash, - Id as ParaId, IndexedVec, OccupiedCoreAssumption, PersistedValidationData, SignedStatement, - SigningContext, UncheckedSignedStatement, ValidatorId, ValidatorIndex, ValidatorSignature, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, AuthorityDiscoveryId, + CandidateHash, CompactStatement, Hash, Id as ParaId, IndexedVec, OccupiedCoreAssumption, + PersistedValidationData, SignedStatement, SigningContext, UncheckedSignedStatement, + ValidatorId, ValidatorIndex, ValidatorSignature, }; use futures::{ @@ -1641,7 +1642,7 @@ async fn handle_incoming_message<'a, Context>( // In case of `Valid` we should have it cached prior, therefore this performs // no Runtime API calls and always returns `Ok(Some(_))`. let pvd = if let Statement::Seconded(receipt) = statement.payload() { - let para_id = receipt.descriptor.para_id; + let para_id = receipt.descriptor.para_id(); // Either call the Runtime API or check that validation data is cached. let result = active_head .fetch_persisted_validation_data(ctx.sender(), relay_parent, para_id) diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs index c0346adfe10..69bcbac76b7 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs @@ -29,7 +29,9 @@ use polkadot_node_network_protocol::{ PeerId, UnifiedReputationChange, }; use polkadot_node_subsystem_util::TimeoutExt; -use polkadot_primitives::{CandidateHash, CommittedCandidateReceipt, Hash}; +use polkadot_primitives::{ + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, Hash, +}; use crate::{ legacy_v1::{COST_WRONG_HASH, LOG_TARGET}, diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs index 8d1683759a0..03e1dc05998 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/responder.rs @@ -29,7 +29,9 @@ use polkadot_node_network_protocol::{ }, PeerId, UnifiedReputationChange as Rep, }; -use polkadot_primitives::{CandidateHash, CommittedCandidateReceipt, Hash}; +use polkadot_primitives::{ + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, Hash, +}; use crate::LOG_TARGET; diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs index 5e00fb96d74..d2fd016ec2f 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs @@ -47,7 +47,8 @@ use polkadot_primitives::{ SessionInfo, ValidationCode, }; use polkadot_primitives_test_helpers::{ - dummy_committed_candidate_receipt, dummy_hash, AlwaysZeroRng, + dummy_committed_candidate_receipt, dummy_committed_candidate_receipt_v2, dummy_hash, + AlwaysZeroRng, }; use sc_keystore::LocalKeystore; use sc_network::ProtocolName; @@ -140,7 +141,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note A let a_seconded_val_0 = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_a.clone()), + Statement::Seconded(candidate_a.into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -167,7 +168,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note B let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_b.clone()), + Statement::Seconded(candidate_b.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -184,7 +185,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note C (beyond 2 - ignored) let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_c.clone()), + Statement::Seconded(candidate_c.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -202,7 +203,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note B (new validator) let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_b.clone()), + Statement::Seconded(candidate_b.into()), &signing_context, ValidatorIndex(1), &bob_public.into(), @@ -219,7 +220,7 @@ fn active_head_accepts_only_2_seconded_per_validator() { // note C (new validator) let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate_c.clone()), + Statement::Seconded(candidate_c.into()), &signing_context, ValidatorIndex(1), &bob_public.into(), @@ -470,7 +471,7 @@ fn peer_view_update_sends_messages() { let statement = SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate.clone()), + Statement::Seconded(candidate.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -612,7 +613,7 @@ fn circulated_statement_goes_to_all_peers_with_view() { let mut c = dummy_committed_candidate_receipt(dummy_hash()); c.descriptor.relay_parent = hash_b; c.descriptor.para_id = ParaId::from(1_u32); - c + c.into() }; let peer_a = PeerId::random(); @@ -746,7 +747,7 @@ fn receiving_from_one_sends_to_another_and_to_candidate_backing() { let mut c = dummy_committed_candidate_receipt(dummy_hash()); c.descriptor.relay_parent = hash_a; c.descriptor.para_id = PARA_ID; - c + c.into() }; let peer_a = PeerId::random(); @@ -1199,7 +1200,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing( SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate.clone()), + Statement::Seconded(candidate.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -1337,7 +1338,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing( let bad_candidate = { let mut bad = candidate.clone(); bad.descriptor.para_id = 0xeadbeaf.into(); - bad + bad.into() }; let response = StatementFetchingResponse::Statement(bad_candidate); outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap(); @@ -1391,7 +1392,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing( assert_eq!(req.candidate_hash, metadata.candidate_hash); // On retry, we should have reverse order: assert_eq!(outgoing.peer, Recipient::Peer(peer_c)); - let response = StatementFetchingResponse::Statement(candidate.clone()); + let response = StatementFetchingResponse::Statement(candidate.clone().into()); outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap(); } ); @@ -1517,7 +1518,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing( req_cfg.inbound_queue.as_mut().unwrap().send(req).await.unwrap(); let StatementFetchingResponse::Statement(committed) = Decode::decode(&mut response_rx.await.unwrap().result.unwrap().as_ref()).unwrap(); - assert_eq!(committed, candidate); + assert_eq!(committed, candidate.into()); handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; @@ -1744,7 +1745,7 @@ fn delay_reputation_changes() { SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate.clone()), + Statement::Seconded(candidate.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -1884,7 +1885,7 @@ fn delay_reputation_changes() { bad.descriptor.para_id = 0xeadbeaf.into(); bad }; - let response = StatementFetchingResponse::Statement(bad_candidate); + let response = StatementFetchingResponse::Statement(bad_candidate.into()); outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap(); } ); @@ -1928,7 +1929,7 @@ fn delay_reputation_changes() { assert_eq!(req.candidate_hash, metadata.candidate_hash); // On retry, we should have reverse order: assert_eq!(outgoing.peer, Recipient::Peer(peer_c)); - let response = StatementFetchingResponse::Statement(candidate.clone()); + let response = StatementFetchingResponse::Statement(candidate.clone().into()); outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap(); } ); @@ -2288,7 +2289,7 @@ fn share_prioritizes_backing_group() { SignedFullStatementWithPVD::sign( &keystore, - Statement::Seconded(candidate.clone()).supply_pvd(pvd), + Statement::Seconded(candidate.clone().into()).supply_pvd(pvd), &signing_context, ValidatorIndex(4), &ferdie_public.into(), @@ -2352,7 +2353,7 @@ fn share_prioritizes_backing_group() { req_cfg.inbound_queue.as_mut().unwrap().send(req).await.unwrap(); let StatementFetchingResponse::Statement(committed) = Decode::decode(&mut response_rx.await.unwrap().result.unwrap().as_ref()).unwrap(); - assert_eq!(committed, candidate); + assert_eq!(committed, candidate.into()); handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; @@ -2514,7 +2515,7 @@ fn peer_cant_flood_with_large_statements() { SignedFullStatement::sign( &keystore, - Statement::Seconded(candidate.clone()), + Statement::Seconded(candidate.clone().into()), &signing_context, ValidatorIndex(0), &alice_public.into(), @@ -2595,7 +2596,7 @@ fn handle_multiple_seconded_statements() { let relay_parent_hash = Hash::repeat_byte(1); let pvd = dummy_pvd(); - let candidate = dummy_committed_candidate_receipt(relay_parent_hash); + let candidate = dummy_committed_candidate_receipt_v2(relay_parent_hash); let candidate_hash = candidate.hash(); // We want to ensure that our peers are not lucky diff --git a/polkadot/node/network/statement-distribution/src/v2/candidates.rs b/polkadot/node/network/statement-distribution/src/v2/candidates.rs index a4f2455c284..1a37d2ea086 100644 --- a/polkadot/node/network/statement-distribution/src/v2/candidates.rs +++ b/polkadot/node/network/statement-distribution/src/v2/candidates.rs @@ -28,8 +28,8 @@ use polkadot_node_network_protocol::PeerId; use polkadot_node_subsystem::messages::HypotheticalCandidate; use polkadot_primitives::{ - CandidateHash, CommittedCandidateReceipt, GroupIndex, Hash, Id as ParaId, - PersistedValidationData, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, GroupIndex, + Hash, Id as ParaId, PersistedValidationData, }; use std::{ @@ -154,8 +154,8 @@ impl Candidates { assigned_group: GroupIndex, ) -> Option { let parent_hash = persisted_validation_data.parent_head.hash(); - let relay_parent = candidate_receipt.descriptor().relay_parent; - let para_id = candidate_receipt.descriptor().para_id; + let relay_parent = candidate_receipt.descriptor.relay_parent(); + let para_id = candidate_receipt.descriptor.para_id(); let prev_state = self.candidates.insert( candidate_hash, @@ -530,12 +530,12 @@ pub struct ConfirmedCandidate { impl ConfirmedCandidate { /// Get the relay-parent of the candidate. pub fn relay_parent(&self) -> Hash { - self.receipt.descriptor().relay_parent + self.receipt.descriptor.relay_parent() } /// Get the para-id of the candidate. pub fn para_id(&self) -> ParaId { - self.receipt.descriptor().para_id + self.receipt.descriptor.para_id() } /// Get the underlying candidate receipt. diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index f9c2d0ddbae..c79ae3953ad 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -50,9 +50,9 @@ use polkadot_node_subsystem_util::{ }, }; use polkadot_primitives::{ - AuthorityDiscoveryId, CandidateHash, CompactStatement, CoreIndex, CoreState, GroupIndex, - GroupRotationInfo, Hash, Id as ParaId, IndexedVec, SessionIndex, SessionInfo, SignedStatement, - SigningContext, UncheckedSignedStatement, ValidatorId, ValidatorIndex, + vstaging::CoreState, AuthorityDiscoveryId, CandidateHash, CompactStatement, CoreIndex, + GroupIndex, GroupRotationInfo, Hash, Id as ParaId, IndexedVec, SessionIndex, SessionInfo, + SignedStatement, SigningContext, UncheckedSignedStatement, ValidatorId, ValidatorIndex, }; use sp_keystore::KeystorePtr; @@ -1201,7 +1201,7 @@ pub(crate) async fn share_local_statement( // have the candidate. Sanity: check the para-id is valid. let expected = match statement.payload() { FullStatementWithPVD::Seconded(ref c, _) => - Some((c.descriptor().para_id, c.descriptor().relay_parent)), + Some((c.descriptor.para_id(), c.descriptor.relay_parent())), FullStatementWithPVD::Valid(hash) => state.candidates.get_confirmed(&hash).map(|c| (c.para_id(), c.relay_parent())), }; @@ -2277,13 +2277,13 @@ async fn fragment_chain_update_inner( } = hypo { let confirmed_candidate = state.candidates.get_confirmed(&candidate_hash); - let prs = state.per_relay_parent.get_mut(&receipt.descriptor().relay_parent); + let prs = state.per_relay_parent.get_mut(&receipt.descriptor.relay_parent()); if let (Some(confirmed), Some(prs)) = (confirmed_candidate, prs) { let per_session = state.per_session.get(&prs.session); let group_index = confirmed.group_index(); // Sanity check if group_index is valid for this para at relay parent. - let Some(expected_groups) = prs.groups_per_para.get(&receipt.descriptor().para_id) + let Some(expected_groups) = prs.groups_per_para.get(&receipt.descriptor.para_id()) else { continue }; @@ -2296,7 +2296,7 @@ async fn fragment_chain_update_inner( ctx, candidate_hash, confirmed.group_index(), - &receipt.descriptor().relay_parent, + &receipt.descriptor.relay_parent(), prs, confirmed, per_session, @@ -2888,7 +2888,7 @@ pub(crate) async fn handle_backed_candidate_message( ctx, state, confirmed.para_id(), - confirmed.candidate_receipt().descriptor().para_head, + confirmed.candidate_receipt().descriptor.para_head(), ) .await; } diff --git a/polkadot/node/network/statement-distribution/src/v2/requests.rs b/polkadot/node/network/statement-distribution/src/v2/requests.rs index b8ed34d26c8..74f29710956 100644 --- a/polkadot/node/network/statement-distribution/src/v2/requests.rs +++ b/polkadot/node/network/statement-distribution/src/v2/requests.rs @@ -47,9 +47,9 @@ use polkadot_node_network_protocol::{ PeerId, UnifiedReputationChange as Rep, }; use polkadot_primitives::{ - CandidateHash, CommittedCandidateReceipt, CompactStatement, GroupIndex, Hash, Id as ParaId, - PersistedValidationData, SessionIndex, SignedStatement, SigningContext, ValidatorId, - ValidatorIndex, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, + CompactStatement, GroupIndex, Hash, Id as ParaId, PersistedValidationData, SessionIndex, + SignedStatement, SigningContext, ValidatorId, ValidatorIndex, }; use futures::{future::BoxFuture, prelude::*, stream::FuturesUnordered}; @@ -696,18 +696,18 @@ fn validate_complete_response( // sanity-check candidate response. // note: roughly ascending cost of operations { - if response.candidate_receipt.descriptor.relay_parent != identifier.relay_parent { + if response.candidate_receipt.descriptor.relay_parent() != identifier.relay_parent { return invalid_candidate_output() } - if response.candidate_receipt.descriptor.persisted_validation_data_hash != + if response.candidate_receipt.descriptor.persisted_validation_data_hash() != response.persisted_validation_data.hash() { return invalid_candidate_output() } if !allowed_para_lookup( - response.candidate_receipt.descriptor.para_id, + response.candidate_receipt.descriptor.para_id(), identifier.group_index, ) { return invalid_candidate_output() @@ -1019,6 +1019,7 @@ mod tests { candidate_receipt.descriptor.persisted_validation_data_hash = persisted_validation_data.hash(); let candidate = candidate_receipt.hash(); + let candidate_receipt: CommittedCandidateReceipt = candidate_receipt.into(); let requested_peer_1 = PeerId::random(); let requested_peer_2 = PeerId::random(); @@ -1074,7 +1075,7 @@ mod tests { requested_peer: requested_peer_1, props: request_properties.clone(), response: Ok(AttestedCandidateResponse { - candidate_receipt: candidate_receipt.clone(), + candidate_receipt: candidate_receipt.clone().into(), persisted_validation_data: persisted_validation_data.clone(), statements, }), @@ -1114,7 +1115,7 @@ mod tests { requested_peer: requested_peer_2, props: request_properties, response: Ok(AttestedCandidateResponse { - candidate_receipt: candidate_receipt.clone(), + candidate_receipt: candidate_receipt.clone().into(), persisted_validation_data: persisted_validation_data.clone(), statements, }), @@ -1197,7 +1198,7 @@ mod tests { requested_peer, props: request_properties, response: Ok(AttestedCandidateResponse { - candidate_receipt: candidate_receipt.clone(), + candidate_receipt: candidate_receipt.clone().into(), persisted_validation_data: persisted_validation_data.clone(), statements, }), @@ -1236,6 +1237,7 @@ mod tests { candidate_receipt.descriptor.persisted_validation_data_hash = persisted_validation_data.hash(); let candidate = candidate_receipt.hash(); + let candidate_receipt: CommittedCandidateReceipt = candidate_receipt.into(); let requested_peer = PeerId::random(); let identifier = request_manager @@ -1417,7 +1419,7 @@ mod tests { requested_peer: requested_peer_1, props: request_properties.clone(), response: Ok(AttestedCandidateResponse { - candidate_receipt: candidate_receipt_1.clone(), + candidate_receipt: candidate_receipt_1.clone().into(), persisted_validation_data: persisted_validation_data_1.clone(), statements, }), 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 119dc832d13..9f2c36ad101 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::{ - AssignmentPair, AsyncBackingParams, Block, BlockNumber, CommittedCandidateReceipt, CoreState, - GroupRotationInfo, HeadData, Header, IndexedVec, PersistedValidationData, ScheduledCore, - SessionIndex, SessionInfo, ValidatorPair, + vstaging::{CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState}, + AssignmentPair, AsyncBackingParams, Block, BlockNumber, GroupRotationInfo, HeadData, Header, + IndexedVec, PersistedValidationData, ScheduledCore, SessionIndex, SessionInfo, ValidatorPair, }; use sc_keystore::LocalKeystore; use sc_network::ProtocolName; diff --git a/polkadot/node/overseer/examples/minimal-example.rs b/polkadot/node/overseer/examples/minimal-example.rs index 807e7405ff1..e1b2af733b4 100644 --- a/polkadot/node/overseer/examples/minimal-example.rs +++ b/polkadot/node/overseer/examples/minimal-example.rs @@ -31,7 +31,9 @@ use polkadot_overseer::{ gen::{FromOrchestra, SpawnedSubsystem}, HeadSupportsParachains, SubsystemError, }; -use polkadot_primitives::{CandidateReceipt, Hash, PersistedValidationData}; +use polkadot_primitives::{ + vstaging::CandidateReceiptV2 as CandidateReceipt, Hash, PersistedValidationData, +}; use polkadot_primitives_test_helpers::{ dummy_candidate_descriptor, dummy_hash, dummy_validation_code, }; @@ -71,7 +73,7 @@ impl Subsystem1 { let (tx, _) = oneshot::channel(); let candidate_receipt = CandidateReceipt { - descriptor: dummy_candidate_descriptor(dummy_hash()), + descriptor: dummy_candidate_descriptor(dummy_hash()).into(), commitments_hash: Hash::zero(), }; diff --git a/polkadot/node/overseer/src/tests.rs b/polkadot/node/overseer/src/tests.rs index 46864a482e2..c3c47335cd3 100644 --- a/polkadot/node/overseer/src/tests.rs +++ b/polkadot/node/overseer/src/tests.rs @@ -28,11 +28,12 @@ use polkadot_node_subsystem_types::messages::{ NetworkBridgeEvent, PvfExecKind, ReportPeerMessage, RuntimeApiRequest, }; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CollatorPair, Id as ParaId, InvalidDisputeStatementKind, - PersistedValidationData, SessionIndex, ValidDisputeStatementKind, ValidatorIndex, + vstaging::CandidateReceiptV2, CandidateHash, CollatorPair, Id as ParaId, + InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, ValidDisputeStatementKind, + ValidatorIndex, }; use polkadot_primitives_test_helpers::{ - dummy_candidate_descriptor, dummy_candidate_receipt, dummy_hash, dummy_validation_code, + dummy_candidate_descriptor, dummy_candidate_receipt_v2, dummy_hash, dummy_validation_code, }; use crate::{ @@ -98,8 +99,8 @@ where let mut c: usize = 0; loop { if c < 10 { - let candidate_receipt = CandidateReceipt { - descriptor: dummy_candidate_descriptor(dummy_hash()), + let candidate_receipt = CandidateReceiptV2 { + descriptor: dummy_candidate_descriptor(dummy_hash()).into(), commitments_hash: dummy_hash(), }; @@ -799,8 +800,8 @@ where fn test_candidate_validation_msg() -> CandidateValidationMessage { let (response_sender, _) = oneshot::channel(); let pov = Arc::new(PoV { block_data: BlockData(Vec::new()) }); - let candidate_receipt = CandidateReceipt { - descriptor: dummy_candidate_descriptor(dummy_hash()), + let candidate_receipt = CandidateReceiptV2 { + descriptor: dummy_candidate_descriptor(dummy_hash()).into(), commitments_hash: Hash::zero(), }; @@ -859,7 +860,7 @@ fn test_statement_distribution_msg() -> StatementDistributionMessage { fn test_availability_recovery_msg() -> AvailabilityRecoveryMessage { let (sender, _) = oneshot::channel(); AvailabilityRecoveryMessage::RecoverAvailableData( - dummy_candidate_receipt(dummy_hash()), + dummy_candidate_receipt_v2(dummy_hash()), Default::default(), None, None, @@ -918,7 +919,7 @@ fn test_dispute_coordinator_msg() -> DisputeCoordinatorMessage { fn test_dispute_distribution_msg() -> DisputeDistributionMessage { let dummy_dispute_message = UncheckedDisputeMessage { - candidate_receipt: dummy_candidate_receipt(dummy_hash()), + candidate_receipt: dummy_candidate_receipt_v2(dummy_hash()), session_index: 0, invalid_vote: InvalidDisputeVote { validator_index: ValidatorIndex(0), diff --git a/polkadot/node/primitives/src/disputes/message.rs b/polkadot/node/primitives/src/disputes/message.rs index f9dec073bf5..d32ed4dadb6 100644 --- a/polkadot/node/primitives/src/disputes/message.rs +++ b/polkadot/node/primitives/src/disputes/message.rs @@ -25,7 +25,8 @@ use codec::{Decode, Encode}; use super::{InvalidDisputeVote, SignedDisputeStatement, ValidDisputeVote}; use polkadot_primitives::{ - CandidateReceipt, DisputeStatement, SessionIndex, SessionInfo, ValidatorIndex, + vstaging::CandidateReceiptV2 as CandidateReceipt, DisputeStatement, SessionIndex, SessionInfo, + ValidatorIndex, }; /// A dispute initiating/participating message that have been built from signed diff --git a/polkadot/node/primitives/src/disputes/mod.rs b/polkadot/node/primitives/src/disputes/mod.rs index 0f08b473365..71e2f0b16be 100644 --- a/polkadot/node/primitives/src/disputes/mod.rs +++ b/polkadot/node/primitives/src/disputes/mod.rs @@ -25,9 +25,9 @@ use sp_application_crypto::AppCrypto; use sp_keystore::{Error as KeystoreError, KeystorePtr}; use polkadot_primitives::{ - CandidateHash, CandidateReceipt, CompactStatement, DisputeStatement, EncodeAs, - InvalidDisputeStatementKind, SessionIndex, SigningContext, UncheckedSigned, - ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorSignature, + vstaging::CandidateReceiptV2 as CandidateReceipt, CandidateHash, CompactStatement, + DisputeStatement, EncodeAs, InvalidDisputeStatementKind, SessionIndex, SigningContext, + UncheckedSigned, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorSignature, }; /// `DisputeMessage` and related types. diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index a525b2bc977..e2e7aa92b11 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -30,10 +30,10 @@ use futures::Future; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use polkadot_primitives::{ - BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, ChunkIndex, CollatorPair, - CommittedCandidateReceipt, CompactStatement, CoreIndex, EncodeAs, Hash, HashT, HeadData, - Id as ParaId, PersistedValidationData, SessionIndex, Signed, UncheckedSigned, ValidationCode, - ValidationCodeHash, MAX_CODE_SIZE, MAX_POV_SIZE, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, BlakeTwo256, BlockNumber, + CandidateCommitments, CandidateHash, ChunkIndex, CollatorPair, CompactStatement, CoreIndex, + EncodeAs, Hash, HashT, HeadData, Id as ParaId, PersistedValidationData, SessionIndex, Signed, + UncheckedSigned, ValidationCode, ValidationCodeHash, MAX_CODE_SIZE, MAX_POV_SIZE, }; pub use sp_consensus_babe::{ AllowedSlots as BabeAllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch, diff --git a/polkadot/node/service/src/parachains_db/upgrade.rs b/polkadot/node/service/src/parachains_db/upgrade.rs index 808acf04b4e..52b010f0b5d 100644 --- a/polkadot/node/service/src/parachains_db/upgrade.rs +++ b/polkadot/node/service/src/parachains_db/upgrade.rs @@ -463,7 +463,7 @@ mod tests { v3::migration_helpers::{v1_to_latest_sanity_check, v2_fill_test_data}, }; use polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter; - use polkadot_primitives_test_helpers::dummy_candidate_receipt; + use polkadot_primitives_test_helpers::dummy_candidate_receipt_v2; #[test] fn test_paritydb_migrate_0_to_1() { @@ -617,7 +617,7 @@ mod tests { assert_eq!(db.num_columns(), super::columns::v3::NUM_COLUMNS as u32); let db = DbAdapter::new(db, columns::v3::ORDERED_COL); // Fill the approval voting column with test data. - v1_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt) + v1_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt_v2) .unwrap() }; @@ -648,7 +648,7 @@ mod tests { assert_eq!(db.num_columns(), super::columns::v3::NUM_COLUMNS as u32); let db = DbAdapter::new(db, columns::v3::ORDERED_COL); // Fill the approval voting column with test data. - v2_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt) + v2_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt_v2) .unwrap() }; diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index 293df9f6e6d..8633818e775 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -28,7 +28,7 @@ polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } polkadot-node-subsystem-types = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } -polkadot-primitives = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, features = ["test"] } polkadot-node-network-protocol = { workspace = true, default-features = true } polkadot-availability-recovery = { features = ["subsystem-benchmarks"], workspace = true, default-features = true } polkadot-availability-distribution = { workspace = true, default-features = true } diff --git a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs index a3a475ac6b9..24cd734c6ae 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs @@ -29,8 +29,8 @@ use polkadot_node_subsystem_types::messages::{ }; use polkadot_overseer::AllMessages; use polkadot_primitives::{ - BlockNumber, CandidateEvent, CandidateReceipt, CoreIndex, GroupIndex, Hash, Header, - Id as ParaId, Slot, ValidatorIndex, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlockNumber, CoreIndex, GroupIndex, Hash, Header, Id as ParaId, Slot, ValidatorIndex, }; use polkadot_primitives_test_helpers::dummy_candidate_receipt_bad_sig; use rand::{seq::SliceRandom, SeedableRng}; @@ -189,7 +189,7 @@ pub fn make_header(parent_hash: Hash, slot: Slot, number: u32) -> Header { fn make_candidate(para_id: ParaId, hash: &Hash) -> CandidateReceipt { let mut r = dummy_candidate_receipt_bad_sig(*hash, Some(Default::default())); r.descriptor.para_id = para_id; - r + r.into() } /// Helper function to create a list of candidates that are included in the block diff --git a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs index 807afb0438c..79de6e72fc8 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs @@ -40,8 +40,8 @@ use polkadot_node_primitives::approval::{ v2::{CoreBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }; use polkadot_primitives::{ - ApprovalVoteMultipleCandidates, CandidateEvent, CandidateHash, CandidateIndex, CoreIndex, Hash, - SessionInfo, Slot, ValidatorId, ValidatorIndex, ASSIGNMENT_KEY_TYPE_ID, + vstaging::CandidateEvent, ApprovalVoteMultipleCandidates, CandidateHash, CandidateIndex, + CoreIndex, Hash, SessionInfo, Slot, ValidatorId, ValidatorIndex, ASSIGNMENT_KEY_TYPE_ID, }; use rand::{seq::SliceRandom, RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs index 29ebc4a419a..1b20960a3f8 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -66,8 +66,9 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers::mock::new_block_import_info; use polkadot_overseer::Handle as OverseerHandleReal; use polkadot_primitives::{ - BlockNumber, CandidateEvent, CandidateIndex, CandidateReceipt, Hash, Header, Slot, ValidatorId, - ValidatorIndex, ASSIGNMENT_KEY_TYPE_ID, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt}, + BlockNumber, CandidateIndex, Hash, Header, Slot, ValidatorId, ValidatorIndex, + ASSIGNMENT_KEY_TYPE_ID, }; use prometheus::Registry; use sc_keystore::LocalKeystore; diff --git a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs index a99f013195f..8ac9796acb6 100644 --- a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs @@ -391,7 +391,7 @@ pub async fn benchmark_availability_write( candidate_hash: backed_candidate.hash(), n_validators: config.n_validators as u32, available_data, - expected_erasure_root: backed_candidate.descriptor().erasure_root, + expected_erasure_root: backed_candidate.descriptor().erasure_root(), tx, core_index: CoreIndex(core_index as u32), node_features: node_features_with_chunk_mapping_enabled(), 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 173b23f6b76..511795970e6 100644 --- a/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/test_state.rs @@ -34,8 +34,9 @@ use polkadot_node_subsystem_test_helpers::{ use polkadot_node_subsystem_util::availability_chunks::availability_chunk_indices; use polkadot_overseer::BlockInfo; use polkadot_primitives::{ - AvailabilityBitfield, BlockNumber, CandidateHash, CandidateReceipt, ChunkIndex, CoreIndex, - Hash, HeadData, Header, PersistedValidationData, Signed, SigningContext, ValidatorIndex, + vstaging::{CandidateReceiptV2 as CandidateReceipt, MutateDescriptorV2}, + AvailabilityBitfield, BlockNumber, CandidateHash, ChunkIndex, CoreIndex, Hash, HeadData, + Header, PersistedValidationData, Signed, SigningContext, ValidatorIndex, }; use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash}; use sp_core::H256; @@ -148,7 +149,10 @@ impl TestState { test_state.chunks.push(new_chunks); test_state.available_data.push(new_available_data); test_state.pov_size_to_candidate.insert(pov_size, index); - test_state.candidate_receipt_templates.push(candidate_receipt); + test_state.candidate_receipt_templates.push(CandidateReceipt { + descriptor: candidate_receipt.descriptor.into(), + commitments_hash: candidate_receipt.commitments_hash, + }); } test_state.block_infos = (1..=config.num_blocks) @@ -189,7 +193,9 @@ impl TestState { test_state.candidate_receipt_templates[candidate_index].clone(); // Make it unique. - candidate_receipt.descriptor.relay_parent = Hash::from_low_u64_be(index as u64); + candidate_receipt + .descriptor + .set_relay_parent(Hash::from_low_u64_be(index as u64)); // Store the new candidate in the state test_state.candidate_hashes.insert(candidate_receipt.hash(), candidate_index); 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 61523de1f1b..6c54d14448a 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -26,9 +26,10 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_types::OverseerSignal; use polkadot_primitives::{ - node_features, ApprovalVotingParams, AsyncBackingParams, CandidateEvent, CandidateReceipt, - CoreState, GroupIndex, GroupRotationInfo, IndexedVec, NodeFeatures, OccupiedCore, - ScheduledCore, SessionIndex, SessionInfo, ValidationCode, ValidatorIndex, + node_features, + vstaging::{CandidateEvent, CandidateReceiptV2 as CandidateReceipt, CoreState, OccupiedCore}, + ApprovalVotingParams, AsyncBackingParams, GroupIndex, GroupRotationInfo, IndexedVec, + NodeFeatures, ScheduledCore, SessionIndex, SessionInfo, ValidationCode, ValidatorIndex, }; use sp_consensus_babe::Epoch as BabeEpoch; use sp_core::H256; 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 88b5e8b76b6..2d2e9434b76 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -41,12 +41,15 @@ use polkadot_node_subsystem_test_helpers::{ }; use polkadot_overseer::BlockInfo; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CompactStatement, - Hash, Header, Id, PersistedValidationData, SessionInfo, SignedStatement, SigningContext, - UncheckedSigned, ValidatorIndex, ValidatorPair, + vstaging::{ + CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, MutateDescriptorV2, + }, + BlockNumber, CandidateHash, CompactStatement, Hash, Header, Id, PersistedValidationData, + SessionInfo, SignedStatement, SigningContext, UncheckedSigned, ValidatorIndex, ValidatorPair, }; use polkadot_primitives_test_helpers::{ - dummy_committed_candidate_receipt, dummy_hash, dummy_head_data, dummy_pvd, + dummy_committed_candidate_receipt_v2, dummy_hash, dummy_head_data, dummy_pvd, }; use sc_network::{config::IncomingRequest, ProtocolName}; use sp_core::{Pair, H256}; @@ -125,8 +128,8 @@ impl TestState { let candidate_index = *pov_size_to_candidate.get(pov_size).expect("pov_size always exists; qed"); let mut receipt = receipt_templates[candidate_index].clone(); - receipt.descriptor.para_id = Id::new(core_idx as u32 + 1); - receipt.descriptor.relay_parent = block_info.hash; + receipt.descriptor.set_para_id(Id::new(core_idx as u32 + 1)); + receipt.descriptor.set_relay_parent(block_info.hash); state.candidate_receipts.entry(block_info.hash).or_default().push( CandidateReceipt { @@ -240,7 +243,7 @@ fn generate_receipt_templates( pov_size_to_candidate .iter() .map(|(&pov_size, &index)| { - let mut receipt = dummy_committed_candidate_receipt(dummy_hash()); + let mut receipt = dummy_committed_candidate_receipt_v2(dummy_hash()); let (_, erasure_root) = derive_erasure_chunks_with_proofs_and_root( n_validators, &AvailableData { @@ -249,8 +252,8 @@ fn generate_receipt_templates( }, |_, _| {}, ); - receipt.descriptor.persisted_validation_data_hash = pvd.hash(); - receipt.descriptor.erasure_root = erasure_root; + receipt.descriptor.set_persisted_validation_data_hash(pvd.hash()); + receipt.descriptor.set_erasure_root(erasure_root); receipt }) .collect() diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 0017adb4556..ba1ba5755be 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -43,15 +43,18 @@ use polkadot_node_primitives::{ ValidationResult, }; use polkadot_primitives::{ - async_backing, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BackedCandidate, - BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, CandidateIndex, - CandidateReceipt, CollatorId, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, - ExecutorParams, GroupIndex, GroupRotationInfo, Hash, HeadData, Header as BlockHeader, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, MultiDisputeStatementSet, - NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - PvfExecKind as RuntimePvfExecKind, SessionIndex, SessionInfo, SignedAvailabilityBitfield, - SignedAvailabilityBitfields, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - ValidatorSignature, + async_backing, slashing, vstaging, + vstaging::{ + BackedCandidate, CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + }, + ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateHash, + CandidateIndex, CollatorId, CoreIndex, DisputeState, ExecutorParams, GroupIndex, + GroupRotationInfo, Hash, HeadData, Header as BlockHeader, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, MultiDisputeStatementSet, NodeFeatures, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, PvfExecKind as RuntimePvfExecKind, SessionIndex, + SessionInfo, SignedAvailabilityBitfield, SignedAvailabilityBitfields, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use polkadot_statement_table::v2::Misbehavior; use std::{ @@ -708,7 +711,7 @@ pub enum RuntimeApiRequest { CandidatePendingAvailability(ParaId, RuntimeApiSender>), /// Get all events concerning candidates (backing, inclusion, time-out) in the parent of /// the block in whose state this request is executed. - CandidateEvents(RuntimeApiSender>), + CandidateEvents(RuntimeApiSender>), /// Get the execution environment parameter set by session index SessionExecutorParams(SessionIndex, RuntimeApiSender>), /// Get the session info for the given session, if stored. @@ -724,7 +727,7 @@ pub enum RuntimeApiRequest { /// Get information about the BABE epoch the block was included in. CurrentBabeEpoch(RuntimeApiSender), /// Get all disputes in relation to a relay parent. - FetchOnChainVotes(RuntimeApiSender>), + FetchOnChainVotes(RuntimeApiSender>), /// Submits a PVF pre-checking statement into the transaction pool. SubmitPvfCheckStatement(PvfCheckStatement, ValidatorSignature, RuntimeApiSender<()>), /// Returns code hashes of PVFs that require pre-checking by validators in the active set. @@ -759,7 +762,7 @@ pub enum RuntimeApiRequest { /// Returns all disabled validators at a given block height. DisabledValidators(RuntimeApiSender>), /// Get the backing state of the given para. - ParaBackingState(ParaId, RuntimeApiSender>), + ParaBackingState(ParaId, RuntimeApiSender>), /// Get candidate's acceptance limitations for asynchronous backing for a relay parent. /// /// If it's not supported by the Runtime, the async backing is said to be disabled. @@ -1256,7 +1259,7 @@ impl HypotheticalCandidate { /// Get the `ParaId` of the hypothetical candidate. pub fn candidate_para(&self) -> ParaId { match *self { - HypotheticalCandidate::Complete { ref receipt, .. } => receipt.descriptor().para_id, + HypotheticalCandidate::Complete { ref receipt, .. } => receipt.descriptor.para_id(), HypotheticalCandidate::Incomplete { candidate_para, .. } => candidate_para, } } @@ -1275,7 +1278,7 @@ impl HypotheticalCandidate { pub fn relay_parent(&self) -> Hash { match *self { HypotheticalCandidate::Complete { ref receipt, .. } => - receipt.descriptor().relay_parent, + receipt.descriptor.relay_parent(), HypotheticalCandidate::Incomplete { candidate_relay_parent, .. } => candidate_relay_parent, } @@ -1285,7 +1288,7 @@ impl HypotheticalCandidate { pub fn output_head_data_hash(&self) -> Option { match *self { HypotheticalCandidate::Complete { ref receipt, .. } => - Some(receipt.descriptor.para_head), + Some(receipt.descriptor.para_head()), HypotheticalCandidate::Incomplete { .. } => None, } } @@ -1308,10 +1311,10 @@ impl HypotheticalCandidate { } /// Get the validation code hash, if the candidate is complete. - pub fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + pub fn validation_code_hash(&self) -> Option { match *self { HypotheticalCandidate::Complete { ref receipt, .. } => - Some(&receipt.descriptor.validation_code_hash), + Some(receipt.descriptor.validation_code_hash()), HypotheticalCandidate::Incomplete { .. } => None, } } diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs index a8af8b7996f..4b96009f44b 100644 --- a/polkadot/node/subsystem-types/src/runtime_client.rs +++ b/polkadot/node/subsystem-types/src/runtime_client.rs @@ -16,12 +16,18 @@ use async_trait::async_trait; use polkadot_primitives::{ - async_backing, runtime_api::ParachainHost, slashing, ApprovalVotingParams, Block, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Header, Id, - InboundDownwardMessage, InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, - PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, - ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + async_backing, + runtime_api::ParachainHost, + slashing, vstaging, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, Block, BlockNumber, CandidateCommitments, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash, Header, Id, InboundDownwardMessage, + InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, + PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, ValidatorSignature, }; use sc_client_api::{AuxStore, HeaderBackend}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; @@ -311,7 +317,7 @@ pub trait RuntimeApiSubsystemClient { &self, at: Hash, para_id: Id, - ) -> Result, ApiError>; + ) -> Result, ApiError>; // === v8 === @@ -380,10 +386,7 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client - .runtime_api() - .availability_cores(at) - .map(|cores| cores.into_iter().map(|core| core.into()).collect::>()) + self.client.runtime_api().availability_cores(at) } async fn persisted_validation_data( @@ -436,10 +439,7 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client - .runtime_api() - .candidate_pending_availability(at, para_id) - .map(|maybe_candidate| maybe_candidate.map(|candidate| candidate.into())) + self.client.runtime_api().candidate_pending_availability(at, para_id) } async fn candidates_pending_availability( @@ -447,19 +447,11 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client - .runtime_api() - .candidates_pending_availability(at, para_id) - .map(|candidates| { - candidates.into_iter().map(|candidate| candidate.into()).collect::>() - }) + self.client.runtime_api().candidates_pending_availability(at, para_id) } async fn candidate_events(&self, at: Hash) -> Result>, ApiError> { - self.client - .runtime_api() - .candidate_events(at) - .map(|events| events.into_iter().map(|event| event.into()).collect::>()) + self.client.runtime_api().candidate_events(at) } async fn dmq_contents( @@ -490,10 +482,7 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client - .runtime_api() - .on_chain_votes(at) - .map(|maybe_votes| maybe_votes.map(|votes| votes.into())) + self.client.runtime_api().on_chain_votes(at) } async fn session_executor_params( @@ -604,13 +593,8 @@ where &self, at: Hash, para_id: Id, - ) -> Result, ApiError> { - self.client - .runtime_api() - .para_backing_state(at, para_id) - .map(|maybe_backing_state| { - maybe_backing_state.map(|backing_state| backing_state.into()) - }) + ) -> Result, ApiError> { + self.client.runtime_api().para_backing_state(at, para_id) } async fn async_backing_params( diff --git a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs index 0c3b4074349..a2a6095b765 100644 --- a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs +++ b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs @@ -770,7 +770,7 @@ pub trait HypotheticalOrConcreteCandidate { /// Return a reference to the persisted validation data, if present. fn persisted_validation_data(&self) -> Option<&PersistedValidationData>; /// Return a reference to the validation code hash, if present. - fn validation_code_hash(&self) -> Option<&ValidationCodeHash>; + fn validation_code_hash(&self) -> Option; /// Return the parent head hash. fn parent_head_data_hash(&self) -> Hash; /// Return the output head hash, if present. @@ -790,7 +790,7 @@ impl HypotheticalOrConcreteCandidate for HypotheticalCandidate { self.persisted_validation_data() } - fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + fn validation_code_hash(&self) -> Option { self.validation_code_hash() } diff --git a/polkadot/node/subsystem-util/src/lib.rs b/polkadot/node/subsystem-util/src/lib.rs index 4bab4e80fe5..3bed1855894 100644 --- a/polkadot/node/subsystem-util/src/lib.rs +++ b/polkadot/node/subsystem-util/src/lib.rs @@ -41,12 +41,15 @@ use codec::Encode; use futures::channel::{mpsc, oneshot}; use polkadot_primitives::{ - async_backing::BackingState, slashing, AsyncBackingParams, AuthorityDiscoveryId, - CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, EncodeAs, - ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, OccupiedCoreAssumption, - PersistedValidationData, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signed, - SigningContext, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - ValidatorSignature, + slashing, + vstaging::{ + async_backing::BackingState, CandidateEvent, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, ScrapedOnChainVotes, + }, + AsyncBackingParams, AuthorityDiscoveryId, CandidateHash, CoreIndex, EncodeAs, ExecutorParams, + GroupIndex, GroupRotationInfo, Hash, Id as ParaId, OccupiedCoreAssumption, + PersistedValidationData, SessionIndex, SessionInfo, Signed, SigningContext, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; pub use rand; use runtime::get_disabled_validators_with_fallback; diff --git a/polkadot/node/subsystem-util/src/runtime/mod.rs b/polkadot/node/subsystem-util/src/runtime/mod.rs index 2f9d3ed7b4f..d84951ae136 100644 --- a/polkadot/node/subsystem-util/src/runtime/mod.rs +++ b/polkadot/node/subsystem-util/src/runtime/mod.rs @@ -30,11 +30,13 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_types::UnpinHandle; use polkadot_primitives::{ - node_features::FeatureIndex, slashing, AsyncBackingParams, CandidateEvent, CandidateHash, - CoreIndex, CoreState, EncodeAs, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, - Id as ParaId, IndexedVec, NodeFeatures, OccupiedCore, ScrapedOnChainVotes, SessionIndex, - SessionInfo, Signed, SigningContext, UncheckedSigned, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, LEGACY_MIN_BACKING_VOTES, + node_features::FeatureIndex, + slashing, + vstaging::{CandidateEvent, CoreState, OccupiedCore, ScrapedOnChainVotes}, + AsyncBackingParams, CandidateHash, CoreIndex, EncodeAs, ExecutorParams, GroupIndex, + GroupRotationInfo, Hash, Id as ParaId, IndexedVec, NodeFeatures, SessionIndex, SessionInfo, + Signed, SigningContext, UncheckedSigned, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, LEGACY_MIN_BACKING_VOTES, }; use std::collections::{BTreeMap, VecDeque}; diff --git a/polkadot/parachain/test-parachains/adder/collator/src/lib.rs b/polkadot/parachain/test-parachains/adder/collator/src/lib.rs index daeb8bc915d..a2fb623331a 100644 --- a/polkadot/parachain/test-parachains/adder/collator/src/lib.rs +++ b/polkadot/parachain/test-parachains/adder/collator/src/lib.rs @@ -236,7 +236,7 @@ impl Collator { if let Ok(res) = recv.await { if !matches!( res.statement.payload(), - Statement::Seconded(s) if s.descriptor.pov_hash == compressed_pov.hash(), + Statement::Seconded(s) if s.descriptor.pov_hash() == compressed_pov.hash(), ) { log::error!( "Seconded statement should match our collation: {:?}", diff --git a/polkadot/parachain/test-parachains/undying/collator/src/lib.rs b/polkadot/parachain/test-parachains/undying/collator/src/lib.rs index 920099f4499..448c181ae06 100644 --- a/polkadot/parachain/test-parachains/undying/collator/src/lib.rs +++ b/polkadot/parachain/test-parachains/undying/collator/src/lib.rs @@ -282,7 +282,7 @@ impl Collator { if let Ok(res) = recv.await { if !matches!( res.statement.payload(), - Statement::Seconded(s) if s.descriptor.pov_hash == compressed_pov.hash(), + Statement::Seconded(s) if s.descriptor.pov_hash() == compressed_pov.hash(), ) { log::error!( "Seconded statement should match our collation: {:?}", diff --git a/polkadot/primitives/src/v8/mod.rs b/polkadot/primitives/src/v8/mod.rs index cca327df42c..fdcb9fe8fb7 100644 --- a/polkadot/primitives/src/v8/mod.rs +++ b/polkadot/primitives/src/v8/mod.rs @@ -484,7 +484,7 @@ pub fn collator_signature_payload>( payload } -fn check_collator_signature>( +pub(crate) fn check_collator_signature>( relay_parent: &H, para_id: &Id, persisted_validation_data_hash: &Hash, diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index bc687f7e2fb..265fcd899d7 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -107,14 +107,42 @@ impl From> for CandidateDescriptor { } } -#[cfg(any(feature = "runtime-benchmarks", feature = "test"))] -impl From> for CandidateDescriptorV2 { +fn clone_into_array(slice: &[T]) -> A +where + A: Default + AsMut<[T]>, + T: Clone, +{ + let mut a = A::default(); + >::as_mut(&mut a).clone_from_slice(slice); + a +} + +impl From> for CandidateDescriptorV2 { fn from(value: CandidateDescriptor) -> Self { - Decode::decode(&mut value.encode().as_slice()).unwrap() + let collator = value.collator.as_slice(); + + Self { + para_id: value.para_id, + relay_parent: value.relay_parent, + // Use first byte of the `collator` field. + version: InternalVersion(collator[0]), + // Use next 2 bytes of the `collator` field. + core_index: u16::from_ne_bytes(clone_into_array(&collator[1..=2])), + // Use next 4 bytes of the `collator` field. + session_index: SessionIndex::from_ne_bytes(clone_into_array(&collator[3..=6])), + // Use remaing 25 bytes of the `collator` field. + reserved1: clone_into_array(&collator[7..]), + persisted_validation_data_hash: value.persisted_validation_data_hash, + pov_hash: value.pov_hash, + erasure_root: value.erasure_root, + reserved2: value.signature.into_inner().0, + para_head: value.para_head, + validation_code_hash: value.validation_code_hash, + } } } -impl CandidateDescriptorV2 { +impl> CandidateDescriptorV2 { /// Constructor pub fn new( para_id: Id, @@ -143,17 +171,74 @@ impl CandidateDescriptorV2 { } } - /// Set the PoV size in the descriptor. Only for tests. - #[cfg(feature = "test")] - pub fn set_pov_hash(&mut self, pov_hash: Hash) { + /// Check the signature of the collator within this descriptor. + pub fn check_collator_signature(&self) -> Result<(), ()> { + // Return `Ok` if collator signature is not included (v2+ descriptor). + let Some(collator) = self.collator() else { return Ok(()) }; + + let Some(signature) = self.signature() else { return Ok(()) }; + + super::v8::check_collator_signature( + &self.relay_parent, + &self.para_id, + &self.persisted_validation_data_hash, + &self.pov_hash, + &self.validation_code_hash, + &collator, + &signature, + ) + } +} + +/// A trait to allow changing the descriptor field values in tests. +#[cfg(feature = "test")] + +pub trait MutateDescriptorV2 { + /// Set the relay parent of the descriptor. + fn set_relay_parent(&mut self, relay_parent: H); + /// Set the `ParaId` of the descriptor. + fn set_para_id(&mut self, para_id: Id); + /// Set the PoV hash of the descriptor. + fn set_pov_hash(&mut self, pov_hash: Hash); + /// Set the version field of the descriptor. + 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 erasure root of the descriptor. + fn set_erasure_root(&mut self, erasure_root: Hash); + /// Set the para head of the descriptor. + fn set_para_head(&mut self, para_head: Hash); +} + +#[cfg(feature = "test")] +impl MutateDescriptorV2 for CandidateDescriptorV2 { + fn set_para_id(&mut self, para_id: Id) { + self.para_id = para_id; + } + + fn set_relay_parent(&mut self, relay_parent: H) { + self.relay_parent = relay_parent; + } + + fn set_pov_hash(&mut self, pov_hash: Hash) { self.pov_hash = pov_hash; } - /// Set the version in the descriptor. Only for tests. - #[cfg(feature = "test")] - pub fn set_version(&mut self, version: InternalVersion) { + fn set_version(&mut self, version: InternalVersion) { self.version = version; } + + fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash) { + self.persisted_validation_data_hash = persisted_validation_data_hash; + } + + fn set_erasure_root(&mut self, erasure_root: Hash) { + self.erasure_root = erasure_root; + } + + fn set_para_head(&mut self, para_head: Hash) { + self.para_head = para_head; + } } /// A candidate-receipt at version 2. @@ -233,6 +318,24 @@ impl CandidateReceiptV2 { } } +impl From> for CandidateReceiptV2 { + fn from(value: super::v8::CandidateReceipt) -> Self { + CandidateReceiptV2 { + descriptor: value.descriptor.into(), + commitments_hash: value.commitments_hash, + } + } +} + +impl From> for CommittedCandidateReceiptV2 { + fn from(value: super::v8::CommittedCandidateReceipt) -> Self { + CommittedCandidateReceiptV2 { + descriptor: value.descriptor.into(), + commitments: value.commitments, + } + } +} + impl CommittedCandidateReceiptV2 { /// Transforms this into a plain `CandidateReceipt`. pub fn to_plain(&self) -> CandidateReceiptV2 { @@ -368,7 +471,7 @@ pub enum CandidateReceiptError { macro_rules! impl_getter { ($field:ident, $type:ident) => { - /// Returns the value of $field field. + /// Returns the value of `$field` field. pub fn $field(&self) -> $type { self.$field } @@ -703,6 +806,13 @@ pub struct OccupiedCore { pub candidate_descriptor: CandidateDescriptorV2, } +impl OccupiedCore { + /// Get the Para currently occupying this core. + pub fn para_id(&self) -> Id { + self.candidate_descriptor.para_id + } +} + /// The state of a particular availability core. #[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(PartialEq))] @@ -724,6 +834,28 @@ pub enum CoreState { Free, } +impl CoreState { + /// Returns the scheduled `ParaId` for the core or `None` if nothing is scheduled. + /// + /// This function is deprecated. `ClaimQueue` should be used to obtain the scheduled `ParaId`s + /// for each core. + #[deprecated( + note = "`para_id` will be removed. Use `ClaimQueue` to query the scheduled `para_id` instead." + )] + pub fn para_id(&self) -> Option { + match self { + Self::Occupied(ref core) => core.next_up_on_available.as_ref().map(|n| n.para_id), + Self::Scheduled(core) => Some(core.para_id), + Self::Free => None, + } + } + + /// Is this core state `Self::Occupied`? + pub fn is_occupied(&self) -> bool { + matches!(self, Self::Occupied(_)) + } +} + impl From> for super::v8::OccupiedCore { fn from(value: OccupiedCore) -> Self { Self { @@ -841,6 +973,25 @@ mod tests { assert_eq!(old_ccr.hash(), new_ccr.hash()); } + #[test] + fn test_from_v1_descriptor() { + let mut old_ccr = dummy_old_committed_candidate_receipt().to_plain(); + old_ccr.descriptor.collator = dummy_collator_id(); + old_ccr.descriptor.signature = dummy_collator_signature(); + + let mut new_ccr = dummy_committed_candidate_receipt_v2().to_plain(); + + // Override descriptor from old candidate receipt. + new_ccr.descriptor = old_ccr.descriptor.clone().into(); + + // We get same candidate hash. + assert_eq!(old_ccr.hash(), new_ccr.hash()); + + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V1); + assert_eq!(old_ccr.descriptor.collator, new_ccr.descriptor.collator().unwrap()); + assert_eq!(old_ccr.descriptor.signature, new_ccr.descriptor.signature().unwrap()); + } + #[test] fn invalid_version_descriptor() { let mut new_ccr = dummy_committed_candidate_receipt_v2(); diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index b0f78717dd9..c2eccafef78 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -44,7 +44,7 @@ pub fn dummy_candidate_receipt>(relay_parent: H) -> CandidateRece } /// Creates a v2 candidate receipt with filler data. -pub fn dummy_candidate_receipt_v2>(relay_parent: H) -> CandidateReceiptV2 { +pub fn dummy_candidate_receipt_v2 + Copy>(relay_parent: H) -> CandidateReceiptV2 { CandidateReceiptV2:: { commitments_hash: dummy_candidate_commitments(dummy_head_data()).hash(), descriptor: dummy_candidate_descriptor_v2(relay_parent), @@ -62,7 +62,7 @@ pub fn dummy_committed_candidate_receipt>( } /// Creates a v2 committed candidate receipt with filler data. -pub fn dummy_committed_candidate_receipt_v2>( +pub fn dummy_committed_candidate_receipt_v2 + Copy>( relay_parent: H, ) -> CommittedCandidateReceiptV2 { CommittedCandidateReceiptV2 { @@ -88,6 +88,23 @@ pub fn dummy_candidate_receipt_bad_sig( } } +/// Create a candidate receipt with a bogus signature and filler data. Optionally set the commitment +/// hash with the `commitments` arg. +pub fn dummy_candidate_receipt_v2_bad_sig( + relay_parent: Hash, + commitments: impl Into>, +) -> CandidateReceiptV2 { + let commitments_hash = if let Some(commitments) = commitments.into() { + commitments + } else { + dummy_candidate_commitments(dummy_head_data()).hash() + }; + CandidateReceiptV2:: { + commitments_hash, + descriptor: dummy_candidate_descriptor_bad_sig(relay_parent).into(), + } +} + /// Create candidate commitments with filler data. pub fn dummy_candidate_commitments(head_data: impl Into>) -> CandidateCommitments { CandidateCommitments { @@ -144,7 +161,9 @@ pub fn dummy_candidate_descriptor>(relay_parent: H) -> CandidateD } /// Create a v2 candidate descriptor with filler data. -pub fn dummy_candidate_descriptor_v2>(relay_parent: H) -> CandidateDescriptorV2 { +pub fn dummy_candidate_descriptor_v2 + Copy>( + relay_parent: H, +) -> CandidateDescriptorV2 { let invalid = Hash::zero(); let descriptor = make_valid_candidate_descriptor_v2( 1.into(), @@ -208,7 +227,7 @@ pub fn make_candidate( parent_head: HeadData, head_data: HeadData, validation_code_hash: ValidationCodeHash, -) -> (CommittedCandidateReceipt, PersistedValidationData) { +) -> (CommittedCandidateReceiptV2, PersistedValidationData) { let pvd = dummy_pvd(parent_head, relay_parent_number); let commitments = CandidateCommitments { head_data, @@ -225,7 +244,8 @@ pub fn make_candidate( candidate.descriptor.para_id = para_id; candidate.descriptor.persisted_validation_data_hash = pvd.hash(); candidate.descriptor.validation_code_hash = validation_code_hash; - let candidate = CommittedCandidateReceipt { descriptor: candidate.descriptor, commitments }; + let candidate = + CommittedCandidateReceiptV2 { descriptor: candidate.descriptor.into(), commitments }; (candidate, pvd) } @@ -269,7 +289,7 @@ pub fn make_valid_candidate_descriptor>( } /// Create a v2 candidate descriptor. -pub fn make_valid_candidate_descriptor_v2>( +pub fn make_valid_candidate_descriptor_v2 + Copy>( para_id: ParaId, relay_parent: H, core_index: CoreIndex, @@ -335,11 +355,11 @@ impl std::default::Default for TestCandidateBuilder { impl TestCandidateBuilder { /// Build a `CandidateReceipt`. - pub fn build(self) -> CandidateReceipt { + pub fn build(self) -> CandidateReceiptV2 { let mut descriptor = dummy_candidate_descriptor(self.relay_parent); descriptor.para_id = self.para_id; descriptor.pov_hash = self.pov_hash; - CandidateReceipt { descriptor, commitments_hash: self.commitments_hash } + CandidateReceipt { descriptor, commitments_hash: self.commitments_hash }.into() } } diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 188ba4995d8..8513d2dad91 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -39,7 +39,7 @@ use assert_matches::assert_matches; use codec::DecodeAll; use frame_support::assert_noop; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CollatorId, CollatorSignature, + vstaging::MutateDescriptorV2, BlockNumber, CandidateCommitments, CollatorId, CollatorSignature, CompactStatement as Statement, Hash, SignedAvailabilityBitfield, SignedStatement, ValidationCode, ValidatorId, ValidityAttestation, PARACHAIN_KEY_TYPE_ID, }; diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index 2c65298baf0..eef26b83368 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -61,7 +61,9 @@ mod enter { use frame_support::assert_ok; use frame_system::limits; use polkadot_primitives::{ - vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2, InternalVersion}, + vstaging::{ + CandidateDescriptorV2, CommittedCandidateReceiptV2, InternalVersion, MutateDescriptorV2, + }, AvailabilityBitfield, CandidateDescriptor, UncheckedSigned, }; use sp_runtime::Perbill; diff --git a/polkadot/statement-table/src/lib.rs b/polkadot/statement-table/src/lib.rs index 469c877eafc..68febf76feb 100644 --- a/polkadot/statement-table/src/lib.rs +++ b/polkadot/statement-table/src/lib.rs @@ -35,8 +35,8 @@ pub use generic::{Config, Context, Table}; pub mod v2 { use crate::generic; use polkadot_primitives::{ - CandidateHash, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement, - CoreIndex, ValidatorIndex, ValidatorSignature, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateHash, + CompactStatement as PrimitiveStatement, CoreIndex, ValidatorIndex, ValidatorSignature, }; /// Statements about candidates on the network. diff --git a/prdoc/pr_5679.prdoc b/prdoc/pr_5679.prdoc new file mode 100644 index 00000000000..59c36ecb933 --- /dev/null +++ b/prdoc/pr_5679.prdoc @@ -0,0 +1,80 @@ +title: Switch to new `CandidateReceipt` primitives +doc: +- audience: + - Node Dev + - Runtime Dev + description: | + This change is just plumbing work and updates all crate interfaces to use the new primitives. + It doesn't alter any functionality and is required before implementing RFC103 on the + node side. +crates: +- name: polkadot-primitives + bump: major +- name: polkadot-runtime-parachains + bump: patch +- name: rococo-runtime + bump: patch +- name: westend-runtime + bump: patch +- name: cumulus-relay-chain-inprocess-interface + bump: major +- name: polkadot-service + bump: patch +- name: polkadot-node-subsystem-types + bump: major +- name: polkadot + bump: patch +- name: cumulus-client-network + bump: major +- name: cumulus-client-pov-recovery + bump: major +- name: cumulus-relay-chain-interface + bump: major +- name: cumulus-relay-chain-minimal-node + bump: major +- name: cumulus-relay-chain-rpc-interface + bump: major +- name: polkadot-node-collation-generation + bump: major +- name: polkadot-node-core-approval-voting + bump: major +- name: polkadot-node-core-av-store + bump: major +- name: polkadot-node-core-backing + bump: major +- name: polkadot-node-core-bitfield-signing + bump: major +- name: polkadot-node-core-candidate-validation + bump: major +- name: polkadot-node-core-dispute-coordinator + bump: major +- name: polkadot-node-core-parachains-inherent + bump: major +- name: polkadot-node-core-prospective-parachains + bump: major +- name: polkadot-node-core-provisioner + bump: major +- name: polkadot-node-core-runtime-api + bump: major +- name: polkadot-availability-distribution + bump: major +- name: polkadot-availability-recovery + bump: major +- name: polkadot-collator-protocol + bump: major +- name: polkadot-dispute-distribution + bump: major +- name: polkadot-node-network-protocol + bump: major +- name: polkadot-statement-distribution + bump: major +- name: polkadot-node-primitives + bump: major +- name: polkadot-node-subsystem-util + bump: major +- name: polkadot-statement-table + bump: major +- name: polkadot-overseer + bump: patch +- name: cumulus-client-consensus-common + bump: major -- GitLab From df66d76f96b47b567dbbee7a6a8f40d3c3bbad43 Mon Sep 17 00:00:00 2001 From: Tsvetomir Dimitrov Date: Mon, 28 Oct 2024 09:44:31 +0200 Subject: [PATCH 50/50] Fix a flaky zombienet test - 0004-coretime-smoke-test (#6236) In the test log I noticed that the batch transaction which configures the coretime chain fails. However when rerunning the transaction manually - it worked. Then I noticed that the coretime chain is initially registered via zombienet and then re-registered via `0004-configure-relay.js`. Because of this there is a period of time when it's not producing blocks and `0004-configure-broker.js` fails to setup the coretime chain. My theory is that the transaction has failed because the coretime chain is stalled during the re-registration. Fixes https://github.com/paritytech/polkadot-sdk/issues/6226 --- polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl b/polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl index cfb1ce7d982..b3a3b46ed78 100644 --- a/polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl +++ b/polkadot/zombienet_tests/smoke/0004-coretime-smoke-test.zndsl @@ -8,6 +8,9 @@ coretime-collator: is up # configure relay chain alice: js-script ./0004-configure-relay.js with "" return is 0 within 600 secs +# Coretime chain should be producing blocks when the extrinsic is sent +alice: parachain 1005 block height is at least 10 within 120 seconds + # configure broker chain coretime-collator: js-script ./0004-configure-broker.js with "" return is 0 within 600 secs -- GitLab