diff --git a/.github/scripts/common/lib.sh b/.github/scripts/common/lib.sh index 33ef2d3e7eda..bfb3120ad9bb 100755 --- a/.github/scripts/common/lib.sh +++ b/.github/scripts/common/lib.sh @@ -315,6 +315,7 @@ function import_gpg_keys() { ) & done wait + gpg -k $SEC } # Check the GPG signature for a given binary @@ -457,3 +458,15 @@ function get_polkadot_node_version_from_code() { # Remove the semicolon sed 's/;//g' } + +validate_stable_tag() { + tag="$1" + pattern='^stable[0-9]+(-[0-9]+)?$' + + if [[ $tag =~ $pattern ]]; then + echo $tag + else + echo "The input '$tag' does not match the pattern." + exit 1 + fi +} diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 000000000000..054c7d786ca9 --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,90 @@ +name: checks + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled] + merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + changes: + # TODO: remove once migration is complete or this workflow is fully stable + if: contains(github.event.label.name, 'GHA-migration') + permissions: + pull-requests: read + uses: ./.github/workflows/reusable-check-changed-files.yml + set-image: + # GitHub Actions allows using 'env' in a container context. + # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 + # This workaround sets the container image for each job using 'set-image' job output. + runs-on: ubuntu-latest + timeout-minutes: 20 + outputs: + IMAGE: ${{ steps.set_image.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - id: set_image + run: cat .github/env >> $GITHUB_OUTPUT + cargo-clippy: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image, changes] # , build-frame-omni-bencher ] + if: ${{ needs.changes.outputs.rust }} + timeout-minutes: 40 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + env: + RUSTFLAGS: "-D warnings" + SKIP_WASM_BUILD: 1 + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: script + run: | + forklift cargo clippy --all-targets --locked --workspace + forklift cargo clippy --all-targets --all-features --locked --workspace + check-try-runtime: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image, changes] # , build-frame-omni-bencher ] + if: ${{ needs.changes.outputs.rust }} + timeout-minutes: 40 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: script + run: | + forklift cargo check --locked --all --features try-runtime + # this is taken from cumulus + # Check that parachain-template will compile with `try-runtime` feature flag. + forklift cargo check --locked -p parachain-template-node --features try-runtime + # add after https://github.com/paritytech/substrate/pull/14502 is merged + # experimental code may rely on try-runtime and vice-versa + forklift cargo check --locked --all --features try-runtime,experimental + # check-core-crypto-features works fast without forklift + check-core-crypto-features: + runs-on: arc-runners-polkadot-sdk-beefy + needs: [set-image, changes] # , build-frame-omni-bencher ] + if: ${{ needs.changes.outputs.rust }} + timeout-minutes: 30 + container: + image: ${{ needs.set-image.outputs.IMAGE }} + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: script + run: | + cd substrate/primitives/core + ./check-features-variants.sh + cd - + cd substrate/primitives/application-crypto + ./check-features-variants.sh + cd - + cd substrate/primitives/keyring + ./check-features-variants.sh + cd - diff --git a/.github/workflows/command-inform.yml b/.github/workflows/command-inform.yml index 2825f4a60460..afdcf4c1b7b9 100644 --- a/.github/workflows/command-inform.yml +++ b/.github/workflows/command-inform.yml @@ -7,7 +7,8 @@ on: jobs: comment: runs-on: ubuntu-latest - if: github.event.issue.pull_request && startsWith(github.event.comment.body, 'bot ') + # Temporary disable the bot until the new command bot works properly + if: github.event.issue.pull_request && startsWith(github.event.comment.body, 'bot ') && false steps: - name: Inform that the new command exist uses: actions/github-script@v7 diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index d22dc8724f37..c06beb5e98eb 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -166,9 +166,13 @@ jobs: title: "[Don't merge] Update the ${{ matrix.template }} template to ${{ github.event.inputs.stable_release_branch }}" body: "The template has NOT been successfully built and needs to be inspected." branch: "update-template/${{ github.event.inputs.stable_release_branch }}" - - name: Push changes - run: | - git add -A . - git commit --allow-empty -m "Update to ${{ github.event.inputs.stable_release_branch }} triggered by ${{ github.event_name }}" - git push - working-directory: "${{ env.template-path }}" + - name: Create PR on success + uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v5 + with: + path: "${{ env.template-path }}" + token: ${{ steps.app_token.outputs.token }} + add-paths: | + ./* + title: "Update the ${{ matrix.template }} template to ${{ github.event.inputs.stable_release_branch }}" + body: "This synchronizes the template to the ${{ github.event.inputs.stable_release_branch }} branch." + branch: "update-template/${{ github.event.inputs.stable_release_branch }}" diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index cda10f2ebf15..f09ecf1c7998 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -45,7 +45,7 @@ on: type: string default: docker.io - # The owner is often the same than the Docker Hub username but does ont have to be. + # The owner is often the same as the Docker Hub username but does ont have to be. # In our case, it is not. owner: description: Owner of the container image repo @@ -58,6 +58,10 @@ on: default: v0.9.18 required: true + stable_tag: + description: Tag matching the actual stable release version in the format stableYYMM or stableYYMM-X for patch releases + required: true + permissions: contents: write @@ -74,6 +78,29 @@ env: VERSION: ${{ inputs.version }} jobs: + validate-inputs: + runs-on: ubuntu-latest + outputs: + stable_tag: ${{ steps.validate_inputs.outputs.stable_tag }} + + steps: + - name: Checkout sources + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Validate inputs + id: validate_inputs + run: | + . ./.github/scripts/common/lib.sh + + VERSION=$(filter_version_from_input "${{ inputs.version }}") + echo "VERSION=${VERSION}" >> $GITHUB_ENV + + RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") + echo "RELEASE_ID=${RELEASE_ID}" >> $GITHUB_ENV + + STABLE_TAG=$(validate_stable_tag ${{ inputs.stable_tag }}) + echo "stable_tag=${STABLE_TAG}" >> $GITHUB_OUTPUT + fetch-artifacts: # this job will be triggered for the polkadot-parachain rc and release or polkadot rc image build if: ${{ inputs.binary == 'polkadot-parachain' || inputs.binary == 'chain-spec-builder' || inputs.image_type == 'rc' }} runs-on: ubuntu-latest @@ -102,9 +129,6 @@ jobs: run: | . ./.github/scripts/common/lib.sh - VERSION=$(filter_version_from_input "${{ inputs.version }}") - echo "VERSION=${VERSION}" >> $GITHUB_ENV - fetch_release_artifacts_from_s3 - name: Fetch chain-spec-builder rc artifacts or release artifacts based on release id @@ -112,7 +136,7 @@ jobs: if: ${{ env.EVENT_NAME == 'workflow_dispatch' && inputs.binary == 'chain-spec-builder' }} run: | . ./.github/scripts/common/lib.sh - RELEASE_ID=$(check_release_id "${{ inputs.release_id }}") + fetch_release_artifacts - name: Upload artifacts @@ -124,7 +148,7 @@ jobs: build-container: # this job will be triggered for the polkadot-parachain rc and release or polkadot rc image build if: ${{ inputs.binary == 'polkadot-parachain' || inputs.binary == 'chain-spec-builder' || inputs.image_type == 'rc' }} runs-on: ubuntu-latest - needs: fetch-artifacts + needs: [fetch-artifacts, validate-inputs] environment: release steps: @@ -179,7 +203,7 @@ jobs: release=$( echo $VERSION | cut -f1 -d- ) echo "tag=latest" >> $GITHUB_OUTPUT echo "release=${release}" >> $GITHUB_OUTPUT - echo "stable=stable" >> $GITHUB_OUTPUT + echo "stable=${{ needs.validate-inputs.outputs.stable_tag }}" >> $GITHUB_OUTPUT - name: Build Injected Container image for polkadot rc or chain-spec-builder if: ${{ env.BINARY == 'polkadot' || env.BINARY == 'chain-spec-builder' }} @@ -257,7 +281,7 @@ jobs: build-polkadot-release-container: # this job will be triggered for polkadot release build if: ${{ inputs.binary == 'polkadot' && inputs.image_type == 'release' }} runs-on: ubuntu-latest - needs: fetch-latest-debian-package-version + needs: [fetch-latest-debian-package-version, validate-inputs] environment: release steps: - name: Checkout sources @@ -295,7 +319,7 @@ jobs: # TODO: The owner should be used below but buildx does not resolve the VARs # TODO: It would be good to get rid of this GHA that we don't really need. tags: | - parity/polkadot:stable + parity/polkadot:${{ needs.validate-inputs.outputs.stable_tag }} parity/polkadot:latest parity/polkadot:${{ needs.fetch-latest-debian-package-version.outputs.polkadot_container_tag }} build-args: | diff --git a/.github/workflows/tests-linux-stable.yml b/.github/workflows/tests-linux-stable.yml index da4bb40a2e78..4a13f5318f7d 100644 --- a/.github/workflows/tests-linux-stable.yml +++ b/.github/workflows/tests-linux-stable.yml @@ -6,7 +6,7 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened, ready_for_review ] + types: [opened, synchronize, reopened, ready_for_review, labeled] merge_group: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -14,6 +14,8 @@ concurrency: jobs: changes: + # TODO: remove once migration is complete or this workflow is fully stable + if: contains(github.event.label.name, 'GHA-migration') permissions: pull-requests: read uses: ./.github/workflows/reusable-check-changed-files.yml @@ -34,7 +36,7 @@ jobs: run: cat .github/env >> $GITHUB_OUTPUT test-linux-stable-int: - needs: [ set-image, changes ] + needs: [set-image, changes] if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 60 @@ -51,11 +53,11 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script - run: WASM_BUILD_NO_COLOR=1 time forklift cargo test -p staging-node-cli --release --locked -- --ignored + run: WASM_BUILD_NO_COLOR=1 forklift cargo test -p staging-node-cli --release --locked -- --ignored # https://github.com/paritytech/ci_cd/issues/864 test-linux-stable-runtime-benchmarks: - needs: [ set-image, changes ] + needs: [set-image, changes] if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 60 @@ -70,4 +72,55 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: script - run: time forklift cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet + run: forklift cargo nextest run --workspace --features runtime-benchmarks benchmark --locked --cargo-profile testnet + + test-linux-stable: + needs: [set-image, changes] + if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ matrix.runners }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + partition: [1/3, 2/3, 3/3] + runners: [arc-runners-polkadot-sdk-beefy, oldlinux] + container: + image: ${{ needs.set-image.outputs.IMAGE }} + # needed for tests that use unshare syscall + options: --security-opt seccomp=unconfined + env: + RUST_TOOLCHAIN: stable + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: script + run: | + # Fixes "detected dubious ownership" error in the ci + git config --global --add safe.directory '*' + forklift cargo nextest run \ + --workspace \ + --locked \ + --release \ + --no-fail-fast \ + --features try-runtime,experimental,riscv,ci-only-tests \ + --partition count:${{ matrix.partition }} + # run runtime-api tests with `enable-staging-api` feature on the 1st node + - name: runtime-api tests + if: ${{ matrix.partition == '1/3' }} + run: forklift cargo nextest run -p sp-api-test --features enable-staging-api + + confirm-required-jobs-passed: + runs-on: ubuntu-latest + name: All tests passed + # If any new job gets added, be sure to add it to this array + needs: + [ + test-linux-stable-int, + test-linux-stable-runtime-benchmarks, + test-linux-stable, + ] + steps: + - run: echo '### Good job! All the tests passed 🚀' >> $GITHUB_STEP_SUMMARY diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index 0103c6b76a2d..319c95ad6112 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -110,8 +110,6 @@ test-linux-stable-codecov: codecovcli -v do-upload -f target/coverage/result/report-${CI_NODE_INDEX}.lcov --disable-search -t ${CODECOV_TOKEN} -r paritytech/polkadot-sdk --commit-sha ${CI_COMMIT_SHA} --fail-on-error --git-service github; fi - # - test-linux-stable: stage: test extends: diff --git a/Cargo.lock b/Cargo.lock index 54a01f12f35c..6ebacc9ec5a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13443,23 +13443,17 @@ name = "polkadot-node-core-prospective-parachains" version = "6.0.0" dependencies = [ "assert_matches", - "bitvec", "fatality", "futures", - "parity-scale-codec", - "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", - "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "rand", "rstest", - "sc-keystore", - "sp-application-crypto", "sp-core", - "sp-keyring", - "sp-keystore", + "sp-tracing 16.0.0", "thiserror", "tracing-gum", ] @@ -13589,8 +13583,10 @@ dependencies = [ "nix 0.28.0", "parity-scale-codec", "polkadot-node-core-pvf-common", + "polkadot-node-primitives", "polkadot-parachain-primitives", "polkadot-primitives", + "sp-maybe-compressed-blob", "tracing-gum", ] @@ -13605,6 +13601,7 @@ dependencies = [ "nix 0.28.0", "parity-scale-codec", "polkadot-node-core-pvf-common", + "polkadot-node-primitives", "polkadot-primitives", "rayon", "rococo-runtime", diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs index 82f86e2b32ef..5b70ed490c63 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/genesis.rs @@ -70,7 +70,7 @@ pub fn genesis() -> Storage { }, assets: asset_hub_rococo_runtime::AssetsConfig { assets: vec![ - (RESERVABLE_ASSET_ID, AssetHubRococoAssetOwner::get(), true, ED), + (RESERVABLE_ASSET_ID, AssetHubRococoAssetOwner::get(), false, ED), (USDT_ID, AssetHubRococoAssetOwner::get(), true, ED), ], ..Default::default() @@ -81,7 +81,7 @@ pub fn genesis() -> Storage { ( PenpalTeleportableAssetLocation::get(), PenpalSiblingSovereignAccount::get(), - true, + false, ED, ), ], diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index fd84030ed13b..d20e059f9fea 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -21,7 +21,7 @@ use sp_core::{sr25519, storage::Storage}; use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, PenpalSiblingSovereignAccount, PenpalTeleportableAssetLocation, RESERVABLE_ASSET_ID, - SAFE_XCM_VERSION, + SAFE_XCM_VERSION, USDT_ID, }; use parachains_common::{AccountId, Balance}; @@ -65,7 +65,10 @@ pub fn genesis() -> Storage { ..Default::default() }, assets: asset_hub_westend_runtime::AssetsConfig { - assets: vec![(RESERVABLE_ASSET_ID, AssetHubWestendAssetOwner::get(), true, ED)], + assets: vec![ + (RESERVABLE_ASSET_ID, AssetHubWestendAssetOwner::get(), false, ED), + (USDT_ID, AssetHubWestendAssetOwner::get(), true, ED), + ], ..Default::default() }, foreign_assets: asset_hub_westend_runtime::ForeignAssetsConfig { @@ -74,7 +77,7 @@ pub fn genesis() -> Storage { ( PenpalTeleportableAssetLocation::get(), PenpalSiblingSovereignAccount::get(), - true, + false, ED, ), ], diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs index 260875088bbc..38c94b34aa2e 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs @@ -22,7 +22,7 @@ use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, Balance}; -use penpal_runtime::xcm_config::{LocalReservableFromAssetHub, RelayLocation}; +use penpal_runtime::xcm_config::{LocalReservableFromAssetHub, RelayLocation, UsdtFromAssetHub}; // Penpal pub const PARA_ID_A: u32 = 2000; pub const PARA_ID_B: u32 = 2001; @@ -81,6 +81,8 @@ pub fn genesis(para_id: u32) -> Storage { (RelayLocation::get(), PenpalAssetOwner::get(), true, ED), // Sufficient AssetHub asset representation (LocalReservableFromAssetHub::get(), PenpalAssetOwner::get(), true, ED), + // USDT from Asset Hub + (UsdtFromAssetHub::get(), PenpalAssetOwner::get(), true, ED), ], ..Default::default() }, diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index 7077fbbb0a9a..b3dec175e112 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -132,6 +132,7 @@ pub mod accounts { pub const EVE_STASH: &str = "Eve//stash"; pub const FERDIE_STASH: &str = "Ferdie//stash"; pub const FERDIE_BEEFY: &str = "Ferdie//stash"; + pub const DUMMY_EMPTY: &str = "JohnDoe"; pub fn init_balances() -> Vec { vec![ 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 6309c0584107..1a30fac6ba91 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 @@ -35,7 +35,8 @@ mod imports { // Cumulus pub use asset_test_utils::xcm_helpers; pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, + accounts::DUMMY_EMPTY, + get_account_id_from_seed, test_parachain_is_trusted_teleporter, 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/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index 8b9fedcd4947..329dbcac4b42 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -939,8 +939,11 @@ fn reserve_transfer_assets_from_system_para_to_para() { ); } -/// Reserve Transfers of a foreign asset and native asset from Parachain to System Para should +/// Reserve Transfers of a random asset and native asset from Parachain to System Para should /// work +/// Receiver is empty account to show deposit works as long as transfer includes enough DOT for ED. +/// Once we have https://github.com/paritytech/polkadot-sdk/issues/5298, +/// we should do equivalent test with USDT instead of DOT. #[test] fn reserve_transfer_assets_from_para_to_system_para() { // Init values for Parachain @@ -965,24 +968,23 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Fund Parachain's sender account with some foreign assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer.clone(), - asset_location_on_penpal, + asset_location_on_penpal.clone(), sender.clone(), asset_amount_to_send * 2, ); // Fund Parachain's sender account with some system assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer, - system_asset_location_on_penpal, + system_asset_location_on_penpal.clone(), sender.clone(), fee_amount_to_send * 2, ); + // Beneficiary is a new (empty) account + let receiver = get_account_id_from_seed::(DUMMY_EMPTY); // Init values for System Parachain - let receiver = AssetHubRococoReceiver::get(); let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - let system_para_native_asset_location = RelayLocation::get(); - let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); let ah_asset_owner = AssetHubRococoAssetOwner::get(); let ah_asset_owner_signer = ::RuntimeOrigin::signed(ah_asset_owner); @@ -1017,11 +1019,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query initial balances let sender_system_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) + >::balance(system_asset_location_on_penpal.clone(), &sender) }); let sender_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location.clone(), &sender) + >::balance(asset_location_on_penpal.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; let receiver_assets_before = AssetHubRococo::execute_with(|| { @@ -1038,11 +1040,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query final balances let sender_system_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) + >::balance(system_asset_location_on_penpal, &sender) }); let sender_foreign_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location, &sender) + >::balance(asset_location_on_penpal, &sender) }); let receiver_balance_after = test.receiver.balance; let receiver_assets_after = AssetHubRococo::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs index f8190e11c51c..3320392b495d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/treasury.rs @@ -14,7 +14,10 @@ // limitations under the License. use crate::imports::*; -use emulated_integration_tests_common::accounts::{ALICE, BOB}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + USDT_ID, +}; use frame_support::{ dispatch::RawOrigin, sp_runtime::traits::Dispatchable, @@ -161,7 +164,6 @@ fn spend_roc_on_asset_hub() { #[test] fn create_and_claim_treasury_spend_in_usdt() { - const ASSET_ID: u32 = 1984; const SPEND_AMOUNT: u128 = 10_000_000; // treasury location from a sibling parachain. let treasury_location: Location = Location::new(1, PalletInstance(18)); @@ -175,7 +177,7 @@ fn create_and_claim_treasury_spend_in_usdt() { let asset_kind = VersionedLocatableAsset::V3 { location: asset_hub_location, asset_id: v3::AssetId::Concrete( - (v3::Junction::PalletInstance(50), v3::Junction::GeneralIndex(ASSET_ID.into())).into(), + (v3::Junction::PalletInstance(50), v3::Junction::GeneralIndex(USDT_ID.into())).into(), ), }; // treasury spend beneficiary. @@ -187,9 +189,9 @@ fn create_and_claim_treasury_spend_in_usdt() { type Assets = ::Assets; // USDT created at genesis, mint some assets to the treasury account. - assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4)); + assert_ok!(>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4)); // beneficiary has zero balance. - assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,); + assert_eq!(>::balance(USDT_ID, &alice,), 0u128,); }); Rococo::execute_with(|| { @@ -231,7 +233,7 @@ fn create_and_claim_treasury_spend_in_usdt() { AssetHubRococo, vec![ RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => { - id: id == &ASSET_ID, + id: id == &USDT_ID, from: from == &treasury_account, to: to == &alice, amount: amount == &SPEND_AMOUNT, @@ -241,7 +243,7 @@ fn create_and_claim_treasury_spend_in_usdt() { ] ); // beneficiary received the assets from the treasury. - assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,); + assert_eq!(>::balance(USDT_ID, &alice,), SPEND_AMOUNT,); }); Rococo::execute_with(|| { 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 060c3fb39254..437268a19e59 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 @@ -35,7 +35,8 @@ mod imports { // Cumulus pub use asset_test_utils::xcm_helpers; pub use emulated_integration_tests_common::{ - test_parachain_is_trusted_teleporter, + accounts::DUMMY_EMPTY, + get_account_id_from_seed, test_parachain_is_trusted_teleporter, 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/fellowship_treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs index 15f4fe33bddc..9520659712fc 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs @@ -14,15 +14,17 @@ // limitations under the License. use crate::imports::*; -use emulated_integration_tests_common::accounts::{ALICE, BOB}; -use frame_support::traits::fungibles::{Create, Inspect, Mutate}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + USDT_ID, +}; +use frame_support::traits::fungibles::{Inspect, Mutate}; use polkadot_runtime_common::impls::VersionedLocatableAsset; use xcm_executor::traits::ConvertLocation; #[test] fn create_and_claim_treasury_spend() { - const ASSET_ID: u32 = 1984; - const SPEND_AMOUNT: u128 = 1_000_000; + const SPEND_AMOUNT: u128 = 1_000_000_000; // treasury location from a sibling parachain. let treasury_location: Location = Location::new(1, [Parachain(CollectivesWestend::para_id().into()), PalletInstance(65)]); @@ -34,7 +36,7 @@ fn create_and_claim_treasury_spend() { // asset kind to be spent from the treasury. let asset_kind = VersionedLocatableAsset::V4 { location: asset_hub_location, - asset_id: AssetId((PalletInstance(50), GeneralIndex(ASSET_ID.into())).into()), + asset_id: AssetId((PalletInstance(50), GeneralIndex(USDT_ID.into())).into()), }; // treasury spend beneficiary. let alice: AccountId = Westend::account_id_of(ALICE); @@ -44,16 +46,10 @@ fn create_and_claim_treasury_spend() { AssetHubWestend::execute_with(|| { type Assets = ::Assets; - // create an asset class and mint some assets to the treasury account. - assert_ok!(>::create( - ASSET_ID, - treasury_account.clone(), - true, - SPEND_AMOUNT / 2 - )); - assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4)); + // USDT created at genesis, mint some assets to the fellowship treasury account. + assert_ok!(>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4)); // beneficiary has zero balance. - assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,); + assert_eq!(>::balance(USDT_ID, &alice,), 0u128,); }); CollectivesWestend::execute_with(|| { @@ -96,7 +92,7 @@ fn create_and_claim_treasury_spend() { AssetHubWestend, vec![ RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => { - id: id == &ASSET_ID, + id: id == &USDT_ID, from: from == &treasury_account, to: to == &alice, amount: amount == &SPEND_AMOUNT, @@ -106,7 +102,7 @@ fn create_and_claim_treasury_spend() { ] ); // beneficiary received the assets from the treasury. - assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,); + assert_eq!(>::balance(USDT_ID, &alice,), SPEND_AMOUNT,); }); CollectivesWestend::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index 65d013a0eec4..729de65382f8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -940,8 +940,11 @@ fn reserve_transfer_assets_from_system_para_to_para() { ); } -/// Reserve Transfers of a foreign asset and native asset from Parachain to System Para should +/// Reserve Transfers of a random asset and native asset from Parachain to System Para should /// work +/// Receiver is empty account to show deposit works as long as transfer includes enough DOT for ED. +/// Once we have https://github.com/paritytech/polkadot-sdk/issues/5298, +/// we should do equivalent test with USDT instead of DOT. #[test] fn reserve_transfer_assets_from_para_to_system_para() { // Init values for Parachain @@ -966,25 +969,24 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Fund Parachain's sender account with some foreign assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer.clone(), - asset_location_on_penpal, + asset_location_on_penpal.clone(), sender.clone(), asset_amount_to_send * 2, ); // Fund Parachain's sender account with some system assets PenpalA::mint_foreign_asset( penpal_asset_owner_signer, - system_asset_location_on_penpal, + system_asset_location_on_penpal.clone(), sender.clone(), fee_amount_to_send * 2, ); + // Beneficiary is a new (empty) account + let receiver = get_account_id_from_seed::(DUMMY_EMPTY); // Init values for System Parachain - let receiver = AssetHubWestendReceiver::get(); let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - let system_para_native_asset_location = RelayLocation::get(); - let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); let ah_asset_owner = AssetHubWestendAssetOwner::get(); let ah_asset_owner_signer = ::RuntimeOrigin::signed(ah_asset_owner); @@ -1019,11 +1021,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query initial balances let sender_system_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location.clone(), &sender) + >::balance(system_asset_location_on_penpal.clone(), &sender) }); let sender_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location.clone(), &sender) + >::balance(asset_location_on_penpal.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; let receiver_assets_before = AssetHubWestend::execute_with(|| { @@ -1040,11 +1042,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query final balances let sender_system_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_native_asset_location, &sender) + >::balance(system_asset_location_on_penpal, &sender) }); let sender_foreign_assets_after = PenpalA::execute_with(|| { type ForeignAssets = ::ForeignAssets; - >::balance(system_para_foreign_asset_location, &sender) + >::balance(asset_location_on_penpal, &sender) }); let receiver_balance_after = test.receiver.balance; let receiver_assets_after = AssetHubWestend::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs index 8cbce3e0d223..b70967184387 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/treasury.rs @@ -14,15 +14,17 @@ // limitations under the License. use crate::imports::*; -use emulated_integration_tests_common::accounts::{ALICE, BOB}; -use frame_support::traits::fungibles::{Create, Inspect, Mutate}; +use emulated_integration_tests_common::{ + accounts::{ALICE, BOB}, + USDT_ID, +}; +use frame_support::traits::fungibles::{Inspect, Mutate}; use polkadot_runtime_common::impls::VersionedLocatableAsset; use xcm_executor::traits::ConvertLocation; #[test] fn create_and_claim_treasury_spend() { - const ASSET_ID: u32 = 1984; - const SPEND_AMOUNT: u128 = 1_000_000; + const SPEND_AMOUNT: u128 = 1_000_000_000; // treasury location from a sibling parachain. let treasury_location: Location = Location::new(1, PalletInstance(37)); // treasury account on a sibling parachain. @@ -33,7 +35,7 @@ fn create_and_claim_treasury_spend() { // asset kind to be spend from the treasury. let asset_kind = VersionedLocatableAsset::V4 { location: asset_hub_location, - asset_id: AssetId([PalletInstance(50), GeneralIndex(ASSET_ID.into())].into()), + asset_id: AssetId([PalletInstance(50), GeneralIndex(USDT_ID.into())].into()), }; // treasury spend beneficiary. let alice: AccountId = Westend::account_id_of(ALICE); @@ -43,16 +45,10 @@ fn create_and_claim_treasury_spend() { AssetHubWestend::execute_with(|| { type Assets = ::Assets; - // create an asset class and mint some assets to the treasury account. - assert_ok!(>::create( - ASSET_ID, - treasury_account.clone(), - true, - SPEND_AMOUNT / 2 - )); - assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4)); + // USDT created at genesis, mint some assets to the treasury account. + assert_ok!(>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4)); // beneficiary has zero balance. - assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,); + assert_eq!(>::balance(USDT_ID, &alice,), 0u128,); }); Westend::execute_with(|| { @@ -94,7 +90,7 @@ fn create_and_claim_treasury_spend() { AssetHubWestend, vec![ RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => { - id: id == &ASSET_ID, + id: id == &USDT_ID, from: from == &treasury_account, to: to == &alice, amount: amount == &SPEND_AMOUNT, @@ -104,7 +100,7 @@ fn create_and_claim_treasury_spend() { ] ); // beneficiary received the assets from the treasury. - assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,); + assert_eq!(>::balance(USDT_ID, &alice,), SPEND_AMOUNT,); }); Westend::execute_with(|| { diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 40a1968ec557..3e8d3357caa8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -669,8 +669,8 @@ fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficie #[test] fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed( ) { - // On AH the xcm fee is 33_873_024 and the ED is 3_300_000 - send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 36_000_000); + // On AH the xcm fee is 26_789_690 and the ED is 3_300_000 + send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 30_000_000); AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs index 3c0533f775e2..b725c24fbea3 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/lib.rs @@ -20,7 +20,6 @@ mod imports { // Substrate pub use frame_support::{ assert_ok, - pallet_prelude::Weight, sp_runtime::{AccountId32, DispatchResult}, traits::fungibles::Inspect, }; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs index 4410d1bd40dc..c28b305b2c94 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-rococo/src/tests/teleport.rs @@ -17,7 +17,7 @@ use crate::imports::*; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - Rococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(627_959_000, 7_200))); + Rococo::assert_xcm_pallet_attempted_complete(None); assert_expected_events!( Rococo, @@ -39,11 +39,7 @@ fn relay_origin_assertions(t: RelayToSystemParaTest) { fn relay_dest_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - Rococo::assert_ump_queue_processed( - true, - Some(PeopleRococo::para_id()), - Some(Weight::from_parts(304_266_000, 7_186)), - ); + Rococo::assert_ump_queue_processed(true, Some(PeopleRococo::para_id()), None); assert_expected_events!( Rococo, @@ -62,20 +58,13 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) { } fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { - Rococo::assert_ump_queue_processed( - false, - Some(PeopleRococo::para_id()), - Some(Weight::from_parts(157_718_000, 3_593)), - ); + Rococo::assert_ump_queue_processed(false, Some(PeopleRococo::para_id()), None); } fn para_origin_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleRococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 600_000_000, - 7_000, - ))); + PeopleRococo::assert_xcm_pallet_attempted_complete(None); PeopleRococo::assert_parachain_system_ump_sent(); @@ -94,7 +83,7 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { fn para_dest_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleRococo::assert_dmp_queue_complete(Some(Weight::from_parts(162_456_000, 0))); + PeopleRococo::assert_dmp_queue_complete(None); assert_expected_events!( PeopleRococo, diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs index 689409fe5040..386a1b91c85b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/lib.rs @@ -19,7 +19,6 @@ mod imports { // Substrate pub use frame_support::{ assert_ok, - pallet_prelude::Weight, sp_runtime::{AccountId32, DispatchResult}, traits::fungibles::Inspect, }; diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs index 6fd3cdeb61fb..294583399011 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/teleport.rs @@ -17,7 +17,7 @@ use crate::imports::*; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(627_959_000, 7_200))); + Westend::assert_xcm_pallet_attempted_complete(None); assert_expected_events!( Westend, @@ -39,11 +39,7 @@ fn relay_origin_assertions(t: RelayToSystemParaTest) { fn relay_dest_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - Westend::assert_ump_queue_processed( - true, - Some(PeopleWestend::para_id()), - Some(Weight::from_parts(304_266_000, 7_186)), - ); + Westend::assert_ump_queue_processed(true, Some(PeopleWestend::para_id()), None); assert_expected_events!( Westend, @@ -62,20 +58,13 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) { } fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) { - Westend::assert_ump_queue_processed( - false, - Some(PeopleWestend::para_id()), - Some(Weight::from_parts(157_718_000, 3_593)), - ); + Westend::assert_ump_queue_processed(false, Some(PeopleWestend::para_id()), None); } fn para_origin_assertions(t: SystemParaToRelayTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 351_425_000, - 3_593, - ))); + PeopleWestend::assert_xcm_pallet_attempted_complete(None); PeopleWestend::assert_parachain_system_ump_sent(); @@ -94,7 +83,7 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { fn para_dest_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; - PeopleWestend::assert_dmp_queue_complete(Some(Weight::from_parts(162_456_000, 0))); + PeopleWestend::assert_dmp_queue_complete(None); assert_expected_events!( PeopleWestend, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 03d3785dccbd..3d6ae6ddd1d2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, 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-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 21_643_000 picoseconds. - Weight::from_parts(22_410_000, 3593) + // Minimum execution time: 34_180_000 picoseconds. + Weight::from_parts(34_745_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 43_758_000 picoseconds. - Weight::from_parts(44_654_000, 6196) + // Minimum execution time: 42_638_000 picoseconds. + Weight::from_parts(43_454_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,20 +90,17 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 87_978_000 picoseconds. - Weight::from_parts(88_517_000, 8799) + // Minimum execution time: 102_916_000 picoseconds. + Weight::from_parts(105_699_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } - // Storage: `ParachainInfo::ParachainId` (r:1 w:0) - // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) pub fn reserve_asset_deposited() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `1489` - // Minimum execution time: 6_883_000 picoseconds. - Weight::from_parts(6_979_000, 1489) - .saturating_add(T::DbWeight::get().reads(1)) + // Estimated: `0` + // Minimum execution time: 1_805_000 picoseconds. + Weight::from_parts(1_901_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -125,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 198_882_000 picoseconds. - Weight::from_parts(199_930_000, 6196) + // Minimum execution time: 108_018_000 picoseconds. + Weight::from_parts(110_310_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -134,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_343_000 picoseconds. - Weight::from_parts(3_487_000, 0) + // Minimum execution time: 3_507_000 picoseconds. + Weight::from_parts(3_724_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -143,13 +140,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 19_399_000 picoseconds. - Weight::from_parts(19_659_000, 3593) + // Minimum execution time: 26_269_000 picoseconds. + Weight::from_parts(26_706_000, 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`) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -160,6 +155,8 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) @@ -168,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 59_017_000 picoseconds. - Weight::from_parts(60_543_000, 6196) + // Minimum execution time: 84_759_000 picoseconds. + Weight::from_parts(86_157_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -193,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 45_409_000 picoseconds. - Weight::from_parts(47_041_000, 3610) + // Minimum execution time: 50_876_000 picoseconds. + Weight::from_parts(51_512_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index fe8d18613925..f7891aedc496 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -1,24 +1,25 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// 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. +// 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_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-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-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-vmdtonbz-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -53,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 20_295_000 picoseconds. - Weight::from_parts(21_142_000, 3593) + // Minimum execution time: 32_612_000 picoseconds. + Weight::from_parts(33_359_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -64,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 42_356_000 picoseconds. - Weight::from_parts(43_552_000, 6196) + // Minimum execution time: 41_144_000 picoseconds. + Weight::from_parts(41_788_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -89,20 +90,17 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 85_553_000 picoseconds. - Weight::from_parts(87_177_000, 8799) + // Minimum execution time: 101_340_000 picoseconds. + Weight::from_parts(103_686_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } - // Storage: `ParachainInfo::ParachainId` (r:1 w:0) - // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) pub fn reserve_asset_deposited() -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `1489` - // Minimum execution time: 6_166_000 picoseconds. - Weight::from_parts(6_352_000, 1489) - .saturating_add(T::DbWeight::get().reads(1)) + // Estimated: `0` + // Minimum execution time: 1_682_000 picoseconds. + Weight::from_parts(1_734_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -124,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 184_462_000 picoseconds. - Weight::from_parts(189_593_000, 6196) + // Minimum execution time: 107_335_000 picoseconds. + Weight::from_parts(109_665_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -133,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_018_000 picoseconds. - Weight::from_parts(3_098_000, 0) + // Minimum execution time: 3_345_000 picoseconds. + Weight::from_parts(3_548_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,13 +140,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 18_583_000 picoseconds. - Weight::from_parts(19_057_000, 3593) + // Minimum execution time: 25_560_000 picoseconds. + Weight::from_parts(26_779_000, 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`) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -159,6 +155,8 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) @@ -167,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 56_666_000 picoseconds. - Weight::from_parts(58_152_000, 6196) + // Minimum execution time: 84_453_000 picoseconds. + Weight::from_parts(86_755_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -192,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 44_197_000 picoseconds. - Weight::from_parts(45_573_000, 3610) + // Minimum execution time: 50_463_000 picoseconds. + Weight::from_parts(51_587_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } 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 c8face15627f..c65880771e08 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 @@ -219,7 +219,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-rococo"), impl_name: create_runtime_str!("bridge-hub-rococo"), authoring_version: 1, - spec_version: 1_015_000, + spec_version: 1_016_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, @@ -1597,42 +1597,42 @@ mod tests { use bp_polkadot_core::SuffixedCommonSignedExtensionExt; sp_io::TestExternalities::default().execute_with(|| { - frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( - frame_system::CheckNonZeroSender::new(), - frame_system::CheckSpecVersion::new(), - frame_system::CheckTxVersion::new(), - frame_system::CheckGenesis::new(), - frame_system::CheckEra::from(Era::Immortal), - frame_system::CheckNonce::from(10), - frame_system::CheckWeight::new(), - pallet_transaction_payment::ChargeTransactionPayment::from(10), - BridgeRejectObsoleteHeadersAndMessages, - ( - bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), - bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), - ), - cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), - frame_metadata_hash_extension::CheckMetadataHash::new(false), - ); - - // for BridgeHubRococo - { - let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::from_params( - VERSION.spec_version, - VERSION.transaction_version, - bp_runtime::TransactionEra::Immortal, - System::block_hash(BlockNumber::zero()), - 10, - 10, - (((), ()), ((), ())), - ); - 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() - ) - } - }); + frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); + let payload: SignedExtra = ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(10), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(10), + BridgeRejectObsoleteHeadersAndMessages, + ( + bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), + bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), + ), + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), + frame_metadata_hash_extension::CheckMetadataHash::new(false), + ); + + // for BridgeHubRococo + { + let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::from_params( + VERSION.spec_version, + VERSION.transaction_version, + bp_runtime::TransactionEra::Immortal, + System::block_hash(BlockNumber::zero()), + 10, + 10, + (((), ()), ((), ())), + ); + 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() + ) + } + }); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 057dc4313510..f2cee0e3e807 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, 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-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 19_610_000 picoseconds. - Weight::from_parts(19_980_000, 3593) + // Minimum execution time: 30_988_000 picoseconds. + Weight::from_parts(31_496_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 44_411_000 picoseconds. - Weight::from_parts(45_110_000, 6196) + // Minimum execution time: 42_805_000 picoseconds. + Weight::from_parts(44_207_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `223` // Estimated: `8799` - // Minimum execution time: 89_739_000 picoseconds. - Weight::from_parts(91_256_000, 8799) + // Minimum execution time: 103_376_000 picoseconds. + Weight::from_parts(104_770_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -124,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 60_045_000 picoseconds. - Weight::from_parts(60_710_000, 6196) + // Minimum execution time: 71_234_000 picoseconds. + Weight::from_parts(72_990_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -133,8 +133,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_257_000 picoseconds. - Weight::from_parts(3_392_000, 0) + // Minimum execution time: 2_636_000 picoseconds. + Weight::from_parts(2_777_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,13 +142,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 19_423_000 picoseconds. - Weight::from_parts(19_823_000, 3593) + // Minimum execution time: 23_839_000 picoseconds. + Weight::from_parts(24_568_000, 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`) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -159,6 +157,8 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) @@ -167,8 +167,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `122` // Estimated: `6196` - // Minimum execution time: 60_484_000 picoseconds. - Weight::from_parts(61_634_000, 6196) + // Minimum execution time: 78_345_000 picoseconds. + Weight::from_parts(80_558_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -192,8 +192,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3593` - // Minimum execution time: 44_863_000 picoseconds. - Weight::from_parts(45_549_000, 3593) + // Minimum execution time: 46_614_000 picoseconds. + Weight::from_parts(47_354_000, 3593) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } 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 7384bf2850fe..1c26742fd67f 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 @@ -198,7 +198,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-westend"), impl_name: create_runtime_str!("bridge-hub-westend"), authoring_version: 1, - spec_version: 1_015_000, + spec_version: 1_016_000, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5, @@ -1281,40 +1281,40 @@ mod tests { use bp_polkadot_core::SuffixedCommonSignedExtensionExt; sp_io::TestExternalities::default().execute_with(|| { - frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); - let payload: SignedExtra = ( - frame_system::CheckNonZeroSender::new(), - frame_system::CheckSpecVersion::new(), - frame_system::CheckTxVersion::new(), - frame_system::CheckGenesis::new(), - frame_system::CheckEra::from(Era::Immortal), - frame_system::CheckNonce::from(10), - frame_system::CheckWeight::new(), - pallet_transaction_payment::ChargeTransactionPayment::from(10), - BridgeRejectObsoleteHeadersAndMessages, - ( - bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(), - ), - 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( - VERSION.spec_version, - VERSION.transaction_version, - bp_runtime::TransactionEra::Immortal, - System::block_hash(BlockNumber::zero()), - 10, - 10, - (((), ()), ((), ())), - ); - 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() - ) - } - }); + frame_system::BlockHash::::insert(BlockNumber::zero(), Hash::default()); + let payload: SignedExtra = ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(10), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(10), + BridgeRejectObsoleteHeadersAndMessages, + ( + bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(), + ), + 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( + VERSION.spec_version, + VERSION.transaction_version, + bp_runtime::TransactionEra::Immortal, + System::block_hash(BlockNumber::zero()), + 10, + 10, + (((), ()), ((), ())), + ); + 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() + ) + } + }); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 4310b2456475..5bd1d1680aa1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,11 +16,11 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-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-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-vmdtonbz-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-westend-dev"), DB CACHE: 1024 // Executed Command: // target/production/polkadot-parachain @@ -33,10 +33,10 @@ // --heap-pages=4096 // --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::fungible -// --chain=bridge-hub-rococo-dev +// --chain=bridge-hub-westend-dev // --header=./cumulus/file_header.txt // --template=./cumulus/templates/xcm-bench-template.hbs -// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/ +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 19_037_000 picoseconds. - Weight::from_parts(19_602_000, 3593) + // Minimum execution time: 30_218_000 picoseconds. + Weight::from_parts(30_783_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,15 +65,13 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 43_115_000 picoseconds. - Weight::from_parts(43_897_000, 6196) + // Minimum execution time: 42_631_000 picoseconds. + Weight::from_parts(43_127_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: `System::Account` (r:3 w:3) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -90,11 +88,11 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `294` + // Measured: `260` // Estimated: `8799` - // Minimum execution time: 90_267_000 picoseconds. - Weight::from_parts(91_460_000, 8799) - .saturating_add(T::DbWeight::get().reads(11)) + // Minimum execution time: 100_978_000 picoseconds. + Weight::from_parts(102_819_000, 8799) + .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } // Storage: `Benchmark::Override` (r:0 w:0) @@ -106,8 +104,6 @@ impl WeightInfo { // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. Weight::from_parts(18_446_744_073_709_551_000, 0) } - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -126,19 +122,19 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `242` + // Measured: `208` // Estimated: `6196` - // Minimum execution time: 60_477_000 picoseconds. - Weight::from_parts(61_314_000, 6196) - .saturating_add(T::DbWeight::get().reads(10)) + // Minimum execution time: 71_533_000 picoseconds. + Weight::from_parts(72_922_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } pub fn receive_teleported_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_996_000 picoseconds. - Weight::from_parts(3_107_000, 0) + // Minimum execution time: 2_863_000 picoseconds. + Weight::from_parts(2_997_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -146,15 +142,11 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 18_907_000 picoseconds. - Weight::from_parts(19_475_000, 3593) + // Minimum execution time: 23_763_000 picoseconds. + Weight::from_parts(24_438_000, 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`) - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -165,21 +157,21 @@ impl WeightInfo { // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `193` + // Measured: `159` // Estimated: `6196` - // Minimum execution time: 59_143_000 picoseconds. - Weight::from_parts(60_316_000, 6196) - .saturating_add(T::DbWeight::get().reads(10)) + // Minimum execution time: 78_182_000 picoseconds. + Weight::from_parts(79_575_000, 6196) + .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } - // Storage: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) - // Proof: UNKNOWN KEY `0x48297505634037ef48c848c99c0b1f1b` (r:1 w:0) // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `ParachainSystem::UpwardDeliveryFeeFactor` (r:1 w:0) @@ -198,11 +190,11 @@ impl WeightInfo { // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `141` - // Estimated: `3606` - // Minimum execution time: 44_459_000 picoseconds. - Weight::from_parts(45_365_000, 3606) - .saturating_add(T::DbWeight::get().reads(9)) + // Measured: `107` + // Estimated: `3593` + // Minimum execution time: 46_767_000 picoseconds. + Weight::from_parts(47_823_000, 3593) + .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 73a719805307..c8dbdadf7b15 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 26_642_000 picoseconds. - Weight::from_parts(27_583_000, 3593) + // Minimum execution time: 29_812_000 picoseconds. + Weight::from_parts(30_526_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 35_124_000 picoseconds. - Weight::from_parts(36_510_000, 6196) + // Minimum execution time: 39_430_000 picoseconds. + Weight::from_parts(39_968_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `207` // Estimated: `6196` - // Minimum execution time: 55_950_000 picoseconds. - Weight::from_parts(57_207_000, 6196) + // Minimum execution time: 65_555_000 picoseconds. + Weight::from_parts(67_161_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 23_747_000 picoseconds. - Weight::from_parts(24_424_000, 3571) + // Minimum execution time: 30_491_000 picoseconds. + Weight::from_parts(31_991_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_853_000 picoseconds. - Weight::from_parts(1_998_000, 0) + // Minimum execution time: 2_568_000 picoseconds. + Weight::from_parts(2_703_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 19_164_000 picoseconds. - Weight::from_parts(19_643_000, 3593) + // Minimum execution time: 22_159_000 picoseconds. + Weight::from_parts(22_517_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3593` - // Minimum execution time: 48_708_000 picoseconds. - Weight::from_parts(49_610_000, 3593) + // Minimum execution time: 57_126_000 picoseconds. + Weight::from_parts(58_830_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,8 +180,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 20_586_000 picoseconds. - Weight::from_parts(21_147_000, 3571) + // Minimum execution time: 26_589_000 picoseconds. + Weight::from_parts(27_285_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index ddfc599fa579..935636651eb9 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("coretime-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 26_842_000 picoseconds. - Weight::from_parts(27_606_000, 3593) + // Minimum execution time: 29_866_000 picoseconds. + Weight::from_parts(30_363_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 35_076_000 picoseconds. - Weight::from_parts(36_109_000, 6196) + // Minimum execution time: 39_434_000 picoseconds. + Weight::from_parts(40_274_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -88,8 +88,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `207` // Estimated: `6196` - // Minimum execution time: 56_951_000 picoseconds. - Weight::from_parts(58_286_000, 6196) + // Minimum execution time: 66_303_000 picoseconds. + Weight::from_parts(68_294_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -118,8 +118,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 23_796_000 picoseconds. - Weight::from_parts(24_692_000, 3571) + // Minimum execution time: 30_523_000 picoseconds. + Weight::from_parts(31_289_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -127,8 +127,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_990_000 picoseconds. - Weight::from_parts(2_142_000, 0) + // Minimum execution time: 2_517_000 picoseconds. + Weight::from_parts(2_634_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -136,8 +136,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 19_572_000 picoseconds. - Weight::from_parts(20_017_000, 3593) + // Minimum execution time: 22_151_000 picoseconds. + Weight::from_parts(22_907_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3593` - // Minimum execution time: 49_336_000 picoseconds. - Weight::from_parts(50_507_000, 3593) + // Minimum execution time: 57_763_000 picoseconds. + Weight::from_parts(58_941_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -180,8 +180,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `106` // Estimated: `3571` - // Minimum execution time: 21_230_000 picoseconds. - Weight::from_parts(21_870_000, 3571) + // Minimum execution time: 26_322_000 picoseconds. + Weight::from_parts(27_197_000, 3571) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs index 11c1bad9aa17..58007173ae1d 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/mod.rs @@ -5,7 +5,7 @@ // 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 +// 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, @@ -60,10 +60,8 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { fn withdraw_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::withdraw_asset()) } - // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &Assets) -> Weight { - // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 - Weight::from_parts(1_000_000_000_u64, 0) + fn reserve_asset_deposited(assets: &Assets) -> Weight { + assets.weigh_assets(XcmFungibleWeight::::reserve_asset_deposited()) } fn receive_teleported_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::receive_teleported_asset()) @@ -114,12 +112,8 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { XcmGeneric::::report_error() } - fn deposit_asset(assets: &AssetFilter, _dest: &Location) -> Weight { - // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_parts(1_000_000_000_u64, 0); - let weight = assets.weigh_assets(XcmFungibleWeight::::deposit_asset()); - hardcoded_weight.min(weight) + assets.weigh_assets(XcmFungibleWeight::::deposit_asset()) } fn deposit_reserve_asset(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::deposit_reserve_asset()) @@ -132,7 +126,7 @@ impl XcmWeightInfo for PeopleRococoXcmWeight { _reserve: &Location, _xcm: &Xcm<()>, ) -> Weight { - assets.weigh_assets(XcmGeneric::::initiate_reserve_withdraw()) + assets.weigh_assets(XcmFungibleWeight::::initiate_reserve_withdraw()) } fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 2364798596d5..4dd44e66dd5e 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// 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. +// 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_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-kusama-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::fungible -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-kusama/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::fungible +// --chain=people-rococo-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,110 +48,140 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::fungible`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn withdraw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 23_309_000 picoseconds. - Weight::from_parts(23_777_000, 3593) + // Minimum execution time: 30_428_000 picoseconds. + Weight::from_parts(31_184_000, 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) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn transfer_asset() -> Weight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 48_808_000 picoseconds. - Weight::from_parts(49_427_000, 6196) + // Minimum execution time: 41_912_000 picoseconds. + Weight::from_parts(43_346_000, 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: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), 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: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `223` // Estimated: `6196` - // Minimum execution time: 71_204_000 picoseconds. - Weight::from_parts(72_121_000, 6196) + // Minimum execution time: 67_706_000 picoseconds. + Weight::from_parts(69_671_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .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`) + pub 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) + } + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_reserve_withdraw() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 29_790_000 picoseconds. + Weight::from_parts(30_655_000, 3535) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } pub fn receive_teleported_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_559_000 picoseconds. - Weight::from_parts(3_616_000, 0) + // Minimum execution time: 2_438_000 picoseconds. + Weight::from_parts(2_597_000, 0) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn deposit_asset() -> Weight { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 25_042_000 picoseconds. - Weight::from_parts(25_630_000, 3593) + // Minimum execution time: 24_040_000 picoseconds. + Weight::from_parts(24_538_000, 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) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `122` // Estimated: `3593` - // Minimum execution time: 49_030_000 picoseconds. - Weight::from_parts(49_828_000, 3593) + // Minimum execution time: 58_275_000 picoseconds. + Weight::from_parts(59_899_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_teleport() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 27_142_000 picoseconds. - Weight::from_parts(27_416_000, 3535) + // Minimum execution time: 25_638_000 picoseconds. + Weight::from_parts(26_514_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index a50c8860c48f..729a32117041 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// 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. +// 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_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-kusama-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-kusama-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::generic +// --chain=people-rococo-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-rococo/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,24 +48,24 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::generic`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_holding() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 30_210_000 picoseconds. - Weight::from_parts(30_864_000, 3535) + // Minimum execution time: 29_430_000 picoseconds. + Weight::from_parts(30_111_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -72,97 +73,97 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_808_000 picoseconds. - Weight::from_parts(2_848_000, 0) + // Minimum execution time: 607_000 picoseconds. + Weight::from_parts(672_000, 0) } - // Storage: PolkadotXcm Queries (r:1 w:0) - // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::Queries` (r:1 w:0) + // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn query_response() -> Weight { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 10_353_000 picoseconds. - Weight::from_parts(10_569_000, 3497) + // Minimum execution time: 7_445_000 picoseconds. + Weight::from_parts(7_623_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_074_000 picoseconds. - Weight::from_parts(12_280_000, 0) + // Minimum execution time: 6_749_000 picoseconds. + Weight::from_parts(7_073_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_080_000 picoseconds. - Weight::from_parts(3_161_000, 0) + // Minimum execution time: 1_275_000 picoseconds. + Weight::from_parts(1_409_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_649_000 picoseconds. - Weight::from_parts(2_732_000, 0) + // Minimum execution time: 670_000 picoseconds. + Weight::from_parts(709_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_652_000 picoseconds. - Weight::from_parts(2_749_000, 0) + // Minimum execution time: 635_000 picoseconds. + Weight::from_parts(723_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_642_000 picoseconds. - Weight::from_parts(2_704_000, 0) + // Minimum execution time: 650_000 picoseconds. + Weight::from_parts(699_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_438_000 picoseconds. - Weight::from_parts(3_508_000, 0) + // Minimum execution time: 678_000 picoseconds. + Weight::from_parts(728_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_626_000 picoseconds. - Weight::from_parts(2_701_000, 0) + // Minimum execution time: 657_000 picoseconds. + Weight::from_parts(703_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_error() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 24_737_000 picoseconds. - Weight::from_parts(25_106_000, 3535) + // Minimum execution time: 25_795_000 picoseconds. + Weight::from_parts(26_415_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: PolkadotXcm AssetTraps (r:1 w:1) - // Proof Skipped: PolkadotXcm AssetTraps (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + // Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn claim_asset() -> Weight { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 14_712_000 picoseconds. - Weight::from_parts(14_976_000, 3555) + // Minimum execution time: 10_792_000 picoseconds. + Weight::from_parts(11_061_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -170,114 +171,93 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_689_000 picoseconds. - Weight::from_parts(2_739_000, 0) + // Minimum execution time: 624_000 picoseconds. + Weight::from_parts(682_000, 0) } - // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn subscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 26_478_000 picoseconds. - Weight::from_parts(26_695_000, 3503) + // Minimum execution time: 23_906_000 picoseconds. + Weight::from_parts(24_740_000, 3503) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn unsubscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_811_000 picoseconds. - Weight::from_parts(5_062_000, 0) + // Minimum execution time: 2_621_000 picoseconds. + Weight::from_parts(2_788_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - pub fn initiate_reserve_withdraw() -> Weight { - // Proof Size summary in bytes: - // Measured: `70` - // Estimated: `3535` - // Minimum execution time: 26_945_000 picoseconds. - Weight::from_parts(28_093_000, 3535) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) - } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_144_000 picoseconds. - Weight::from_parts(4_217_000, 0) + // Minimum execution time: 954_000 picoseconds. + Weight::from_parts(1_046_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_726_000 picoseconds. - Weight::from_parts(2_802_000, 0) + // Minimum execution time: 742_000 picoseconds. + Weight::from_parts(790_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_719_000 picoseconds. - Weight::from_parts(2_790_000, 0) + // Minimum execution time: 664_000 picoseconds. + Weight::from_parts(722_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_660_000 picoseconds. - Weight::from_parts(2_742_000, 0) + // Minimum execution time: 619_000 picoseconds. + Weight::from_parts(672_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_874_000 picoseconds. - Weight::from_parts(2_940_000, 0) + // Minimum execution time: 798_000 picoseconds. + Weight::from_parts(851_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn query_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 27_235_000 picoseconds. - Weight::from_parts(27_811_000, 3535) + // Minimum execution time: 29_580_000 picoseconds. + Weight::from_parts(31_100_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -285,27 +265,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_807_000 picoseconds. - Weight::from_parts(4_918_000, 0) + // Minimum execution time: 3_150_000 picoseconds. + Weight::from_parts(3_326_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 24_698_000 picoseconds. - Weight::from_parts(25_077_000, 3535) + // Minimum execution time: 26_152_000 picoseconds. + Weight::from_parts(26_635_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -313,35 +293,35 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_613_000 picoseconds. - Weight::from_parts(2_703_000, 0) + // Minimum execution time: 693_000 picoseconds. + Weight::from_parts(724_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_602_000 picoseconds. - Weight::from_parts(2_661_000, 0) + // Minimum execution time: 632_000 picoseconds. + Weight::from_parts(678_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_557_000 picoseconds. - Weight::from_parts(2_655_000, 0) + // Minimum execution time: 646_000 picoseconds. + Weight::from_parts(694_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_724_000 picoseconds. - Weight::from_parts(2_760_000, 0) + // Minimum execution time: 622_000 picoseconds. + Weight::from_parts(656_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_764_000 picoseconds. - Weight::from_parts(2_872_000, 0) + // Minimum execution time: 639_000 picoseconds. + Weight::from_parts(679_000, 0) } } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs index b1fc7ad8ed83..b44e8d4b61b8 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/mod.rs @@ -5,7 +5,7 @@ // 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 +// 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, @@ -60,10 +60,8 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { fn withdraw_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::withdraw_asset()) } - // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &Assets) -> Weight { - // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 - Weight::from_parts(1_000_000_000_u64, 0) + fn reserve_asset_deposited(assets: &Assets) -> Weight { + assets.weigh_assets(XcmFungibleWeight::::reserve_asset_deposited()) } fn receive_teleported_asset(assets: &Assets) -> Weight { assets.weigh_assets(XcmFungibleWeight::::receive_teleported_asset()) @@ -114,12 +112,8 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { XcmGeneric::::report_error() } - fn deposit_asset(assets: &AssetFilter, _dest: &Location) -> Weight { - // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_parts(1_000_000_000_u64, 0); - let weight = assets.weigh_assets(XcmFungibleWeight::::deposit_asset()); - hardcoded_weight.min(weight) + assets.weigh_assets(XcmFungibleWeight::::deposit_asset()) } fn deposit_reserve_asset(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { assets.weigh_assets(XcmFungibleWeight::::deposit_reserve_asset()) @@ -132,13 +126,10 @@ impl XcmWeightInfo for PeopleWestendXcmWeight { _reserve: &Location, _xcm: &Xcm<()>, ) -> Weight { - assets.weigh_assets(XcmGeneric::::initiate_reserve_withdraw()) + assets.weigh_assets(XcmFungibleWeight::::initiate_reserve_withdraw()) } fn initiate_teleport(assets: &AssetFilter, _dest: &Location, _xcm: &Xcm<()>) -> Weight { - // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_parts(200_000_000_u64, 0); - let weight = assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()); - hardcoded_weight.min(weight) + assets.weigh_assets(XcmFungibleWeight::::initiate_teleport()) } fn report_holding(_response_info: &QueryResponseInfo, _assets: &AssetFilter) -> Weight { XcmGeneric::::report_holding() diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 92d08a246180..8f6bfde986bb 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// 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. +// 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_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-polkadot-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-westend-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-polkadot-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::fungible -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-polkadot/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::fungible +// --chain=people-westend-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,110 +48,140 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::fungible`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn withdraw_asset() -> Weight { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 23_363_000 picoseconds. - Weight::from_parts(23_663_000, 3593) + // Minimum execution time: 30_040_000 picoseconds. + Weight::from_parts(30_758_000, 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) + // Storage: `System::Account` (r:2 w:2) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn transfer_asset() -> Weight { // Proof Size summary in bytes: // Measured: `153` // Estimated: `6196` - // Minimum execution time: 49_093_000 picoseconds. - Weight::from_parts(49_719_000, 6196) + // Minimum execution time: 42_135_000 picoseconds. + Weight::from_parts(42_970_000, 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: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), 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: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `223` // Estimated: `6196` - // Minimum execution time: 74_134_000 picoseconds. - Weight::from_parts(74_719_000, 6196) + // Minimum execution time: 67_385_000 picoseconds. + Weight::from_parts(69_776_000, 6196) .saturating_add(T::DbWeight::get().reads(8)) .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`) + pub 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) + } + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + pub fn initiate_reserve_withdraw() -> Weight { + // Proof Size summary in bytes: + // Measured: `70` + // Estimated: `3535` + // Minimum execution time: 29_804_000 picoseconds. + Weight::from_parts(30_662_000, 3535) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } pub fn receive_teleported_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_726_000 picoseconds. - Weight::from_parts(3_881_000, 0) + // Minimum execution time: 2_358_000 picoseconds. + Weight::from_parts(2_497_000, 0) } - // Storage: System Account (r:1 w:1) - // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn deposit_asset() -> Weight { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3593` - // Minimum execution time: 25_903_000 picoseconds. - Weight::from_parts(26_150_000, 3593) + // Minimum execution time: 23_732_000 picoseconds. + Weight::from_parts(24_098_000, 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) - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: // Measured: `122` // Estimated: `3593` - // Minimum execution time: 51_084_000 picoseconds. - Weight::from_parts(51_859_000, 3593) + // Minimum execution time: 58_449_000 picoseconds. + Weight::from_parts(60_235_000, 3593) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn initiate_teleport() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 28_038_000 picoseconds. - Weight::from_parts(28_438_000, 3535) + // Minimum execution time: 25_708_000 picoseconds. + Weight::from_parts(26_495_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 861f03819959..1377d31f2db7 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -1,41 +1,42 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// 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. +// 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_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("people-polkadot-dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: Compiled, CHAIN: Some("people-westend-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=people-polkadot-dev -// --execution=wasm -// --wasm-execution=compiled -// --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./cumulus/parachains/runtimes/people/people-polkadot/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_benchmarks::generic +// --chain=people-westend-dev +// --header=./cumulus/file_header.txt +// --template=./cumulus/templates/xcm-bench-template.hbs +// --output=./cumulus/parachains/runtimes/people/people-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,24 +48,24 @@ use core::marker::PhantomData; /// Weights for `pallet_xcm_benchmarks::generic`. pub struct WeightInfo(PhantomData); impl WeightInfo { - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_holding() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 30_819_000 picoseconds. - Weight::from_parts(31_157_000, 3535) + // Minimum execution time: 29_537_000 picoseconds. + Weight::from_parts(30_513_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -72,97 +73,97 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_869_000 picoseconds. - Weight::from_parts(2_920_000, 0) + // Minimum execution time: 683_000 picoseconds. + Weight::from_parts(738_000, 0) } - // Storage: PolkadotXcm Queries (r:1 w:0) - // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::Queries` (r:1 w:0) + // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn query_response() -> Weight { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 10_268_000 picoseconds. - Weight::from_parts(10_496_000, 3497) + // Minimum execution time: 7_498_000 picoseconds. + Weight::from_parts(7_904_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_990_000 picoseconds. - Weight::from_parts(12_206_000, 0) + // Minimum execution time: 7_029_000 picoseconds. + Weight::from_parts(7_325_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_170_000 picoseconds. - Weight::from_parts(3_308_000, 0) + // Minimum execution time: 1_343_000 picoseconds. + Weight::from_parts(1_410_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_650_000 picoseconds. - Weight::from_parts(2_783_000, 0) + // Minimum execution time: 696_000 picoseconds. + Weight::from_parts(734_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_681_000 picoseconds. - Weight::from_parts(2_829_000, 0) + // Minimum execution time: 690_000 picoseconds. + Weight::from_parts(740_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_622_000 picoseconds. - Weight::from_parts(2_688_000, 0) + // Minimum execution time: 667_000 picoseconds. + Weight::from_parts(697_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_385_000 picoseconds. - Weight::from_parts(3_538_000, 0) + // Minimum execution time: 692_000 picoseconds. + Weight::from_parts(743_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_630_000 picoseconds. - Weight::from_parts(2_720_000, 0) + // Minimum execution time: 670_000 picoseconds. + Weight::from_parts(712_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_error() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 24_446_000 picoseconds. - Weight::from_parts(24_854_000, 3535) + // Minimum execution time: 26_405_000 picoseconds. + Weight::from_parts(26_877_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: PolkadotXcm AssetTraps (r:1 w:1) - // Proof Skipped: PolkadotXcm AssetTraps (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::AssetTraps` (r:1 w:1) + // Proof: `PolkadotXcm::AssetTraps` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn claim_asset() -> Weight { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 14_713_000 picoseconds. - Weight::from_parts(15_010_000, 3555) + // Minimum execution time: 10_953_000 picoseconds. + Weight::from_parts(11_345_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -170,114 +171,93 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_702_000 picoseconds. - Weight::from_parts(2_744_000, 0) + // Minimum execution time: 644_000 picoseconds. + Weight::from_parts(693_000, 0) } - // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn subscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_955_000 picoseconds. - Weight::from_parts(26_632_000, 3503) + // Minimum execution time: 24_157_000 picoseconds. + Weight::from_parts(24_980_000, 3503) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) - // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + // Storage: `PolkadotXcm::VersionNotifyTargets` (r:0 w:1) + // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) pub fn unsubscribe_version() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_965_000 picoseconds. - Weight::from_parts(5_168_000, 0) + // Minimum execution time: 2_767_000 picoseconds. + Weight::from_parts(2_844_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) - pub fn initiate_reserve_withdraw() -> Weight { - // Proof Size summary in bytes: - // Measured: `70` - // Estimated: `3535` - // Minimum execution time: 27_707_000 picoseconds. - Weight::from_parts(28_081_000, 3535) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) - } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_215_000 picoseconds. - Weight::from_parts(4_362_000, 0) + // Minimum execution time: 1_079_000 picoseconds. + Weight::from_parts(1_141_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_843_000 picoseconds. - Weight::from_parts(2_957_000, 0) + // Minimum execution time: 776_000 picoseconds. + Weight::from_parts(829_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_751_000 picoseconds. - Weight::from_parts(2_809_000, 0) + // Minimum execution time: 696_000 picoseconds. + Weight::from_parts(740_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_674_000 picoseconds. - Weight::from_parts(2_737_000, 0) + // Minimum execution time: 655_000 picoseconds. + Weight::from_parts(684_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_891_000 picoseconds. - Weight::from_parts(2_952_000, 0) + // Minimum execution time: 825_000 picoseconds. + Weight::from_parts(853_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn query_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 28_600_000 picoseconds. - Weight::from_parts(29_001_000, 3535) + // Minimum execution time: 30_222_000 picoseconds. + Weight::from_parts(31_110_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -285,27 +265,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_748_000 picoseconds. - Weight::from_parts(4_813_000, 0) + // Minimum execution time: 3_108_000 picoseconds. + Weight::from_parts(3_325_000, 0) } - // Storage: ParachainInfo ParachainId (r:1 w:0) - // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - // Storage: PolkadotXcm SupportedVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) - // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) - // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) - // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) - // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem HostConfiguration (r:1 w:0) - // Proof Skipped: ParachainSystem HostConfiguration (max_values: Some(1), max_size: None, mode: Measured) - // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - // Proof Skipped: ParachainSystem PendingUpwardMessages (max_values: Some(1), max_size: None, mode: Measured) + // Storage: `ParachainInfo::ParachainId` (r:1 w:0) + // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + // Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + // Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::HostConfiguration` (r:1 w:0) + // Proof: `ParachainSystem::HostConfiguration` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + // Storage: `ParachainSystem::PendingUpwardMessages` (r:1 w:1) + // Proof: `ParachainSystem::PendingUpwardMessages` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) pub fn report_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `70` // Estimated: `3535` - // Minimum execution time: 25_483_000 picoseconds. - Weight::from_parts(25_737_000, 3535) + // Minimum execution time: 26_548_000 picoseconds. + Weight::from_parts(26_911_000, 3535) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -313,35 +293,35 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_755_000 picoseconds. - Weight::from_parts(2_817_000, 0) + // Minimum execution time: 684_000 picoseconds. + Weight::from_parts(726_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_700_000 picoseconds. - Weight::from_parts(2_773_000, 0) + // Minimum execution time: 649_000 picoseconds. + Weight::from_parts(700_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_670_000 picoseconds. - Weight::from_parts(2_711_000, 0) + // Minimum execution time: 650_000 picoseconds. + Weight::from_parts(686_000, 0) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_710_000 picoseconds. - Weight::from_parts(2_762_000, 0) + // Minimum execution time: 652_000 picoseconds. + Weight::from_parts(703_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_839_000 picoseconds. - Weight::from_parts(2_931_000, 0) + // Minimum execution time: 673_000 picoseconds. + Weight::from_parts(742_000, 0) } } diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index eca7c7bbc3c2..d0c421bccaf8 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -282,6 +282,10 @@ parameter_types! { 1, [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())] ); + pub UsdtFromAssetHub: Location = Location::new( + 1, + [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1984)] + ); /// The Penpal runtime is utilized for testing with various environment setups. /// This storage item provides the opportunity to customize testing scenarios diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs index f48dd927ee96..5984fa77a2c5 100644 --- a/cumulus/primitives/storage-weight-reclaim/src/lib.rs +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -165,15 +165,14 @@ where ); return Ok(()) }; - let benchmarked_weight = info.weight.proof_size(); - let consumed_weight = post_dispatch_proof_size.saturating_sub(pre_dispatch_proof_size); - // 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 storage_size_diff = - benchmarked_weight.saturating_sub(unspent).abs_diff(consumed_weight as u64); + let benchmarked_weight = info.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); // This value will be reclaimed by [`frame_system::CheckWeight`], so we need to calculate // that in. @@ -294,6 +293,45 @@ mod tests { }) } + #[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 does_nothing_without_extension() { let mut test_ext = new_test_ext(); diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile index 60698de1d6ad..55b9156e6a0a 100644 --- a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile +++ b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile @@ -1,7 +1,7 @@ # this image is built on top of existing Zombienet image ARG ZOMBIENET_IMAGE # this image uses substrate-relay image built elsewhere -ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.6 +ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.8 # metadata ARG VCS_REF diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index bc0970c01f1f..6dc87858530c 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -25,7 +25,7 @@ #![doc = simple_mermaid::mermaid!("../../mermaid/IA.mmd")] #![warn(rustdoc::broken_intra_doc_links)] #![warn(rustdoc::private_intra_doc_links)] -#![doc(html_favicon_url = "https://polkadot.network/favicon-32x32.png")] +#![doc(html_favicon_url = "https://polkadot.com/favicon.ico")] #![doc( html_logo_url = "https://europe1.discourse-cdn.com/standard21/uploads/polkadot2/original/1X/eb57081e2bb7c39e5fcb1a98b443e423fa4448ae.svg" )] diff --git a/polkadot/node/core/candidate-validation/Cargo.toml b/polkadot/node/core/candidate-validation/Cargo.toml index 13ab3e3fba50..fcacc38cae65 100644 --- a/polkadot/node/core/candidate-validation/Cargo.toml +++ b/polkadot/node/core/candidate-validation/Cargo.toml @@ -17,7 +17,6 @@ gum = { workspace = true, default-features = true } sp-keystore = { workspace = true } sp-application-crypto = { workspace = true } -sp-maybe-compressed-blob = { workspace = true, default-features = true } codec = { features = ["bit-vec", "derive"], workspace = true } polkadot-primitives = { workspace = true, default-features = true } @@ -36,5 +35,6 @@ sp-keyring = { workspace = true, default-features = true } futures = { features = ["thread-pool"], workspace = true } assert_matches = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } +sp-maybe-compressed-blob = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } polkadot-primitives-test-helpers = { workspace = true } diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index 1985964ebc51..103d29e8d269 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -27,9 +27,7 @@ use polkadot_node_core_pvf::{ InternalValidationError, InvalidCandidate as WasmInvalidCandidate, PossiblyInvalidError, PrepareError, PrepareJobKind, PvfPrepData, ValidationError, ValidationHost, }; -use polkadot_node_primitives::{ - BlockData, InvalidCandidate, PoV, ValidationResult, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT, -}; +use polkadot_node_primitives::{InvalidCandidate, PoV, ValidationResult}; use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{ @@ -41,9 +39,7 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_util as util; use polkadot_overseer::ActiveLeavesUpdate; -use polkadot_parachain_primitives::primitives::{ - ValidationParams, ValidationResult as WasmValidationResult, -}; +use polkadot_parachain_primitives::primitives::ValidationResult as WasmValidationResult; use polkadot_primitives::{ executor_params::{ DEFAULT_APPROVAL_EXECUTION_TIMEOUT, DEFAULT_BACKING_EXECUTION_TIMEOUT, @@ -504,21 +500,12 @@ where continue; }; - let pvf = match sp_maybe_compressed_blob::decompress( - &validation_code.0, - VALIDATION_CODE_BOMB_LIMIT, - ) { - Ok(code) => PvfPrepData::from_code( - code.into_owned(), - executor_params.clone(), - timeout, - PrepareJobKind::Prechecking, - ), - Err(e) => { - gum::debug!(target: LOG_TARGET, err=?e, "cannot decompress validation code"); - continue - }, - }; + let pvf = PvfPrepData::from_code( + validation_code.0, + executor_params.clone(), + timeout, + PrepareJobKind::Prechecking, + ); active_pvfs.push(pvf); processed_code_hashes.push(code_hash); @@ -651,21 +638,12 @@ where let timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Precheck); - let pvf = match sp_maybe_compressed_blob::decompress( - &validation_code.0, - VALIDATION_CODE_BOMB_LIMIT, - ) { - Ok(code) => PvfPrepData::from_code( - code.into_owned(), - executor_params, - timeout, - PrepareJobKind::Prechecking, - ), - Err(e) => { - gum::debug!(target: LOG_TARGET, err=?e, "precheck: cannot decompress validation code"); - return PreCheckOutcome::Invalid - }, - }; + let pvf = PvfPrepData::from_code( + validation_code.0, + executor_params, + timeout, + PrepareJobKind::Prechecking, + ); match validation_backend.precheck_pvf(pvf).await { Ok(_) => PreCheckOutcome::Valid, @@ -873,41 +851,7 @@ async fn validate_candidate_exhaustive( return Ok(ValidationResult::Invalid(e)) } - let raw_validation_code = match sp_maybe_compressed_blob::decompress( - &validation_code.0, - VALIDATION_CODE_BOMB_LIMIT, - ) { - Ok(code) => code, - Err(e) => { - gum::info!(target: LOG_TARGET, ?para_id, err=?e, "Invalid candidate (validation code)"); - - // Code already passed pre-checking, if decompression fails now this most likely means - // some local corruption happened. - return Err(ValidationFailed("Code decompression failed".to_string())) - }, - }; - metrics.observe_code_size(raw_validation_code.len()); - - metrics.observe_pov_size(pov.block_data.0.len(), true); - let raw_block_data = - match sp_maybe_compressed_blob::decompress(&pov.block_data.0, POV_BOMB_LIMIT) { - Ok(block_data) => BlockData(block_data.to_vec()), - Err(e) => { - gum::info!(target: LOG_TARGET, ?para_id, err=?e, "Invalid candidate (PoV code)"); - - // If the PoV is invalid, the candidate certainly is. - return Ok(ValidationResult::Invalid(InvalidCandidate::PoVDecompressionFailure)) - }, - }; - metrics.observe_pov_size(raw_block_data.0.len(), false); - - let params = ValidationParams { - parent_head: persisted_validation_data.parent_head.clone(), - block_data: raw_block_data, - relay_parent_number: persisted_validation_data.relay_parent_number, - relay_parent_storage_root: persisted_validation_data.relay_parent_storage_root, - }; - + let persisted_validation_data = Arc::new(persisted_validation_data); let result = match exec_kind { // Retry is disabled to reduce the chance of nondeterministic blocks getting backed and // honest backers getting slashed. @@ -915,7 +859,7 @@ async fn validate_candidate_exhaustive( let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare); let exec_timeout = pvf_exec_timeout(&executor_params, exec_kind); let pvf = PvfPrepData::from_code( - raw_validation_code.to_vec(), + validation_code.0, executor_params, prep_timeout, PrepareJobKind::Compilation, @@ -925,7 +869,8 @@ async fn validate_candidate_exhaustive( .validate_candidate( pvf, exec_timeout, - params.encode(), + persisted_validation_data.clone(), + pov, polkadot_node_core_pvf::Priority::Normal, ) .await @@ -933,9 +878,10 @@ async fn validate_candidate_exhaustive( PvfExecKind::Approval => validation_backend .validate_candidate_with_retry( - raw_validation_code.to_vec(), + validation_code.0, pvf_exec_timeout(&executor_params, exec_kind), - params, + persisted_validation_data.clone(), + pov, executor_params, PVF_APPROVAL_EXECUTION_RETRY_DELAY, polkadot_node_core_pvf::Priority::Critical, @@ -961,6 +907,8 @@ async fn validate_candidate_exhaustive( Ok(ValidationResult::Invalid(InvalidCandidate::Timeout)), Err(ValidationError::Invalid(WasmInvalidCandidate::WorkerReportedInvalid(e))) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(e))), + Err(ValidationError::Invalid(WasmInvalidCandidate::PoVDecompressionFailure)) => + Ok(ValidationResult::Invalid(InvalidCandidate::PoVDecompressionFailure)), Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousWorkerDeath)) => Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError( "ambiguous worker death".to_string(), @@ -1007,7 +955,7 @@ async fn validate_candidate_exhaustive( // invalid. Ok(ValidationResult::Invalid(InvalidCandidate::CommitmentsHashMismatch)) } else { - Ok(ValidationResult::Valid(outputs, persisted_validation_data)) + Ok(ValidationResult::Valid(outputs, (*persisted_validation_data).clone())) } }, } @@ -1020,7 +968,8 @@ trait ValidationBackend { &mut self, pvf: PvfPrepData, exec_timeout: Duration, - encoded_params: Vec, + pvd: Arc, + pov: Arc, // The priority for the preparation job. prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result; @@ -1035,9 +984,10 @@ trait ValidationBackend { /// preparation. async fn validate_candidate_with_retry( &mut self, - raw_validation_code: Vec, + code: Vec, exec_timeout: Duration, - params: ValidationParams, + pvd: Arc, + pov: Arc, executor_params: ExecutorParams, retry_delay: Duration, // The priority for the preparation job. @@ -1046,7 +996,7 @@ trait ValidationBackend { let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare); // Construct the PVF a single time, since it is an expensive operation. Cloning it is cheap. let pvf = PvfPrepData::from_code( - raw_validation_code, + code, executor_params, prep_timeout, PrepareJobKind::Compilation, @@ -1057,7 +1007,13 @@ trait ValidationBackend { // Use `Priority::Critical` as finality trumps parachain liveliness. let mut validation_result = self - .validate_candidate(pvf.clone(), exec_timeout, params.encode(), prepare_priority) + .validate_candidate( + pvf.clone(), + exec_timeout, + pvd.clone(), + pov.clone(), + prepare_priority, + ) .await; if validation_result.is_ok() { return validation_result @@ -1130,10 +1086,14 @@ trait ValidationBackend { validation_result ); - // Encode the params again when re-trying. We expect the retry case to be relatively - // rare, and we want to avoid unconditionally cloning data. validation_result = self - .validate_candidate(pvf.clone(), new_timeout, params.encode(), prepare_priority) + .validate_candidate( + pvf.clone(), + new_timeout, + pvd.clone(), + pov.clone(), + prepare_priority, + ) .await; } } @@ -1153,13 +1113,13 @@ impl ValidationBackend for ValidationHost { &mut self, pvf: PvfPrepData, exec_timeout: Duration, - encoded_params: Vec, + pvd: Arc, + pov: Arc, // The priority for the preparation job. prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { let (tx, rx) = oneshot::channel(); - if let Err(err) = - self.execute_pvf(pvf, exec_timeout, encoded_params, prepare_priority, tx).await + if let Err(err) = self.execute_pvf(pvf, exec_timeout, pvd, pov, prepare_priority, tx).await { return Err(InternalValidationError::HostCommunication(format!( "cannot send pvf to the validation host, it might have shut down: {:?}", diff --git a/polkadot/node/core/candidate-validation/src/metrics.rs b/polkadot/node/core/candidate-validation/src/metrics.rs index 28fc957ddb1a..1459907aa599 100644 --- a/polkadot/node/core/candidate-validation/src/metrics.rs +++ b/polkadot/node/core/candidate-validation/src/metrics.rs @@ -23,8 +23,6 @@ pub(crate) struct MetricsInner { pub(crate) validate_from_chain_state: prometheus::Histogram, pub(crate) validate_from_exhaustive: prometheus::Histogram, pub(crate) validate_candidate_exhaustive: prometheus::Histogram, - pub(crate) pov_size: prometheus::HistogramVec, - pub(crate) code_size: prometheus::Histogram, } /// Candidate validation metrics. @@ -70,21 +68,6 @@ impl Metrics { .as_ref() .map(|metrics| metrics.validate_candidate_exhaustive.start_timer()) } - - pub fn observe_code_size(&self, code_size: usize) { - if let Some(metrics) = &self.0 { - metrics.code_size.observe(code_size as f64); - } - } - - pub fn observe_pov_size(&self, pov_size: usize, compressed: bool) { - if let Some(metrics) = &self.0 { - metrics - .pov_size - .with_label_values(&[if compressed { "true" } else { "false" }]) - .observe(pov_size as f64); - } - } } impl metrics::Metrics for Metrics { @@ -121,33 +104,6 @@ impl metrics::Metrics for Metrics { ))?, registry, )?, - pov_size: prometheus::register( - prometheus::HistogramVec::new( - prometheus::HistogramOpts::new( - "polkadot_parachain_candidate_validation_pov_size", - "The compressed and decompressed size of the proof of validity of a candidate", - ) - .buckets( - prometheus::exponential_buckets(16384.0, 2.0, 10) - .expect("arguments are always valid; qed"), - ), - &["compressed"], - )?, - registry, - )?, - code_size: prometheus::register( - prometheus::Histogram::with_opts( - prometheus::HistogramOpts::new( - "polkadot_parachain_candidate_validation_code_size", - "The size of the decompressed WASM validation blob used for checking a candidate", - ) - .buckets( - prometheus::exponential_buckets(16384.0, 2.0, 10) - .expect("arguments are always valid; qed"), - ), - )?, - registry, - )?, }; Ok(Metrics(Some(metrics))) } diff --git a/polkadot/node/core/candidate-validation/src/tests.rs b/polkadot/node/core/candidate-validation/src/tests.rs index 86d855f78b45..55282fdf4ee1 100644 --- a/polkadot/node/core/candidate-validation/src/tests.rs +++ b/polkadot/node/core/candidate-validation/src/tests.rs @@ -20,6 +20,7 @@ use super::*; use assert_matches::assert_matches; use futures::executor; use polkadot_node_core_pvf::PrepareError; +use polkadot_node_primitives::{BlockData, VALIDATION_CODE_BOMB_LIMIT}; use polkadot_node_subsystem::messages::AllMessages; use polkadot_node_subsystem_util::reexports::SubsystemContext; use polkadot_overseer::ActivatedLeaf; @@ -385,7 +386,8 @@ impl ValidationBackend for MockValidateCandidateBackend { &mut self, _pvf: PvfPrepData, _timeout: Duration, - _encoded_params: Vec, + _pvd: Arc, + _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { // This is expected to panic if called more times than expected, indicating an error in the @@ -950,115 +952,6 @@ fn compressed_code_works() { assert_matches!(v, Ok(ValidationResult::Valid(_, _))); } -#[test] -fn code_decompression_failure_is_error() { - let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() }; - let pov = PoV { block_data: BlockData(vec![1; 32]) }; - let head_data = HeadData(vec![1, 1, 1]); - - let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; - let validation_code = - sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1) - .map(ValidationCode) - .unwrap(); - - let descriptor = make_valid_candidate_descriptor( - ParaId::from(1_u32), - dummy_hash(), - validation_data.hash(), - pov.hash(), - validation_code.hash(), - head_data.hash(), - dummy_hash(), - Sr25519Keyring::Alice, - ); - - let validation_result = WasmValidationResult { - head_data, - new_validation_code: None, - upward_messages: Default::default(), - horizontal_messages: Default::default(), - processed_downward_messages: 0, - hrmp_watermark: 0, - }; - - let candidate_receipt = CandidateReceipt { descriptor, commitments_hash: Hash::zero() }; - - let pool = TaskExecutor::new(); - let (_ctx, _ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< - AllMessages, - _, - >(pool.clone()); - - let v = executor::block_on(validate_candidate_exhaustive( - MockValidateCandidateBackend::with_hardcoded_result(Ok(validation_result)), - validation_data, - validation_code, - candidate_receipt, - Arc::new(pov), - ExecutorParams::default(), - PvfExecKind::Backing, - &Default::default(), - )); - - assert_matches!(v, Err(_)); -} - -#[test] -fn pov_decompression_failure_is_invalid() { - let validation_data = - PersistedValidationData { max_pov_size: POV_BOMB_LIMIT as u32, ..Default::default() }; - let head_data = HeadData(vec![1, 1, 1]); - - let raw_block_data = vec![2u8; POV_BOMB_LIMIT + 1]; - let pov = sp_maybe_compressed_blob::compress(&raw_block_data, POV_BOMB_LIMIT + 1) - .map(|raw| PoV { block_data: BlockData(raw) }) - .unwrap(); - - let validation_code = ValidationCode(vec![2; 16]); - - let descriptor = make_valid_candidate_descriptor( - ParaId::from(1_u32), - dummy_hash(), - validation_data.hash(), - pov.hash(), - validation_code.hash(), - head_data.hash(), - dummy_hash(), - Sr25519Keyring::Alice, - ); - - let validation_result = WasmValidationResult { - head_data, - new_validation_code: None, - upward_messages: Default::default(), - horizontal_messages: Default::default(), - processed_downward_messages: 0, - hrmp_watermark: 0, - }; - - let candidate_receipt = CandidateReceipt { descriptor, commitments_hash: Hash::zero() }; - - let pool = TaskExecutor::new(); - let (_ctx, _ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< - AllMessages, - _, - >(pool.clone()); - - let v = executor::block_on(validate_candidate_exhaustive( - MockValidateCandidateBackend::with_hardcoded_result(Ok(validation_result)), - validation_data, - validation_code, - candidate_receipt, - Arc::new(pov), - ExecutorParams::default(), - PvfExecKind::Backing, - &Default::default(), - )); - - assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::PoVDecompressionFailure))); -} - struct MockPreCheckBackend { result: Result<(), PrepareError>, } @@ -1075,7 +968,8 @@ impl ValidationBackend for MockPreCheckBackend { &mut self, _pvf: PvfPrepData, _timeout: Duration, - _encoded_params: Vec, + _pvd: Arc, + _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { unreachable!() @@ -1149,70 +1043,6 @@ fn precheck_works() { executor::block_on(test_fut); } -#[test] -fn precheck_invalid_pvf_blob_compression() { - let relay_parent = [3; 32].into(); - - let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; - let validation_code = - sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1) - .map(ValidationCode) - .unwrap(); - let validation_code_hash = validation_code.hash(); - - let pool = TaskExecutor::new(); - let (mut ctx, mut ctx_handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< - AllMessages, - _, - >(pool.clone()); - - let (check_fut, check_result) = precheck_pvf( - ctx.sender(), - MockPreCheckBackend::with_hardcoded_result(Ok(())), - relay_parent, - validation_code_hash, - ) - .remote_handle(); - - let test_fut = async move { - assert_matches!( - ctx_handle.recv().await, - AllMessages::RuntimeApi(RuntimeApiMessage::Request( - rp, - RuntimeApiRequest::ValidationCodeByHash( - vch, - tx - ), - )) => { - assert_eq!(vch, validation_code_hash); - assert_eq!(rp, relay_parent); - - let _ = tx.send(Ok(Some(validation_code.clone()))); - } - ); - assert_matches!( - ctx_handle.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionIndexForChild(tx)) - ) => { - tx.send(Ok(1u32.into())).unwrap(); - } - ); - assert_matches!( - ctx_handle.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionExecutorParams(_, tx)) - ) => { - tx.send(Ok(Some(ExecutorParams::default()))).unwrap(); - } - ); - assert_matches!(check_result.await, PreCheckOutcome::Invalid); - }; - - let test_fut = future::join(test_fut, check_fut); - executor::block_on(test_fut); -} - #[test] fn precheck_properly_classifies_outcomes() { let inner = |prepare_result, precheck_outcome| { @@ -1292,7 +1122,8 @@ impl ValidationBackend for MockHeadsUp { &mut self, _pvf: PvfPrepData, _timeout: Duration, - _encoded_params: Vec, + _pvd: Arc, + _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, ) -> Result { unreachable!() diff --git a/polkadot/node/core/prospective-parachains/Cargo.toml b/polkadot/node/core/prospective-parachains/Cargo.toml index 97da5a1e94a0..705014e67a05 100644 --- a/polkadot/node/core/prospective-parachains/Cargo.toml +++ b/polkadot/node/core/prospective-parachains/Cargo.toml @@ -12,24 +12,18 @@ workspace = true [dependencies] futures = { workspace = true } gum = { workspace = true, default-features = true } -codec = { workspace = true, default-features = true } thiserror = { workspace = true } fatality = { workspace = true } -bitvec = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } -polkadot-node-primitives = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } [dev-dependencies] assert_matches = { workspace = true } polkadot-node-subsystem-test-helpers = { workspace = true } -polkadot-node-subsystem-types = { workspace = true, default-features = true } polkadot-primitives-test-helpers = { workspace = true } +sp-tracing = { workspace = true } sp-core = { workspace = true, default-features = true } -sc-keystore = { workspace = true, default-features = true } -sp-application-crypto = { workspace = true, default-features = true } -sp-keyring = { workspace = true, default-features = true } -sp-keystore = { workspace = true, default-features = true } +rand = { workspace = true } rstest = { workspace = true } diff --git a/polkadot/node/core/prospective-parachains/src/error.rs b/polkadot/node/core/prospective-parachains/src/error.rs index 2b0933ab1c7e..4b332b9c5de5 100644 --- a/polkadot/node/core/prospective-parachains/src/error.rs +++ b/polkadot/node/core/prospective-parachains/src/error.rs @@ -30,18 +30,6 @@ use fatality::Nested; #[allow(missing_docs)] #[fatality::fatality(splitable)] pub enum Error { - #[fatal] - #[error("SubsystemError::Context error: {0}")] - SubsystemContext(String), - - #[fatal] - #[error("Spawning a task failed: {0}")] - SpawnFailed(SubsystemError), - - #[fatal] - #[error("Participation worker receiver exhausted.")] - ParticipationWorkerReceiverExhausted, - #[fatal] #[error("Receiving message from overseer failed: {0}")] SubsystemReceive(#[source] SubsystemError), @@ -55,9 +43,6 @@ pub enum Error { #[error(transparent)] ChainApi(#[from] ChainApiError), - #[error(transparent)] - Subsystem(SubsystemError), - #[error("Request to chain API subsystem dropped")] ChainApiRequestCanceled(oneshot::Canceled), 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 b5fe70e76923..b060897d4391 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/mod.rs @@ -18,41 +18,58 @@ //! //! # Overview //! -//! This module exposes two main types: [`FragmentChain`] and [`CandidateStorage`] which are meant -//! to be used in close conjunction. Each fragment chain is associated with a particular -//! relay-parent and each node in the chain represents a candidate. Each parachain has a single -//! candidate storage, but can have one chain for each relay chain block in the view. -//! Therefore, the same candidate can be present in multiple fragment chains of a parachain. One of -//! the purposes of the candidate storage is to deduplicate the large candidate data that is being -//! referenced from multiple fragment chains. +//! The main type exposed by this module is the [`FragmentChain`]. //! -//! A chain has an associated [`Scope`] which defines limits on candidates within the chain. -//! Candidates themselves have their own [`Constraints`] which are either the constraints from the -//! scope, or, if there are previous nodes in the chain, a modified version of the previous -//! candidate's constraints. +//! Each fragment chain is associated with a particular relay-parent (an active leaf) and has a +//! [`Scope`], which contains the allowed relay parents (up to `allowed_ancestry_len`), the pending +//! availability candidates and base constraints derived from the latest included candidate. Each +//! parachain has a single `FragmentChain` for each active leaf where it's scheduled. //! -//! Another use of the `CandidateStorage` is to keep a record of candidates which may not be yet -//! included in any chain, but which may become part of a chain in the future. This is needed for -//! elastic scaling, so that we may parallelise the backing process across different groups. As long -//! as some basic constraints are not violated by an unconnected candidate (like the relay parent -//! being in scope), we proceed with the backing process, hoping that its predecessors will be -//! backed soon enough. This is commonly called a potential candidate. Note that not all potential -//! candidates will be maintained in the CandidateStorage. The total number of connected + potential -//! candidates will be at most max_candidate_depth + 1. +//! A fragment chain consists mainly of the current best backable chain (we'll call this the best +//! chain) and a storage of unconnected potential candidates (we'll call this the unconnected +//! storage). +//! +//! The best chain contains all the candidates pending availability and a subsequent chain +//! of candidates that have reached the backing quorum and are better than any other backable forks +//! according to the fork selection rule (more on this rule later). It has a length of size at most +//! `max_candidate_depth + 1`. +//! +//! The unconnected storage keeps a record of seconded/backable candidates that may be +//! added to the best chain in the future. +//! Once a candidate is seconded, it becomes part of this unconnected storage. +//! Only after it is backed it may be added to the best chain (but not necessarily). It's only +//! added if it builds on the latest candidate in the chain and if there isn't a better backable +//! candidate according to the fork selection rule. +//! +//! An important thing to note is that the candidates present in the unconnected storage may have +//! any/no relationship between them. In other words, they may form N trees and may even form +//! cycles. This is needed so that we may begin validating candidates for which we don't yet know +//! their parent (so we may parallelize the backing process across different groups for elastic +//! scaling) and so that we accept parachain forks. +//! +//! We accept parachain forks only if the fork selection rule allows for it. In other words, if we +//! have a backed candidate, we begin seconding/validating a fork only if it has a lower candidate +//! hash. Once both forks are backed, we discard the one with the higher candidate hash. +//! We assume all validators pick the same fork according to the fork selection rule. If we decided +//! to not accept parachain forks, candidates could end up getting only half of the backing votes or +//! even less (for forks of larger arity). This would affect the validator rewards. Still, we don't +//! guarantee that a fork-producing parachains will be able to fully use elastic scaling. +//! +//! Once a candidate is backed and becomes part of the best chain, we can trim from the +//! unconnected storage candidates which constitute forks on the best chain and no longer have +//! potential. //! //! This module also makes use of types provided by the Inclusion Emulator module, such as //! [`Fragment`] and [`Constraints`]. These perform the actual job of checking for validity of //! prospective fragments. //! -//! # Parachain forks +//! # Fork choice rule //! -//! Parachains are expected to not create forks, hence the use of fragment chains as opposed to -//! fragment trees. If parachains do create forks, their performance in regards to async backing and -//! elastic scaling will suffer, because different validators will have different views of the -//! future. +//! The motivation for the fork choice rule is described in the previous chapter. //! -//! This is a compromise we can make - collators which want to use async backing and elastic scaling -//! need to cooperate for the highest throughput. +//! The current rule is: choose the candidate with the lower candidate hash. +//! The candidate hash is quite random and finding a candidate with a lower hash in order to favour +//! it would essentially mean solving a proof of work problem. //! //! # Parachain cycles //! @@ -65,70 +82,117 @@ //! resolved by having candidates reference their parent by candidate hash. //! //! However, dealing with cycles increases complexity during the backing/inclusion process for no -//! practical reason. Therefore, fragment chains will not accept such candidates. +//! practical reason. +//! These cycles may be accepted by fragment chains while candidates are part of the unconnected +//! storage, but they will definitely not make it to the best chain. //! //! On the other hand, enforcing that a parachain will NEVER be acyclic would be very complicated //! (looping through the entire parachain's history on every new candidate or changing the candidate //! receipt to reference the parent's candidate hash). //! +//! Therefore, we don't provide a guarantee that a cycle-producing parachain will work (although in +//! practice they probably will if the cycle length is larger than the number of assigned cores +//! multiplied by two). +//! //! # Spam protection //! -//! As long as the [`CandidateStorage`] has bounded input on the number of candidates supplied, -//! [`FragmentChain`] complexity is bounded. This means that higher-level code needs to be selective -//! about limiting the amount of candidates that are considered. +//! As long as the supplied number of candidates is bounded, [`FragmentChain`] complexity is +//! bounded. This means that higher-level code needs to be selective about limiting the amount of +//! candidates that are considered. +//! +//! Practically speaking, the collator-protocol will not allow more than `max_candidate_depth + 1` +//! collations to be fetched at a relay parent and statement-distribution will not allow more than +//! `max_candidate_depth + 1` seconded candidates at a relay parent per each validator in the +//! backing group. Considering the `allowed_ancestry_len` configuration value, the number of +//! candidates in a `FragmentChain` (including its unconnected storage) should not exceed: +//! +//! `allowed_ancestry_len * (max_candidate_depth + 1) * backing_group_size`. //! //! The code in this module is not designed for speed or efficiency, but conceptual simplicity. //! Our assumption is that the amount of candidates and parachains we consider will be reasonably //! bounded and in practice will not exceed a few thousand at any time. This naive implementation //! will still perform fairly well under these conditions, despite being somewhat wasteful of //! memory. +//! +//! Still, the expensive candidate data (CandidateCommitments) are wrapped in an `Arc` and shared +//! across fragment chains of the same para on different active leaves. #[cfg(test)] mod tests; use std::{ + cmp::{min, Ordering}, collections::{ hash_map::{Entry, HashMap}, - BTreeMap, HashSet, + BTreeMap, HashSet, VecDeque, }, sync::Arc, }; use super::LOG_TARGET; -use polkadot_node_subsystem::messages::{Ancestors, HypotheticalCandidate}; +use polkadot_node_subsystem::messages::Ancestors; use polkadot_node_subsystem_util::inclusion_emulator::{ - ConstraintModifications, Constraints, Fragment, ProspectiveCandidate, RelayChainBlockInfo, + self, ConstraintModifications, Constraints, Fragment, HypotheticalOrConcreteCandidate, + ProspectiveCandidate, RelayChainBlockInfo, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CommittedCandidateReceipt, Hash, HeadData, Id as ParaId, - PersistedValidationData, + BlockNumber, CandidateCommitments, CandidateHash, CommittedCandidateReceipt, Hash, HeadData, + PersistedValidationData, ValidationCodeHash, }; +use thiserror::Error; + +/// Fragment chain related errors. +#[derive(Debug, Clone, PartialEq, Error)] +pub(crate) enum Error { + #[error("Candidate already known")] + CandidateAlreadyKnown, + #[error("Candidate's parent head is equal to its output head. Would introduce a cycle.")] + ZeroLengthCycle, + #[error("Candidate would introduce a cycle")] + Cycle, + #[error("Candidate would introduce two paths to the same output state")] + MultiplePaths, + #[error("Attempting to directly introduce a Backed candidate. It should first be introduced as Seconded")] + IntroduceBackedCandidate, + #[error("Relay parent {0:?} of the candidate precedes the relay parent {1:?} of a pending availability candidate")] + RelayParentPrecedesCandidatePendingAvailability(Hash, Hash), + #[error("Candidate would introduce a fork with a pending availability candidate: {0:?}")] + ForkWithCandidatePendingAvailability(CandidateHash), + #[error("Fork selection rule favours another candidate: {0:?}")] + ForkChoiceRule(CandidateHash), + #[error("Could not find parent of the candidate")] + ParentCandidateNotFound, + #[error("Could not compute candidate constraints: {0:?}")] + ComputeConstraints(inclusion_emulator::ModificationError), + #[error("Candidate violates constraints: {0:?}")] + CheckAgainstConstraints(inclusion_emulator::FragmentValidityError), + #[error("Relay parent would move backwards from the latest candidate in the chain")] + RelayParentMovedBackwards, + #[error(transparent)] + CandidateEntry(#[from] CandidateEntryError), + #[error("Relay parent {0:?} not in scope. Earliest relay parent allowed {1:?}")] + RelayParentNotInScope(Hash, Hash), +} -/// Kinds of failures to import a candidate into storage. -#[derive(Debug, Clone, PartialEq)] -pub enum CandidateStorageInsertionError { - /// An error indicating that a supplied candidate didn't match the persisted - /// validation data provided alongside it. - PersistedValidationDataMismatch, - /// The candidate was already known. - CandidateAlreadyKnown(CandidateHash), +/// The rule for selecting between two backed candidate forks, when adding to the chain. +/// All validators should adhere to this rule, in order to not lose out on rewards in case of +/// forking parachains. +fn fork_selection_rule(hash1: &CandidateHash, hash2: &CandidateHash) -> Ordering { + hash1.cmp(hash2) } -/// Stores candidates and information about them such as their relay-parents and their backing -/// states. +/// Utility for storing candidates and information about them such as their relay-parents and their +/// backing states. This does not assume any restriction on whether or not the candidates form a +/// chain. Useful for storing all kinds of candidates. #[derive(Clone, Default)] pub(crate) struct CandidateStorage { - // Index from head data hash to candidate hashes with that head data as a parent. Purely for + // Index from head data hash to candidate hashes with that head data as a parent. Useful for // efficiency when responding to `ProspectiveValidationDataRequest`s or when trying to find a // new candidate to push to a chain. - // Even though having multiple candidates with same parent would be invalid for a parachain, it - // could happen across different relay chain forks, hence the HashSet. by_parent_head: HashMap>, - // Index from head data hash to candidate hashes outputting that head data. Purely for + // Index from head data hash to candidate hashes outputting that head data. For // efficiency when responding to `ProspectiveValidationDataRequest`s. - // Even though having multiple candidates with same output would be invalid for a parachain, - // it could happen across different relay chain forks. by_output_head: HashMap>, // Index from candidate hash to fragment node. @@ -136,63 +200,59 @@ pub(crate) struct CandidateStorage { } impl CandidateStorage { - /// Introduce a new candidate. - pub fn add_candidate( + /// Introduce a new pending availability candidate. + pub fn add_pending_availability_candidate( &mut self, + candidate_hash: CandidateHash, candidate: CommittedCandidateReceipt, persisted_validation_data: PersistedValidationData, - state: CandidateState, - ) -> Result { - let candidate_hash = candidate.hash(); - if self.by_candidate_hash.contains_key(&candidate_hash) { - return Err(CandidateStorageInsertionError::CandidateAlreadyKnown(candidate_hash)) - } + ) -> Result<(), Error> { + let entry = CandidateEntry::new( + candidate_hash, + candidate, + persisted_validation_data, + CandidateState::Backed, + )?; - if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash { - return Err(CandidateStorageInsertionError::PersistedValidationDataMismatch) - } + self.add_candidate_entry(entry) + } - let entry = CandidateEntry { - candidate_hash, - parent_head_data_hash: persisted_validation_data.parent_head.hash(), - output_head_data_hash: candidate.commitments.head_data.hash(), - 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, - }), - }; + /// Return the number of stored candidates. + pub fn len(&self) -> usize { + self.by_candidate_hash.len() + } + + /// Introduce a new candidate entry. + fn add_candidate_entry(&mut self, candidate: CandidateEntry) -> Result<(), Error> { + let candidate_hash = candidate.candidate_hash; + if self.by_candidate_hash.contains_key(&candidate_hash) { + return Err(Error::CandidateAlreadyKnown) + } self.by_parent_head - .entry(entry.parent_head_data_hash()) + .entry(candidate.parent_head_data_hash) .or_default() .insert(candidate_hash); self.by_output_head - .entry(entry.output_head_data_hash()) + .entry(candidate.output_head_data_hash) .or_default() .insert(candidate_hash); - // sanity-checked already. - self.by_candidate_hash.insert(candidate_hash, entry); + self.by_candidate_hash.insert(candidate_hash, candidate); - Ok(candidate_hash) + Ok(()) } /// Remove a candidate from the store. - pub fn remove_candidate(&mut self, candidate_hash: &CandidateHash) { + fn remove_candidate(&mut self, candidate_hash: &CandidateHash) { if let Some(entry) = self.by_candidate_hash.remove(candidate_hash) { - if let Entry::Occupied(mut e) = self.by_parent_head.entry(entry.parent_head_data_hash()) - { + if let Entry::Occupied(mut e) = self.by_parent_head.entry(entry.parent_head_data_hash) { e.get_mut().remove(&candidate_hash); if e.get().is_empty() { e.remove(); } } - if let Entry::Occupied(mut e) = self.by_output_head.entry(entry.output_head_data_hash()) - { + if let Entry::Occupied(mut e) = self.by_output_head.entry(entry.output_head_data_hash) { e.get_mut().remove(&candidate_hash); if e.get().is_empty() { e.remove(); @@ -202,7 +262,7 @@ impl CandidateStorage { } /// Note that an existing candidate has been backed. - pub fn mark_backed(&mut self, candidate_hash: &CandidateHash) { + fn mark_backed(&mut self, candidate_hash: &CandidateHash) { if let Some(entry) = self.by_candidate_hash.get_mut(candidate_hash) { gum::trace!(target: LOG_TARGET, ?candidate_hash, "Candidate marked as backed"); entry.state = CandidateState::Backed; @@ -211,38 +271,18 @@ impl CandidateStorage { } } - /// Whether a candidate is recorded as being backed. - pub fn is_backed(&self, candidate_hash: &CandidateHash) -> bool { - self.by_candidate_hash - .get(candidate_hash) - .map_or(false, |e| e.state == CandidateState::Backed) - } - /// Whether a candidate is contained within the storage already. - pub fn contains(&self, candidate_hash: &CandidateHash) -> bool { + fn contains(&self, candidate_hash: &CandidateHash) -> bool { self.by_candidate_hash.contains_key(candidate_hash) } - /// Return an iterator over the stored candidates. - pub fn candidates(&self) -> impl Iterator { + /// Return an iterator over references to the stored candidates, in arbitrary order. + fn candidates(&self) -> impl Iterator { self.by_candidate_hash.values() } - /// Retain only candidates which pass the predicate. - pub(crate) fn retain(&mut self, pred: impl Fn(&CandidateHash) -> bool) { - self.by_candidate_hash.retain(|h, _v| pred(h)); - self.by_parent_head.retain(|_parent, children| { - children.retain(|h| pred(h)); - !children.is_empty() - }); - self.by_output_head.retain(|_output, candidates| { - candidates.retain(|h| pred(h)); - !candidates.is_empty() - }); - } - - /// Get head-data by hash. - pub(crate) fn head_data_by_hash(&self, hash: &Hash) -> Option<&HeadData> { + /// Try getting head-data by hash. + fn head_data_by_hash(&self, hash: &Hash) -> Option<&HeadData> { // First, search for candidates outputting this head data and extract the head data // from their commitments if they exist. // @@ -262,16 +302,8 @@ impl CandidateStorage { }) } - /// Returns candidate's relay parent, if present. - pub(crate) fn relay_parent_of_candidate(&self, candidate_hash: &CandidateHash) -> Option { - self.by_candidate_hash.get(candidate_hash).map(|entry| entry.relay_parent) - } - - /// Returns the candidates which have the given head data hash as parent. - /// We don't allow forks in a parachain, but we may have multiple candidates with same parent - /// across different relay chain forks. That's why it returns an iterator (but only one will be - /// valid and used in the end). - fn possible_para_children<'a>( + /// Returns the backed candidates which have the given head data hash as parent. + fn possible_backed_para_children<'a>( &'a self, parent_head_hash: &'a Hash, ) -> impl Iterator + 'a { @@ -280,12 +312,11 @@ impl CandidateStorage { .get(parent_head_hash) .into_iter() .flat_map(|hashes| hashes.iter()) - .filter_map(move |h| by_candidate_hash.get(h)) - } - - #[cfg(test)] - pub fn len(&self) -> (usize, usize) { - (self.by_parent_head.len(), self.by_candidate_hash.len()) + .filter_map(move |h| { + by_candidate_hash.get(h).and_then(|candidate| { + (candidate.state == CandidateState::Backed).then_some(candidate) + }) + }) } } @@ -293,14 +324,24 @@ impl CandidateStorage { /// /// Candidates aren't even considered until they've at least been seconded. #[derive(Debug, PartialEq, Clone)] -pub(crate) enum CandidateState { +enum CandidateState { /// The candidate has been seconded. Seconded, /// The candidate has been completely backed by the group. Backed, } +#[derive(Debug, Clone, PartialEq, Error)] +/// Possible errors when construcing a candidate entry. +pub enum CandidateEntryError { + #[error("Candidate does not match the persisted validation data provided alongside it")] + PersistedValidationDataMismatch, + #[error("Candidate's parent head is equal to its output head. Would introduce a cycle")] + ZeroLengthCycle, +} + #[derive(Debug, Clone)] +/// Representation of a candidate into the [`CandidateStorage`]. pub(crate) struct CandidateEntry { candidate_hash: CandidateHash, parent_head_data_hash: Hash, @@ -311,16 +352,79 @@ pub(crate) struct CandidateEntry { } impl CandidateEntry { + /// Create a new seconded candidate entry. + pub fn new_seconded( + candidate_hash: CandidateHash, + candidate: CommittedCandidateReceipt, + persisted_validation_data: PersistedValidationData, + ) -> Result { + Self::new(candidate_hash, candidate, persisted_validation_data, CandidateState::Seconded) + } + pub fn hash(&self) -> CandidateHash { self.candidate_hash } - pub fn parent_head_data_hash(&self) -> Hash { + fn new( + candidate_hash: CandidateHash, + candidate: CommittedCandidateReceipt, + persisted_validation_data: PersistedValidationData, + state: CandidateState, + ) -> Result { + if persisted_validation_data.hash() != candidate.descriptor.persisted_validation_data_hash { + return Err(CandidateEntryError::PersistedValidationDataMismatch) + } + + let parent_head_data_hash = persisted_validation_data.parent_head.hash(); + let output_head_data_hash = candidate.commitments.head_data.hash(); + + if parent_head_data_hash == output_head_data_hash { + return Err(CandidateEntryError::ZeroLengthCycle) + } + + Ok(Self { + candidate_hash, + parent_head_data_hash, + output_head_data_hash, + 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, + }), + }) + } +} + +impl HypotheticalOrConcreteCandidate for CandidateEntry { + fn commitments(&self) -> Option<&CandidateCommitments> { + Some(&self.candidate.commitments) + } + + fn persisted_validation_data(&self) -> Option<&PersistedValidationData> { + Some(&self.candidate.persisted_validation_data) + } + + fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + Some(&self.candidate.validation_code_hash) + } + + fn parent_head_data_hash(&self) -> Hash { self.parent_head_data_hash } - pub fn output_head_data_hash(&self) -> Hash { - self.output_head_data_hash + fn output_head_data_hash(&self) -> Option { + Some(self.output_head_data_hash) + } + + fn relay_parent(&self) -> Hash { + self.relay_parent + } + + fn candidate_hash(&self) -> CandidateHash { + self.candidate_hash } } @@ -337,8 +441,6 @@ pub(crate) struct PendingAvailability { /// The scope of a [`FragmentChain`]. #[derive(Debug, Clone)] pub(crate) struct Scope { - /// The assigned para id of this `FragmentChain`. - para: ParaId, /// The relay parent we're currently building on top of. relay_parent: RelayChainBlockInfo, /// The other relay parents candidates are allowed to build upon, mapped by the block number. @@ -356,10 +458,14 @@ pub(crate) struct Scope { /// An error variant indicating that ancestors provided to a scope /// had unexpected order. #[derive(Debug)] -pub struct UnexpectedAncestor { +pub(crate) struct UnexpectedAncestor { /// The block number that this error occurred at. + /// Allow as dead code, but it's being read in logs. + #[allow(dead_code)] pub number: BlockNumber, /// The previous seen block number, which did not match `number`. + /// Allow as dead code, but it's being read in logs. + #[allow(dead_code)] pub prev: BlockNumber, } @@ -381,7 +487,6 @@ impl Scope { /// /// It is allowed to provide zero ancestors. pub fn with_ancestors( - para: ParaId, relay_parent: RelayChainBlockInfo, base_constraints: Constraints, pending_availability: Vec, @@ -408,7 +513,6 @@ impl Scope { } Ok(Scope { - para, relay_parent, base_constraints, pending_availability, @@ -436,24 +540,29 @@ impl Scope { self.ancestors_by_hash.get(hash).map(|info| info.clone()) } + /// Get the base constraints of the scope + pub fn base_constraints(&self) -> &Constraints { + &self.base_constraints + } + /// Whether the candidate in question is one pending availability in this scope. - pub fn get_pending_availability( + fn get_pending_availability( &self, candidate_hash: &CandidateHash, ) -> Option<&PendingAvailability> { self.pending_availability.iter().find(|c| &c.candidate_hash == candidate_hash) } - - /// Get the base constraints of the scope - pub fn base_constraints(&self) -> &Constraints { - &self.base_constraints - } } -pub struct FragmentNode { +#[cfg_attr(test, derive(Clone))] +/// A node that is part of a `BackedChain`. It holds constraints based on the ancestors in the +/// chain. +struct FragmentNode { fragment: Fragment, candidate_hash: CandidateHash, cumulative_modifications: ConstraintModifications, + parent_head_data_hash: Hash, + output_head_data_hash: Hash, } impl FragmentNode { @@ -462,211 +571,336 @@ impl FragmentNode { } } -/// Response given by `can_add_candidate_as_potential` -#[derive(PartialEq, Debug)] -pub enum PotentialAddition { - /// Can be added as either connected or unconnected candidate. - Anyhow, - /// Can only be added as a connected candidate to the chain. - IfConnected, - /// Cannot be added. - None, +impl From<&FragmentNode> for CandidateEntry { + fn from(node: &FragmentNode) -> Self { + // We don't need to perform the checks done in `CandidateEntry::new()`, since a + // `FragmentNode` always comes from a `CandidateEntry` + Self { + candidate_hash: node.candidate_hash, + parent_head_data_hash: node.parent_head_data_hash, + output_head_data_hash: node.output_head_data_hash, + candidate: node.fragment.candidate_clone(), + relay_parent: node.relay_parent(), + // A fragment node is always backed. + state: CandidateState::Backed, + } + } } -/// This is a chain of candidates based on some underlying storage of candidates and a scope. +/// A candidate chain of backed/backable candidates. +/// Includes the candidates pending availability and candidates which may be backed on-chain. +#[derive(Default)] +#[cfg_attr(test, derive(Clone))] +struct BackedChain { + // Holds the candidate chain. + chain: Vec, + // Index from head data hash to the candidate hash with that head data as a parent. + // Only contains the candidates present in the `chain`. + by_parent_head: HashMap, + // Index from head data hash to the candidate hash outputting that head data. + // Only contains the candidates present in the `chain`. + by_output_head: HashMap, + // A set of the candidate hashes in the `chain`. + candidates: HashSet, +} + +impl BackedChain { + fn push(&mut self, candidate: FragmentNode) { + self.candidates.insert(candidate.candidate_hash); + self.by_parent_head + .insert(candidate.parent_head_data_hash, candidate.candidate_hash); + self.by_output_head + .insert(candidate.output_head_data_hash, candidate.candidate_hash); + self.chain.push(candidate); + } + + fn clear(&mut self) -> Vec { + self.by_parent_head.clear(); + self.by_output_head.clear(); + self.candidates.clear(); + + std::mem::take(&mut self.chain) + } + + fn revert_to_parent_hash<'a>( + &'a mut self, + parent_head_data_hash: &Hash, + ) -> impl Iterator + 'a { + let mut found_index = None; + for index in 0..self.chain.len() { + let node = &self.chain[0]; + + if found_index.is_some() { + self.by_parent_head.remove(&node.parent_head_data_hash); + self.by_output_head.remove(&node.output_head_data_hash); + self.candidates.remove(&node.candidate_hash); + } else if &node.output_head_data_hash == parent_head_data_hash { + found_index = Some(index); + } + } + + if let Some(index) = found_index { + self.chain.drain(min(index + 1, self.chain.len())..) + } else { + // Don't remove anything, but use drain to satisfy the compiler. + self.chain.drain(0..0) + } + } + + fn contains(&self, hash: &CandidateHash) -> bool { + self.candidates.contains(hash) + } +} + +/// This is the fragment chain specific to an active leaf. /// -/// All nodes in the chain must be either pending availability or within the scope. Within the scope -/// means it's built off of the relay-parent or an ancestor. +/// It holds the current best backable candidate chain, as well as potential candidates +/// which could become connected to the chain in the future or which could even overwrite the +/// existing chain. +#[cfg_attr(test, derive(Clone))] pub(crate) struct FragmentChain { + // The current scope, which dictates the on-chain operating constraints that all future + // candidates must adhere to. scope: Scope, - chain: Vec, - - candidates: HashSet, + // The current best chain of backable candidates. It only contains candidates which build on + // top of each other and which have reached the backing quorum. In the presence of potential + // forks, this chain will pick a fork according to the `fork_selection_rule`. + best_chain: BackedChain, - // Index from head data hash to candidate hashes with that head data as a parent. - by_parent_head: HashMap, - // Index from head data hash to candidate hashes outputting that head data. - by_output_head: HashMap, + // The potential candidate storage. Contains candidates which are not yet part of the `chain` + // but may become in the future. These can form any tree shape as well as contain any + // unconnected candidates for which we don't know the parent. + unconnected: CandidateStorage, } impl FragmentChain { - /// Create a new [`FragmentChain`] with given scope and populated from the storage. - pub fn populate(scope: Scope, storage: &CandidateStorage) -> Self { - gum::trace!( - target: LOG_TARGET, - relay_parent = ?scope.relay_parent.hash, - relay_parent_num = scope.relay_parent.number, - para_id = ?scope.para, - ancestors = scope.ancestors.len(), - "Instantiating Fragment Chain", - ); - + /// Create a new [`FragmentChain`] with the given scope and populate it with the candidates + /// pending availability. + pub fn init(scope: Scope, mut candidates_pending_availability: CandidateStorage) -> Self { let mut fragment_chain = Self { scope, - chain: Vec::new(), - candidates: HashSet::new(), - by_parent_head: HashMap::new(), - by_output_head: HashMap::new(), + best_chain: BackedChain::default(), + unconnected: CandidateStorage::default(), }; - fragment_chain.populate_chain(storage); + // We only need to populate the best backable chain. Candidates pending availability must + // form a chain with the latest included head. + fragment_chain.populate_chain(&mut candidates_pending_availability); fragment_chain } - /// Get the scope of the Fragment Chain. + /// Populate the [`FragmentChain`] given the new candidates pending availability and the + /// optional previous fragment chain (of the previous relay parent). + pub fn populate_from_previous(&mut self, prev_fragment_chain: &FragmentChain) { + let mut prev_storage = prev_fragment_chain.unconnected.clone(); + + for candidate in prev_fragment_chain.best_chain.chain.iter() { + // If they used to be pending availability, don't add them. This is fine + // because: + // - if they still are pending availability, they have already been added to the new + // storage. + // - if they were included, no point in keeping them. + // + // This cannot happen for the candidates in the unconnected storage. The pending + // availability candidates will always be part of the best chain. + if prev_fragment_chain + .scope + .get_pending_availability(&candidate.candidate_hash) + .is_none() + { + let _ = prev_storage.add_candidate_entry(candidate.into()); + } + } + + // First populate the best backable chain. + self.populate_chain(&mut prev_storage); + + // Now that we picked the best backable chain, trim the forks generated by candidates which + // are not present in the best chain. + self.trim_uneligible_forks(&mut prev_storage, None); + + // Finally, keep any candidates which haven't been trimmed but still have potential. + self.populate_unconnected_potential_candidates(prev_storage); + } + + /// Get the scope of the [`FragmentChain`]. pub fn scope(&self) -> &Scope { &self.scope } - /// Returns the number of candidates in the chain - pub(crate) fn len(&self) -> usize { - self.candidates.len() + /// Returns the number of candidates in the best backable chain. + pub fn best_chain_len(&self) -> usize { + self.best_chain.chain.len() } - /// Whether the candidate exists. - pub(crate) fn contains_candidate(&self, candidate: &CandidateHash) -> bool { - self.candidates.contains(candidate) + /// Returns the number of candidates in unconnected potential storage. + pub fn unconnected_len(&self) -> usize { + self.unconnected.len() + } + + /// Whether the candidate exists as part of the unconnected potential candidates. + pub fn contains_unconnected_candidate(&self, candidate: &CandidateHash) -> bool { + self.unconnected.contains(candidate) } /// Return a vector of the chain's candidate hashes, in-order. - pub(crate) fn to_vec(&self) -> Vec { - self.chain.iter().map(|candidate| candidate.candidate_hash).collect() + pub fn best_chain_vec(&self) -> Vec { + self.best_chain.chain.iter().map(|candidate| candidate.candidate_hash).collect() } - /// Try accumulating more candidates onto the chain. - /// - /// Candidates can only be added if they build on the already existing chain. - pub(crate) fn extend_from_storage(&mut self, storage: &CandidateStorage) { - self.populate_chain(storage); + /// Return a vector of the unconnected potential candidate hashes, in arbitrary order. + pub fn unconnected(&self) -> impl Iterator { + self.unconnected.candidates() } - /// Returns the hypothetical state of a candidate with the given hash and parent head data - /// in regards to the existing chain. - /// - /// Returns true if either: - /// - the candidate is already present - /// - the candidate can be added to the chain - /// - the candidate could potentially be added to the chain in the future (its ancestors are - /// still unknown but it doesn't violate other rules). - /// - /// If this returns false, the candidate could never be added to the current chain (not now, not - /// ever) - pub(crate) fn hypothetical_membership( + /// Return whether this candidate is backed in this chain or the unconnected storage. + pub fn is_candidate_backed(&self, hash: &CandidateHash) -> bool { + self.best_chain.candidates.contains(hash) || + matches!( + self.unconnected.by_candidate_hash.get(hash), + Some(candidate) if candidate.state == CandidateState::Backed + ) + } + + /// Mark a candidate as backed. This can trigger a recreation of the best backable chain. + pub fn candidate_backed(&mut self, newly_backed_candidate: &CandidateHash) { + // Already backed. + if self.best_chain.candidates.contains(newly_backed_candidate) { + return + } + let Some(parent_head_hash) = self + .unconnected + .by_candidate_hash + .get(newly_backed_candidate) + .map(|entry| entry.parent_head_data_hash) + else { + // Candidate is not in unconnected storage. + return + }; + + // Mark the candidate hash. + self.unconnected.mark_backed(newly_backed_candidate); + + // Revert to parent_head_hash + if !self.revert_to(&parent_head_hash) { + // If nothing was reverted, there is nothing we can do for now. + return + } + + let mut prev_storage = std::mem::take(&mut self.unconnected); + + // Populate the chain. + self.populate_chain(&mut prev_storage); + + // Now that we picked the best backable chain, trim the forks generated by candidates + // which are not present in the best chain. We can start trimming from this candidate + // onwards. + self.trim_uneligible_forks(&mut prev_storage, Some(parent_head_hash)); + + // Finally, keep any candidates which haven't been trimmed but still have potential. + self.populate_unconnected_potential_candidates(prev_storage); + } + + /// Checks if this candidate could be added in the future to this chain. + /// This will return `Error::CandidateAlreadyKnown` if the candidate is already in the chain or + /// the unconnected candidate storage. + pub fn can_add_candidate_as_potential( &self, - candidate: HypotheticalCandidate, - candidate_storage: &CandidateStorage, - ) -> bool { + candidate: &impl HypotheticalOrConcreteCandidate, + ) -> Result<(), Error> { let candidate_hash = candidate.candidate_hash(); - // If we've already used this candidate in the chain - if self.candidates.contains(&candidate_hash) { - return true + if self.best_chain.contains(&candidate_hash) || self.unconnected.contains(&candidate_hash) { + return Err(Error::CandidateAlreadyKnown) } - let can_add_as_potential = self.can_add_candidate_as_potential( - candidate_storage, - &candidate.candidate_hash(), - &candidate.relay_parent(), - candidate.parent_head_data_hash(), - candidate.output_head_data_hash(), - ); + self.check_potential(candidate) + } - if can_add_as_potential == PotentialAddition::None { - return false + /// Try adding a seconded candidate, if the candidate has potential. It will never be added to + /// the chain directly in the seconded state, it will only be part of the unconnected storage. + pub fn try_adding_seconded_candidate( + &mut self, + candidate: &CandidateEntry, + ) -> Result<(), Error> { + if candidate.state == CandidateState::Backed { + return Err(Error::IntroduceBackedCandidate); } - let Some(candidate_relay_parent) = self.scope.ancestor(&candidate.relay_parent()) else { - // can_add_candidate_as_potential already checked for this, but just to be safe. - return false - }; + self.can_add_candidate_as_potential(candidate)?; - let identity_modifications = ConstraintModifications::identity(); - let cumulative_modifications = if let Some(last_candidate) = self.chain.last() { - &last_candidate.cumulative_modifications - } else { - &identity_modifications - }; + // This clone is cheap, as it uses an Arc for the expensive stuff. + // We can't consume the candidate because other fragment chains may use it also. + self.unconnected.add_candidate_entry(candidate.clone())?; - let child_constraints = - match self.scope.base_constraints.apply_modifications(&cumulative_modifications) { - Err(e) => { - gum::debug!( - target: LOG_TARGET, - new_parent_head = ?cumulative_modifications.required_parent, - ?candidate_hash, - err = ?e, - "Failed to apply modifications", - ); - - return false - }, - Ok(c) => c, - }; + Ok(()) + } - let parent_head_hash = candidate.parent_head_data_hash(); - if parent_head_hash == child_constraints.required_parent.hash() { - // We do additional checks for complete candidates. - if let HypotheticalCandidate::Complete { - ref receipt, - ref persisted_validation_data, - .. - } = candidate - { - if Fragment::check_against_constraints( - &candidate_relay_parent, - &child_constraints, - &receipt.commitments, - &receipt.descriptor().validation_code_hash, - persisted_validation_data, - ) - .is_err() - { - gum::debug!( - target: LOG_TARGET, - "Fragment::check_against_constraints() returned error", - ); - return false - } - } + /// Try getting the full head data associated with this hash. + pub fn get_head_data_by_hash(&self, head_data_hash: &Hash) -> Option { + // First, see if this is the head data of the latest included candidate. + let required_parent = &self.scope.base_constraints().required_parent; + if &required_parent.hash() == head_data_hash { + return Some(required_parent.clone()) + } - // If we got this far, it can be added to the chain right now. - true - } else if can_add_as_potential == PotentialAddition::Anyhow { - // Otherwise it is or can be an unconnected candidate, but only if PotentialAddition - // does not force us to only add a connected candidate. - true - } else { - false + // Cheaply check if the head data is in the best backable chain. + let has_head_data_in_chain = self + .best_chain + .by_parent_head + .get(head_data_hash) + .or_else(|| self.best_chain.by_output_head.get(head_data_hash)) + .is_some(); + + if has_head_data_in_chain { + return self.best_chain.chain.iter().find_map(|candidate| { + if &candidate.parent_head_data_hash == head_data_hash { + Some( + candidate + .fragment + .candidate() + .persisted_validation_data + .parent_head + .clone(), + ) + } else if &candidate.output_head_data_hash == head_data_hash { + Some(candidate.fragment.candidate().commitments.head_data.clone()) + } else { + None + } + }); } + + // Lastly, try getting the head data from the unconnected candidates. + self.unconnected.head_data_by_hash(head_data_hash).cloned() } - /// Select `count` candidates after the given `ancestors` which pass - /// the predicate and have not already been backed on chain. + /// Select `count` candidates after the given `ancestors` which can be backed on chain next. /// /// The intention of the `ancestors` is to allow queries on the basis of /// one or more candidates which were previously pending availability becoming /// available or candidates timing out. - pub(crate) fn find_backable_chain( + pub fn find_backable_chain( &self, ancestors: Ancestors, count: u32, - pred: impl Fn(&CandidateHash) -> bool, - ) -> Vec { + ) -> Vec<(CandidateHash, Hash)> { if count == 0 { return vec![] } let base_pos = self.find_ancestor_path(ancestors); - let actual_end_index = std::cmp::min(base_pos + (count as usize), self.chain.len()); + let actual_end_index = + std::cmp::min(base_pos + (count as usize), self.best_chain.chain.len()); let mut res = Vec::with_capacity(actual_end_index - base_pos); - for elem in &self.chain[base_pos..actual_end_index] { - if self.scope.get_pending_availability(&elem.candidate_hash).is_none() && - pred(&elem.candidate_hash) - { - res.push(elem.candidate_hash); + for elem in &self.best_chain.chain[base_pos..actual_end_index] { + // Only supply candidates which are not yet pending availability. `ancestors` should + // have already contained them, but check just in case. + if self.scope.get_pending_availability(&elem.candidate_hash).is_none() { + res.push((elem.candidate_hash, elem.relay_parent())); } else { break } @@ -679,11 +913,11 @@ impl FragmentChain { // Stops when the ancestors are all used or when a node in the chain is not present in the // ancestor set. Returns the index in the chain were the search stopped. fn find_ancestor_path(&self, mut ancestors: Ancestors) -> usize { - if self.chain.is_empty() { + if self.best_chain.chain.is_empty() { return 0; } - for (index, candidate) in self.chain.iter().enumerate() { + for (index, candidate) in self.best_chain.chain.iter().enumerate() { if !ancestors.remove(&candidate.candidate_hash) { return index } @@ -691,16 +925,16 @@ impl FragmentChain { // This means that we found the entire chain in the ancestor set. There won't be anything // left to back. - self.chain.len() + self.best_chain.chain.len() } - // Return the earliest relay parent a new candidate can have in order to be added to the chain. - // This is the relay parent of the last candidate in the chain. + // Return the earliest relay parent a new candidate can have in order to be added to the chain + // right now. This is the relay parent of the last candidate in the chain. // The value returned may not be valid if we want to add a candidate pending availability, which // may have a relay parent which is out of scope. Special handling is needed in that case. // `None` is returned if the candidate's relay parent info cannot be found. fn earliest_relay_parent(&self) -> Option { - if let Some(last_candidate) = self.chain.last() { + if let Some(last_candidate) = self.best_chain.chain.last() { self.scope.ancestor(&last_candidate.relay_parent()).or_else(|| { // if the relay-parent is out of scope _and_ it is in the chain, // it must be a candidate pending availability. @@ -713,152 +947,239 @@ impl FragmentChain { } } - // Checks if this candidate could be added in the future to this chain. - // This assumes that the chain does not already contain this candidate. It may or may not be - // present in the `CandidateStorage`. - // Even if the candidate is a potential candidate, this function will indicate that it can be - // kept only if there's enough room for it. - pub(crate) fn can_add_candidate_as_potential( - &self, - storage: &CandidateStorage, - candidate_hash: &CandidateHash, - relay_parent: &Hash, - parent_head_hash: Hash, - output_head_hash: Option, - ) -> PotentialAddition { - // If we've got enough candidates for the configured depth, no point in adding more. - if self.chain.len() > self.scope.max_depth { - return PotentialAddition::None - } + // Return the earliest relay parent a potential candidate may have for it to ever be added to + // the chain. This is the relay parent of the last candidate pending availability or the + // earliest relay parent in scope. + fn earliest_relay_parent_pending_availability(&self) -> RelayChainBlockInfo { + self.best_chain + .chain + .iter() + .rev() + .find_map(|candidate| { + self.scope + .get_pending_availability(&candidate.candidate_hash) + .map(|c| c.relay_parent.clone()) + }) + .unwrap_or_else(|| self.scope.earliest_relay_parent()) + } - if !self.check_potential(relay_parent, parent_head_hash, output_head_hash) { - return PotentialAddition::None - } + // Populate the unconnected potential candidate storage starting from a previous storage. + fn populate_unconnected_potential_candidates(&mut self, old_storage: CandidateStorage) { + for candidate in old_storage.by_candidate_hash.into_values() { + // Sanity check, all pending availability candidates should be already present in the + // chain. + if self.scope.get_pending_availability(&candidate.candidate_hash).is_some() { + continue + } - let present_in_storage = storage.contains(candidate_hash); + match self.can_add_candidate_as_potential(&candidate) { + Ok(()) => { + let _ = self.unconnected.add_candidate_entry(candidate); + }, + // Swallow these errors as they can legitimately happen when pruning stale + // candidates. + Err(_) => {}, + }; + } + } - let unconnected = self - .find_unconnected_potential_candidates( - storage, - present_in_storage.then_some(candidate_hash), - ) - .len(); + // Check whether a candidate outputting this head data would introduce a cycle or multiple paths + // to the same state. Trivial 0-length cycles are checked in `CandidateEntry::new`. + fn check_cycles_or_invalid_tree(&self, output_head_hash: &Hash) -> Result<(), Error> { + // this should catch a cycle where this candidate would point back to the parent of some + // candidate in the chain. + if self.best_chain.by_parent_head.contains_key(output_head_hash) { + return Err(Error::Cycle) + } - if (self.chain.len() + unconnected) < self.scope.max_depth { - PotentialAddition::Anyhow - } else if (self.chain.len() + unconnected) == self.scope.max_depth { - // If we've only one slot left to fill, it must be filled with a connected candidate. - PotentialAddition::IfConnected - } else { - PotentialAddition::None + // multiple paths to the same state, which can't happen for a chain. + if self.best_chain.by_output_head.contains_key(output_head_hash) { + return Err(Error::MultiplePaths) } + + Ok(()) } - // The candidates which are present in `CandidateStorage`, are not part of this chain but could - // become part of this chain in the future. Capped at the max depth minus the existing chain - // length. - // If `ignore_candidate` is supplied and found in storage, it won't be counted. - pub(crate) fn find_unconnected_potential_candidates( + // Checks the potential of a candidate to be added to the chain now or in the future. + // It works both with concrete candidates for which we have the full PVD and committed receipt, + // but also does some more basic checks for incomplete candidates (before even fetching them). + fn check_potential( &self, - storage: &CandidateStorage, - ignore_candidate: Option<&CandidateHash>, - ) -> Vec { - let mut candidates = vec![]; - for candidate in storage.candidates() { - if let Some(ignore_candidate) = ignore_candidate { - if ignore_candidate == &candidate.candidate_hash { - continue - } - } - // We stop at max_depth + 1 with the search. There's no point in looping further. - if (self.chain.len() + candidates.len()) > self.scope.max_depth { - break - } - if !self.candidates.contains(&candidate.candidate_hash) && - self.check_potential( - &candidate.relay_parent, - candidate.candidate.persisted_validation_data.parent_head.hash(), - Some(candidate.candidate.commitments.head_data.hash()), - ) { - candidates.push(candidate.candidate_hash); + candidate: &impl HypotheticalOrConcreteCandidate, + ) -> Result<(), Error> { + let relay_parent = candidate.relay_parent(); + let parent_head_hash = candidate.parent_head_data_hash(); + + // trivial 0-length cycle. + if let Some(output_head_hash) = candidate.output_head_data_hash() { + if parent_head_hash == output_head_hash { + return Err(Error::ZeroLengthCycle) } } - candidates - } + // Check if the relay parent is in scope. + let Some(relay_parent) = self.scope.ancestor(&relay_parent) else { + return Err(Error::RelayParentNotInScope( + relay_parent, + self.scope.earliest_relay_parent().hash, + )) + }; - // Check if adding a candidate which transitions `parent_head_hash` to `output_head_hash` would - // introduce a fork or a cycle in the parachain. - // `output_head_hash` is optional because we sometimes make this check before retrieving the - // collation. - fn is_fork_or_cycle(&self, parent_head_hash: Hash, output_head_hash: Option) -> bool { - if self.by_parent_head.contains_key(&parent_head_hash) { - // fork. our parent has another child already - return true + // Check if the relay parent moved backwards from the latest candidate pending availability. + let earliest_rp_of_pending_availability = self.earliest_relay_parent_pending_availability(); + if relay_parent.number < earliest_rp_of_pending_availability.number { + return Err(Error::RelayParentPrecedesCandidatePendingAvailability( + relay_parent.hash, + earliest_rp_of_pending_availability.hash, + )) } - if let Some(output_head_hash) = output_head_hash { - if self.by_output_head.contains_key(&output_head_hash) { - // this is not a chain, there are multiple paths to the same state. - return true + // If it's a fork with a backed candidate in the current chain. + if let Some(other_candidate) = self.best_chain.by_parent_head.get(&parent_head_hash) { + if self.scope().get_pending_availability(other_candidate).is_some() { + // Cannot accept a fork with a candidate pending availability. + return Err(Error::ForkWithCandidatePendingAvailability(*other_candidate)) } - // trivial 0-length cycle. - if parent_head_hash == output_head_hash { - return true - } - - // this should catch any other cycles. our output state cannot already be the parent - // state of another candidate, unless this is a cycle, since the already added - // candidates form a chain. - if self.by_parent_head.contains_key(&output_head_hash) { - return true + // If the candidate is backed and in the current chain, accept only a candidate + // according to the fork selection rule. + if fork_selection_rule(other_candidate, &candidate.candidate_hash()) == Ordering::Less { + return Err(Error::ForkChoiceRule(*other_candidate)) } } - false - } + // Try seeing if the parent candidate is in the current chain or if it is the latest + // included candidate. If so, get the constraints the candidate must satisfy. + let (constraints, maybe_min_relay_parent_number) = + if let Some(parent_candidate) = self.best_chain.by_output_head.get(&parent_head_hash) { + let Some(parent_candidate) = + self.best_chain.chain.iter().find(|c| &c.candidate_hash == parent_candidate) + else { + // Should never really happen. + return Err(Error::ParentCandidateNotFound) + }; - // Checks the potential of a candidate to be added to the chain in the future. - // Verifies that the relay parent is in scope and not moving backwards and that we're not - // introducing forks or cycles with other candidates in the chain. - // `output_head_hash` is optional because we sometimes make this check before retrieving the - // collation. - fn check_potential( - &self, - relay_parent: &Hash, - parent_head_hash: Hash, - output_head_hash: Option, - ) -> bool { - if self.is_fork_or_cycle(parent_head_hash, output_head_hash) { - return false + ( + self.scope + .base_constraints + .apply_modifications(&parent_candidate.cumulative_modifications) + .map_err(Error::ComputeConstraints)?, + self.scope.ancestor(&parent_candidate.relay_parent()).map(|rp| rp.number), + ) + } else if self.scope.base_constraints.required_parent.hash() == parent_head_hash { + // It builds on the latest included candidate. + (self.scope.base_constraints.clone(), None) + } else { + // If the parent is not yet part of the chain, there's nothing else we can check for + // now. + return Ok(()) + }; + + // Check for cycles or invalid tree transitions. + if let Some(ref output_head_hash) = candidate.output_head_data_hash() { + self.check_cycles_or_invalid_tree(output_head_hash)?; } - let Some(earliest_rp) = self.earliest_relay_parent() else { return false }; + // Check against constraints if we have a full concrete candidate. + if let (Some(commitments), Some(pvd), Some(validation_code_hash)) = ( + candidate.commitments(), + candidate.persisted_validation_data(), + candidate.validation_code_hash(), + ) { + Fragment::check_against_constraints( + &relay_parent, + &constraints, + commitments, + validation_code_hash, + pvd, + ) + .map_err(Error::CheckAgainstConstraints)?; + } - let Some(relay_parent) = self.scope.ancestor(relay_parent) else { return false }; + if relay_parent.number < constraints.min_relay_parent_number { + return Err(Error::RelayParentMovedBackwards) + } - if relay_parent.number < earliest_rp.number { - return false // relay parent moved backwards. + if let Some(earliest_rp) = maybe_min_relay_parent_number { + if relay_parent.number < earliest_rp { + return Err(Error::RelayParentMovedBackwards) + } } - true + Ok(()) } - // Populate the fragment chain with candidates from CandidateStorage. - // Can be called by the constructor or when introducing a new candidate. - // If we're introducing a new candidate onto an existing chain, we may introduce more than one, - // since we may connect already existing candidates to the chain. - fn populate_chain(&mut self, storage: &CandidateStorage) { - let mut cumulative_modifications = if let Some(last_candidate) = self.chain.last() { - last_candidate.cumulative_modifications.clone() + // Once the backable chain was populated, trim the forks generated by candidates which + // are not present in the best chain. Fan this out into a full breadth-first search. + // If `starting_point` is `Some()`, start the search from the candidates having this parent head + // hash. + fn trim_uneligible_forks(&self, storage: &mut CandidateStorage, starting_point: Option) { + // Start out with the candidates in the chain. They are all valid candidates. + let mut queue: VecDeque<_> = if let Some(starting_point) = starting_point { + [(starting_point, true)].into_iter().collect() } else { - ConstraintModifications::identity() + if self.best_chain.chain.is_empty() { + [(self.scope.base_constraints.required_parent.hash(), true)] + .into_iter() + .collect() + } else { + self.best_chain.chain.iter().map(|c| (c.parent_head_data_hash, true)).collect() + } }; + // To make sure that cycles don't make us loop forever, keep track of the visited parent + // heads. + let mut visited = HashSet::new(); + + while let Some((parent, parent_has_potential)) = queue.pop_front() { + visited.insert(parent); + + let Some(children) = storage.by_parent_head.get(&parent) else { continue }; + // Cannot remove while iterating so store them here temporarily. + let mut to_remove = vec![]; + + for child_hash in children.iter() { + let Some(child) = storage.by_candidate_hash.get(child_hash) else { continue }; + + // Already visited this parent. Either is a cycle or multiple paths that lead to the + // same candidate. Either way, stop this branch to avoid looping forever. + if visited.contains(&child.output_head_data_hash) { + continue + } + + // Only keep a candidate if its full ancestry was already kept as potential and this + // candidate itself has potential. + if parent_has_potential && self.check_potential(child).is_ok() { + queue.push_back((child.output_head_data_hash, true)); + } else { + // Otherwise, remove this candidate and continue looping for its children, but + // mark the parent's potential as `false`. We only want to remove its + // children. + to_remove.push(*child_hash); + queue.push_back((child.output_head_data_hash, false)); + } + } + + for hash in to_remove { + storage.remove_candidate(&hash); + } + } + } + + // Populate the fragment chain with candidates from the supplied `CandidateStorage`. + // Can be called by the constructor or when backing a new candidate. + // When this is called, it may cause the previous chain to be completely erased or it may add + // more than one candidate. + fn populate_chain(&mut self, storage: &mut CandidateStorage) { + let mut cumulative_modifications = + if let Some(last_candidate) = self.best_chain.chain.last() { + last_candidate.cumulative_modifications.clone() + } else { + ConstraintModifications::identity() + }; let Some(mut earliest_rp) = self.earliest_relay_parent() else { return }; loop { - if self.chain.len() > self.scope.max_depth { + if self.best_chain.chain.len() > self.scope.max_depth { break; } @@ -878,113 +1199,157 @@ impl FragmentChain { }; let required_head_hash = child_constraints.required_parent.hash(); - // Even though we don't allow parachain forks under the same active leaf, they may still - // appear under different relay chain forks, hence the iterator below. - let possible_children = storage.possible_para_children(&required_head_hash); - let mut added_child = false; - for candidate in possible_children { - // Add one node to chain if - // 1. it does not introduce a fork or a cycle. - // 2. parent hash is correct. - // 3. relay-parent does not move backwards. - // 4. all non-pending-availability candidates have relay-parent in scope. - // 5. candidate outputs fulfill constraints - - if self.is_fork_or_cycle( - candidate.parent_head_data_hash(), - Some(candidate.output_head_data_hash()), - ) { - continue - } - let pending = self.scope.get_pending_availability(&candidate.candidate_hash); - let Some(relay_parent) = pending - .map(|p| p.relay_parent.clone()) - .or_else(|| self.scope.ancestor(&candidate.relay_parent)) - else { - continue - }; - - // require: candidates don't move backwards - // and only pending availability candidates can be out-of-scope. - // - // earliest_rp can be before the earliest relay parent in the scope - // when the parent is a pending availability candidate as well, but - // only other pending candidates can have a relay parent out of scope. - let min_relay_parent_number = pending - .map(|p| match self.chain.len() { - 0 => p.relay_parent.number, - _ => earliest_rp.number, - }) - .unwrap_or_else(|| earliest_rp.number); - - if relay_parent.number < min_relay_parent_number { - continue // relay parent moved backwards. - } + // Select the few possible backed/backable children which can be added to the chain + // right now. + let possible_children = storage + .possible_backed_para_children(&required_head_hash) + .filter_map(|candidate| { + // Only select a candidate if: + // 1. it does not introduce a fork or a cycle. + // 2. parent hash is correct. + // 3. relay-parent does not move backwards. + // 4. all non-pending-availability candidates have relay-parent in scope. + // 5. candidate outputs fulfill constraints + + let pending = self.scope.get_pending_availability(&candidate.candidate_hash); + let Some(relay_parent) = pending + .map(|p| p.relay_parent.clone()) + .or_else(|| self.scope.ancestor(&candidate.relay_parent)) + else { + return None + }; + + if self.check_cycles_or_invalid_tree(&candidate.output_head_data_hash).is_err() + { + return None + } - // don't add candidates if they're already present in the chain. - // this can never happen, as candidates can only be duplicated if there's a cycle - // and we shouldn't have allowed for a cycle to be chained. - if self.contains_candidate(&candidate.candidate_hash) { - continue - } + // require: candidates don't move backwards + // and only pending availability candidates can be out-of-scope. + // + // earliest_rp can be before the earliest relay parent in the scope + // when the parent is a pending availability candidate as well, but + // only other pending candidates can have a relay parent out of scope. + let min_relay_parent_number = pending + .map(|p| match self.best_chain.chain.len() { + 0 => p.relay_parent.number, + _ => earliest_rp.number, + }) + .unwrap_or_else(|| earliest_rp.number); + + if relay_parent.number < min_relay_parent_number { + return None // relay parent moved backwards. + } - let fragment = { - let mut constraints = child_constraints.clone(); - if let Some(ref p) = pending { - // overwrite for candidates pending availability as a special-case. - constraints.min_relay_parent_number = p.relay_parent.number; + // don't add candidates if they're already present in the chain. + // this can never happen, as candidates can only be duplicated if there's a + // cycle and we shouldn't have allowed for a cycle to be chained. + if self.best_chain.contains(&candidate.candidate_hash) { + return None } - let f = Fragment::new( - relay_parent.clone(), - constraints, - // It's cheap to clone because it's wrapped in an Arc - candidate.candidate.clone(), - ); - - match f { - Ok(f) => f, - Err(e) => { - gum::debug!( - target: LOG_TARGET, - err = ?e, - ?relay_parent, - candidate_hash = ?candidate.candidate_hash, - "Failed to instantiate fragment", - ); - - break - }, + let fragment = { + let mut constraints = child_constraints.clone(); + if let Some(ref p) = pending { + // overwrite for candidates pending availability as a special-case. + constraints.min_relay_parent_number = p.relay_parent.number; + } + + let f = Fragment::new( + relay_parent.clone(), + constraints, + // It's cheap to clone because it's wrapped in an Arc + candidate.candidate.clone(), + ); + + match f { + Ok(f) => f, + Err(e) => { + gum::debug!( + target: LOG_TARGET, + err = ?e, + ?relay_parent, + candidate_hash = ?candidate.candidate_hash, + "Failed to instantiate fragment", + ); + + return None + }, + } + }; + + Some(( + fragment, + candidate.candidate_hash, + candidate.output_head_data_hash, + candidate.parent_head_data_hash, + )) + }); + + // Choose the best candidate. + let best_candidate = + possible_children.min_by(|(_, ref child1, _, _), (_, ref child2, _, _)| { + // Always pick a candidate pending availability as best. + if self.scope.get_pending_availability(child1).is_some() { + Ordering::Less + } else if self.scope.get_pending_availability(child2).is_some() { + Ordering::Greater + } else { + // Otherwise, use the fork selection rule. + fork_selection_rule(child1, child2) } - }; + }); + + if let Some((fragment, candidate_hash, output_head_data_hash, parent_head_data_hash)) = + best_candidate + { + // Remove the candidate from storage. + storage.remove_candidate(&candidate_hash); // Update the cumulative constraint modifications. cumulative_modifications.stack(fragment.constraint_modifications()); // Update the earliest rp - earliest_rp = relay_parent; + earliest_rp = fragment.relay_parent().clone(); let node = FragmentNode { fragment, - candidate_hash: candidate.candidate_hash, + candidate_hash, + parent_head_data_hash, + output_head_data_hash, cumulative_modifications: cumulative_modifications.clone(), }; - self.chain.push(node); - self.candidates.insert(candidate.candidate_hash); - // We've already checked for forks and cycles. - self.by_parent_head - .insert(candidate.parent_head_data_hash(), candidate.candidate_hash); - self.by_output_head - .insert(candidate.output_head_data_hash(), candidate.candidate_hash); - added_child = true; - // We can only add one child for a candidate. (it's a chain, not a tree) - break; - } - - if !added_child { + // Add the candidate to the chain now. + self.best_chain.push(node); + } else { break } } } + + // Revert the best backable chain so that the last candidate will be one outputting the given + // `parent_head_hash`. If the `parent_head_hash` is exactly the required parent of the base + // constraints (builds on the latest included candidate), revert the entire chain. + // Return false if we couldn't find the parent head hash. + fn revert_to(&mut self, parent_head_hash: &Hash) -> bool { + let mut removed_items = None; + if &self.scope.base_constraints.required_parent.hash() == parent_head_hash { + removed_items = Some(self.best_chain.clear()); + } + + if removed_items.is_none() && self.best_chain.by_output_head.contains_key(parent_head_hash) + { + removed_items = Some(self.best_chain.revert_to_parent_hash(parent_head_hash).collect()); + } + + let Some(removed_items) = removed_items else { return false }; + + // Even if it's empty, we need to return true, because we'll be able to add a new candidate + // to the chain. + for node in &removed_items { + let _ = self.unconnected.add_candidate_entry(node.into()); + } + true + } } 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 26ee94d59d8e..9886d19e5224 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs @@ -17,8 +17,12 @@ use super::*; use assert_matches::assert_matches; use polkadot_node_subsystem_util::inclusion_emulator::InboundHrmpLimitations; -use polkadot_primitives::{BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData}; +use polkadot_primitives::{ + BlockNumber, CandidateCommitments, CandidateDescriptor, HeadData, Id as ParaId, +}; use polkadot_primitives_test_helpers as test_helpers; +use rand::{seq::SliceRandom, thread_rng}; +use std::ops::Range; fn make_constraints( min_relay_parent_number: BlockNumber, @@ -54,7 +58,7 @@ fn make_committed_candidate( let persisted_validation_data = PersistedValidationData { parent_head, relay_parent_number, - relay_parent_storage_root: Hash::repeat_byte(69), + relay_parent_storage_root: Hash::zero(), max_pov_size: 1_000_000, }; @@ -83,9 +87,20 @@ fn make_committed_candidate( (persisted_validation_data, candidate) } +fn populate_chain_from_previous_storage( + scope: &Scope, + storage: &CandidateStorage, +) -> FragmentChain { + let mut chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + let mut prev_chain = chain.clone(); + prev_chain.unconnected = storage.clone(); + + chain.populate_from_previous(&prev_chain); + chain +} + #[test] fn scope_rejects_ancestors_that_skip_blocks() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 10, hash: Hash::repeat_byte(10), @@ -104,7 +119,6 @@ fn scope_rejects_ancestors_that_skip_blocks() { assert_matches!( Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -117,7 +131,6 @@ fn scope_rejects_ancestors_that_skip_blocks() { #[test] fn scope_rejects_ancestor_for_0_block() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 0, hash: Hash::repeat_byte(0), @@ -136,7 +149,6 @@ fn scope_rejects_ancestor_for_0_block() { assert_matches!( Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -149,7 +161,6 @@ fn scope_rejects_ancestor_for_0_block() { #[test] fn scope_only_takes_ancestors_up_to_min() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 5, hash: Hash::repeat_byte(0), @@ -179,7 +190,6 @@ fn scope_only_takes_ancestors_up_to_min() { let pending_availability = Vec::new(); let scope = Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -194,7 +204,6 @@ fn scope_only_takes_ancestors_up_to_min() { #[test] fn scope_rejects_unordered_ancestors() { - let para_id = ParaId::from(5u32); let relay_parent = RelayChainBlockInfo { number: 5, hash: Hash::repeat_byte(0), @@ -225,7 +234,6 @@ fn scope_rejects_unordered_ancestors() { assert_matches!( Scope::with_ancestors( - para_id, relay_parent, base_constraints, pending_availability, @@ -257,718 +265,695 @@ fn candidate_storage_methods() { let mut wrong_pvd = pvd.clone(); wrong_pvd.max_pov_size = 0; assert_matches!( - storage.add_candidate(candidate.clone(), wrong_pvd, CandidateState::Seconded), - Err(CandidateStorageInsertionError::PersistedValidationDataMismatch) + CandidateEntry::new( + candidate_hash, + candidate.clone(), + wrong_pvd.clone(), + CandidateState::Seconded + ), + Err(CandidateEntryError::PersistedValidationDataMismatch) + ); + assert_matches!( + CandidateEntry::new_seconded(candidate_hash, candidate.clone(), wrong_pvd), + Err(CandidateEntryError::PersistedValidationDataMismatch) ); + // Zero-length cycle. + { + let mut candidate = candidate.clone(); + 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(); + assert_matches!( + CandidateEntry::new_seconded(candidate_hash, candidate, pvd), + Err(CandidateEntryError::ZeroLengthCycle) + ); + } assert!(!storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), None); + 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(&parent_head_hash), None); - assert_eq!(storage.is_backed(&candidate_hash), false); - // Add a valid candidate - storage - .add_candidate(candidate.clone(), pvd.clone(), CandidateState::Seconded) - .unwrap(); + // Add a valid candidate. + let candidate_entry = CandidateEntry::new( + candidate_hash, + candidate.clone(), + pvd.clone(), + CandidateState::Seconded, + ) + .unwrap(); + storage.add_candidate_entry(candidate_entry.clone()).unwrap(); assert!(storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 1); - assert_eq!(storage.possible_para_children(&candidate.descriptor.para_head).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), Some(relay_parent)); + 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.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); - assert_eq!(storage.is_backed(&candidate_hash), false); + // Now mark it as backed + storage.mark_backed(&candidate_hash); + // Marking it twice is fine. storage.mark_backed(&candidate_hash); - assert_eq!(storage.is_backed(&candidate_hash), true); + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + vec![candidate_hash] + ); + assert_eq!(storage.possible_backed_para_children(&candidate.descriptor.para_head).count(), 0); // Re-adding a candidate fails. assert_matches!( - storage.add_candidate(candidate.clone(), pvd.clone(), CandidateState::Seconded), - Err(CandidateStorageInsertionError::CandidateAlreadyKnown(hash)) if candidate_hash == hash + storage.add_candidate_entry(candidate_entry), + Err(Error::CandidateAlreadyKnown) ); // Remove candidate and re-add it later in backed state. storage.remove_candidate(&candidate_hash); assert!(!storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), None); + + // Removing it twice is fine. + 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(&parent_head_hash), None); - assert_eq!(storage.is_backed(&candidate_hash), false); storage - .add_candidate(candidate.clone(), pvd.clone(), CandidateState::Backed) + .add_pending_availability_candidate(candidate_hash, candidate.clone(), pvd) .unwrap(); - assert_eq!(storage.is_backed(&candidate_hash), true); - - // Test retain - storage.retain(|_| true); assert!(storage.contains(&candidate_hash)); - storage.retain(|_| false); - assert!(!storage.contains(&candidate_hash)); - assert_eq!(storage.possible_para_children(&parent_head_hash).count(), 0); - assert_eq!(storage.relay_parent_of_candidate(&candidate_hash), None); - assert_eq!(storage.head_data_by_hash(&candidate.descriptor.para_head), None); - assert_eq!(storage.head_data_by_hash(&parent_head_hash), None); - assert_eq!(storage.is_backed(&candidate_hash), false); + + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + vec![candidate_hash] + ); + 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( + ParaId::from(5u32), + relay_parent, + 8, + vec![4, 5, 6].into(), + vec![2, 3, 4].into(), + 7, + ); + let candidate_hash_2 = candidate_2.hash(); + let candidate_entry_2 = + CandidateEntry::new_seconded(candidate_hash_2, candidate_2, pvd_2).unwrap(); + + storage.add_candidate_entry(candidate_entry_2).unwrap(); + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + vec![candidate_hash] + ); + + // Now mark it as backed. + storage.mark_backed(&candidate_hash_2); + assert_eq!( + storage + .possible_backed_para_children(&parent_head_hash) + .map(|c| c.candidate_hash) + .collect::>(), + [candidate_hash, candidate_hash_2].into_iter().collect() + ); } #[test] -fn populate_and_extend_from_storage_empty() { +fn init_and_populate_from_empty() { // Empty chain and empty storage. - let storage = CandidateStorage::default(); let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); let scope = Scope::with_ancestors( - ParaId::from(2), RelayChainBlockInfo { number: 1, hash: Hash::repeat_byte(1), storage_root: Hash::repeat_byte(2), }, base_constraints, - pending_availability, + Vec::new(), 4, vec![], ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert_eq!(chain.best_chain_len(), 0); + assert_eq!(chain.unconnected_len(), 0); + + let mut new_chain = FragmentChain::init(scope, CandidateStorage::default()); + new_chain.populate_from_previous(&chain); + assert_eq!(chain.best_chain_len(), 0); + assert_eq!(chain.unconnected_len(), 0); } #[test] -fn populate_and_extend_from_storage_with_existing_empty_to_vec() { +fn test_populate_and_check_potential() { let mut storage = CandidateStorage::default(); let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - let relay_parent_b = Hash::repeat_byte(2); - let relay_parent_c = Hash::repeat_byte(3); + let relay_parent_x = Hash::repeat_byte(1); + let relay_parent_y = Hash::repeat_byte(2); + let relay_parent_z = Hash::repeat_byte(3); + let relay_parent_x_info = + RelayChainBlockInfo { number: 0, hash: relay_parent_x, storage_root: Hash::zero() }; + let relay_parent_y_info = + RelayChainBlockInfo { number: 1, hash: relay_parent_y, storage_root: Hash::zero() }; + let relay_parent_z_info = + RelayChainBlockInfo { number: 2, hash: relay_parent_z, storage_root: Hash::zero() }; + + let ancestors = vec![ + // These need to be ordered in reverse. + relay_parent_y_info.clone(), + relay_parent_x_info.clone(), + ]; + + let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); + // Candidates A -> B -> C. They are all backed let (pvd_a, candidate_a) = make_committed_candidate( para_id, - relay_parent_a, - 0, + relay_parent_x_info.hash, + relay_parent_x_info.number, vec![0x0a].into(), vec![0x0b].into(), - 0, + relay_parent_x_info.number, ); let candidate_a_hash = candidate_a.hash(); - + let candidate_a_entry = + CandidateEntry::new(candidate_a_hash, candidate_a, pvd_a.clone(), CandidateState::Backed) + .unwrap(); + storage.add_candidate_entry(candidate_a_entry.clone()).unwrap(); let (pvd_b, candidate_b) = make_committed_candidate( para_id, - relay_parent_b, - 1, + relay_parent_y_info.hash, + relay_parent_y_info.number, vec![0x0b].into(), vec![0x0c].into(), - 1, + relay_parent_y_info.number, ); let candidate_b_hash = candidate_b.hash(); - + let candidate_b_entry = + CandidateEntry::new(candidate_b_hash, candidate_b, pvd_b, CandidateState::Backed).unwrap(); + storage.add_candidate_entry(candidate_b_entry.clone()).unwrap(); let (pvd_c, candidate_c) = make_committed_candidate( para_id, - relay_parent_c, - 2, + relay_parent_z_info.hash, + relay_parent_z_info.number, vec![0x0c].into(), vec![0x0d].into(), - 2, + relay_parent_z_info.number, ); let candidate_c_hash = candidate_c.hash(); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - let relay_parent_b_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number, - hash: relay_parent_b, - storage_root: pvd_b.relay_parent_storage_root, - }; - let relay_parent_c_info = RelayChainBlockInfo { - number: pvd_c.relay_parent_number, - hash: relay_parent_c, - storage_root: pvd_c.relay_parent_storage_root, - }; - - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); - - let ancestors = vec![ - // These need to be ordered in reverse. - relay_parent_b_info.clone(), - relay_parent_a_info.clone(), - ]; - - storage - .add_candidate(candidate_a.clone(), pvd_a.clone(), CandidateState::Seconded) - .unwrap(); - storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Backed) - .unwrap(); - storage - .add_candidate(candidate_c.clone(), pvd_c.clone(), CandidateState::Backed) - .unwrap(); + let candidate_c_entry = + CandidateEntry::new(candidate_c_hash, candidate_c, pvd_c, CandidateState::Backed).unwrap(); + storage.add_candidate_entry(candidate_c_entry.clone()).unwrap(); // Candidate A doesn't adhere to the base constraints. { for wrong_constraints in [ // Different required parent - make_constraints(0, vec![0], vec![0x0e].into()), + make_constraints( + relay_parent_x_info.number, + vec![relay_parent_x_info.number], + vec![0x0e].into(), + ), // Min relay parent number is wrong - make_constraints(1, vec![0], vec![0x0a].into()), + make_constraints(relay_parent_y_info.number, vec![0], vec![0x0a].into()), ] { let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), wrong_constraints.clone(), - pending_availability.clone(), + vec![], 4, ancestors.clone(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); + let chain = populate_chain_from_previous_storage(&scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); + assert!(chain.best_chain_vec().is_empty()); // If the min relay parent number is wrong, candidate A can never become valid. // Otherwise, if only the required parent doesn't match, candidate A is still a // potential candidate. - if wrong_constraints.min_relay_parent_number == 1 { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate_a.hash(), - &candidate_a.descriptor.relay_parent, - pvd_a.parent_head.hash(), - Some(candidate_a.commitments.head_data.hash()), - ), - PotentialAddition::None + if wrong_constraints.min_relay_parent_number == relay_parent_y_info.number { + // If A is not a potential candidate, its descendants will also not be added. + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::RelayParentNotInScope(_, _)) ); + // However, if taken independently, both B and C still have potential, since we + // don't know that A doesn't. + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); } else { assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate_a.hash(), - &candidate_a.descriptor.relay_parent, - pvd_a.parent_head.hash(), - Some(candidate_a.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow - ); - } - - // All other candidates can always be potential candidates. - for (candidate, pvd) in - [(candidate_b.clone(), pvd_b.clone()), (candidate_c.clone(), pvd_c.clone())] - { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_a_hash, candidate_b_hash, candidate_c_hash].into_iter().collect() ); } } } - // Various max depths. + // Various depths { - // depth is 0, will only allow 1 candidate + // Depth is 0, only allows one candidate, but the others will be kept as potential. let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 0, ancestors.clone(), ) .unwrap(); - // Before populating the chain, all candidates are potential candidates. However, they can - // only be added as connected candidates, because only one candidates is allowed by max - // depth - let chain = FragmentChain::populate(scope.clone(), &CandidateStorage::default()); - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &CandidateStorage::default(), - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::IfConnected - ); - } - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash]); - // since depth is maxed out, we can't add more potential candidates - // candidate A is no longer a potential candidate because it's already present. - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&candidate_a_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); + + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_b_hash, candidate_c_hash].into_iter().collect() + ); // depth is 1, allows two candidates let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 1, ancestors.clone(), ) .unwrap(); - // Before populating the chain, all candidates can be added as potential. - let mut modified_storage = CandidateStorage::default(); - let chain = FragmentChain::populate(scope.clone(), &modified_storage); - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow - ); - } - // Add an unconnected candidate. We now should only allow a Connected candidate, because max - // depth only allows one more candidate. - modified_storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Seconded) - .unwrap(); - let chain = FragmentChain::populate(scope.clone(), &modified_storage); - for (candidate, pvd) in - [(candidate_a.clone(), pvd_a.clone()), (candidate_c.clone(), pvd_c.clone())] - { - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::IfConnected - ); - } + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&candidate_a_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); - // Now try populating from all candidates. - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // since depth is maxed out, we can't add more potential candidates - // candidate A and B are no longer a potential candidate because they're already present. - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_c_hash].into_iter().collect() + ); - // depths larger than 2, allows all candidates + // depth is larger than 2, allows all three candidates for depth in 2..6 { let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], depth, ancestors.clone(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - // Candidates are no longer potential candidates because they're already part of the - // chain. - for (candidate, pvd) in [ - (candidate_a.clone(), pvd_a.clone()), - (candidate_b.clone(), pvd_b.clone()), - (candidate_c.clone(), pvd_c.clone()), - ] { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&candidate_a_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); + + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!( + chain.best_chain_vec(), + vec![candidate_a_hash, candidate_b_hash, candidate_c_hash] + ); + assert_eq!(chain.unconnected_len(), 0); } } - // Wrong relay parents + // Relay parents out of scope { - // Candidates A has relay parent out of scope. - let ancestors_without_a = vec![relay_parent_b_info.clone()]; + // Candidate A has relay parent out of scope. Candidates B and C will also be deleted since + // they form a chain with A. + let ancestors_without_x = vec![relay_parent_y_info.clone()]; let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 4, - ancestors_without_a, + ancestors_without_x, ) .unwrap(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert!(chain.best_chain_vec().is_empty()); + assert_eq!(chain.unconnected_len(), 0); - let mut chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); - - // Candidate A is not a potential candidate, but candidates B and C still are. - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate_a.hash(), - &candidate_a.descriptor.relay_parent, - pvd_a.parent_head.hash(), - Some(candidate_a.commitments.head_data.hash()), - ), - PotentialAddition::None + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::RelayParentNotInScope(_, _)) ); - for (candidate, pvd) in - [(candidate_b.clone(), pvd_b.clone()), (candidate_c.clone(), pvd_c.clone())] - { - assert_eq!( - chain.can_add_candidate_as_potential( - &storage, - &candidate.hash(), - &candidate.descriptor.relay_parent, - pvd.parent_head.hash(), - Some(candidate.commitments.head_data.hash()), - ), - PotentialAddition::Anyhow - ); - } + // However, if taken independently, both B and C still have potential, since we + // don't know that A doesn't. + assert!(chain.can_add_candidate_as_potential(&candidate_b_entry).is_ok()); + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); - // Candidate C has the same relay parent as candidate A's parent. Relay parent not allowed - // to move backwards - let mut modified_storage = storage.clone(); - modified_storage.remove_candidate(&candidate_c_hash); - let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( - para_id, - relay_parent_a, - 1, - vec![0x0c].into(), - vec![0x0d].into(), - 2, - ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); + // Candidates A and B have relay parents out of scope. Candidate C will also be deleted + // since it forms a chain with A and B. let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 4, - ancestors.clone(), + vec![], ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); + let chain = populate_chain_from_previous_storage(&scope, &storage); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None + assert!(chain.best_chain_vec().is_empty()); + assert_eq!(chain.unconnected_len(), 0); + + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::RelayParentNotInScope(_, _)) + ); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_b_entry), + Err(Error::RelayParentNotInScope(_, _)) ); + // However, if taken independently, C still has potential, since we + // don't know that A and B don't + assert!(chain.can_add_candidate_as_potential(&candidate_c_entry).is_ok()); } - // Parachain fork and cycles are not allowed. + // Parachain cycle is not allowed. Make C have the same parent as A. { - // Candidate C has the same parent as candidate B. - let mut modified_storage = storage.clone(); - modified_storage.remove_candidate(&candidate_c_hash); - let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( - para_id, - relay_parent_c, - 2, - vec![0x0b].into(), - vec![0x0d].into(), - 2, - ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - // We'll either have A->B or A->C. It's not deterministic because CandidateStorage uses - // HashSets and HashMaps. - if chain.to_vec() == vec![candidate_a_hash, candidate_b_hash] { - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } else if chain.to_vec() == vec![candidate_a_hash, wrong_candidate_c.hash()] { - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, wrong_candidate_c.hash()]); - // Candidate B is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &candidate_b.hash(), - &candidate_b.descriptor.relay_parent, - pvd_b.parent_head.hash(), - Some(candidate_b.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - } else { - panic!("Unexpected chain: {:?}", chain.to_vec()); - } - - // Candidate C is a 0-length cycle. - // Candidate C has the same parent as candidate B. let mut modified_storage = storage.clone(); modified_storage.remove_candidate(&candidate_c_hash); let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( para_id, - relay_parent_c, - 2, - vec![0x0c].into(), + relay_parent_z_info.hash, + relay_parent_z_info.number, vec![0x0c].into(), - 2, + vec![0x0a].into(), + relay_parent_z_info.number, ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), + let wrong_candidate_c_entry = CandidateEntry::new( + wrong_candidate_c.hash(), + wrong_candidate_c, + wrong_pvd_c, + CandidateState::Backed, ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None - ); - - // Candidate C points back to the pre-state of candidate C. - let mut modified_storage = storage.clone(); - modified_storage.remove_candidate(&candidate_c_hash); - let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( - para_id, - relay_parent_c, - 2, - vec![0x0c].into(), - vec![0x0b].into(), - 2, - ); - modified_storage - .add_candidate(wrong_candidate_c.clone(), wrong_pvd_c.clone(), CandidateState::Seconded) - .unwrap(); + modified_storage.add_candidate_entry(wrong_candidate_c_entry.clone()).unwrap(); let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + relay_parent_z_info.clone(), base_constraints.clone(), - pending_availability.clone(), + vec![], 4, ancestors.clone(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - chain.extend_from_storage(&modified_storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - // Candidate C is not even a potential candidate. - assert_eq!( - chain.can_add_candidate_as_potential( - &modified_storage, - &wrong_candidate_c.hash(), - &wrong_candidate_c.descriptor.relay_parent, - wrong_pvd_c.parent_head.hash(), - Some(wrong_candidate_c.commitments.head_data.hash()), - ), - PotentialAddition::None + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + + assert_matches!( + chain.can_add_candidate_as_potential(&wrong_candidate_c_entry), + Err(Error::Cycle) ); + // However, if taken independently, C still has potential, since we don't know A and B. + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&wrong_candidate_c_entry).is_ok()); } - // Test with candidates pending availability - { - // Valid options - for pending in [ - vec![PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }], - vec![ - PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }, - PendingAvailability { - candidate_hash: candidate_b_hash, - relay_parent: relay_parent_b_info.clone(), - }, - ], - vec![ - PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }, - PendingAvailability { - candidate_hash: candidate_b_hash, - relay_parent: relay_parent_b_info.clone(), - }, - PendingAvailability { - candidate_hash: candidate_c_hash, - relay_parent: relay_parent_c_info.clone(), - }, - ], - ] { - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - pending, - 3, - ancestors.clone(), - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - } + // Candidate C has the same relay parent as candidate A's parent. Relay parent not allowed + // to move backwards + let mut modified_storage = storage.clone(); + modified_storage.remove_candidate(&candidate_c_hash); + let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0c].into(), + vec![0x0d].into(), + 0, + ); + let wrong_candidate_c_entry = CandidateEntry::new( + wrong_candidate_c.hash(), + wrong_candidate_c, + wrong_pvd_c, + CandidateState::Backed, + ) + .unwrap(); + modified_storage.add_candidate_entry(wrong_candidate_c_entry.clone()).unwrap(); + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 4, + ancestors.clone(), + ) + .unwrap(); - // Relay parents of pending availability candidates can be out of scope - // Relay parent of candidate A is out of scope. - let ancestors_without_a = vec![relay_parent_b_info.clone()]; - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), - base_constraints.clone(), - vec![PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info.clone(), - }], - 4, - ancestors_without_a, - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); - // Even relay parents of pending availability candidates which are out of scope cannot move - // backwards. - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info.clone(), + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&wrong_candidate_c_entry), + Err(Error::RelayParentMovedBackwards) + ); + + // Candidate C is an unconnected candidate. + // C's relay parent is allowed to move backwards from B's relay parent, because C may later on + // trigger a reorg and B may get removed. + let mut modified_storage = storage.clone(); + modified_storage.remove_candidate(&candidate_c_hash); + let (unconnected_pvd_c, unconnected_candidate_c) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0d].into(), + vec![0x0e].into(), + 0, + ); + let unconnected_candidate_c_hash = unconnected_candidate_c.hash(); + let unconnected_candidate_c_entry = CandidateEntry::new( + unconnected_candidate_c_hash, + unconnected_candidate_c, + unconnected_pvd_c, + CandidateState::Backed, + ) + .unwrap(); + modified_storage + .add_candidate_entry(unconnected_candidate_c_entry.clone()) + .unwrap(); + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 4, + ancestors.clone(), + ) + .unwrap(); + let chain = FragmentChain::init(scope.clone(), CandidateStorage::default()); + assert!(chain.can_add_candidate_as_potential(&unconnected_candidate_c_entry).is_ok()); + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [unconnected_candidate_c_hash].into_iter().collect() + ); + + // Candidate A is a pending availability candidate and Candidate C is an unconnected candidate, + // C's relay parent is not allowed to move backwards from A's relay parent because we're sure A + // will not get removed in the future, as it's already on-chain (unless it times out + // availability, a case for which we don't care to optimise for) + + modified_storage.remove_candidate(&candidate_a_hash); + let (modified_pvd_a, modified_candidate_a) = make_committed_candidate( + para_id, + relay_parent_y_info.hash, + relay_parent_y_info.number, + vec![0x0a].into(), + vec![0x0b].into(), + relay_parent_y_info.number, + ); + let modified_candidate_a_hash = modified_candidate_a.hash(); + modified_storage + .add_candidate_entry( + CandidateEntry::new( + modified_candidate_a_hash, + modified_candidate_a, + modified_pvd_a, + CandidateState::Backed, + ) + .unwrap(), + ) + .unwrap(); + + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![PendingAvailability { + candidate_hash: modified_candidate_a_hash, + relay_parent: relay_parent_y_info.clone(), + }], + 4, + ancestors.clone(), + ) + .unwrap(); + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + assert_eq!(chain.best_chain_vec(), vec![modified_candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&unconnected_candidate_c_entry), + Err(Error::RelayParentPrecedesCandidatePendingAvailability(_, _)) + ); + + // Not allowed to fork from a candidate pending availability + let (wrong_pvd_c, wrong_candidate_c) = make_committed_candidate( + para_id, + relay_parent_y_info.hash, + relay_parent_y_info.number, + vec![0x0a].into(), + vec![0x0b2].into(), + 0, + ); + let wrong_candidate_c_hash = wrong_candidate_c.hash(); + let wrong_candidate_c_entry = CandidateEntry::new( + wrong_candidate_c_hash, + wrong_candidate_c, + wrong_pvd_c, + CandidateState::Backed, + ) + .unwrap(); + modified_storage.add_candidate_entry(wrong_candidate_c_entry.clone()).unwrap(); + + // Does not even matter if the fork selection rule would have picked up the new candidate, as + // the other is already pending availability. + assert_eq!( + fork_selection_rule(&wrong_candidate_c_hash, &modified_candidate_a_hash), + Ordering::Less + ); + + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![PendingAvailability { + candidate_hash: modified_candidate_a_hash, + relay_parent: relay_parent_y_info.clone(), + }], + 4, + ancestors.clone(), + ) + .unwrap(); + + let chain = populate_chain_from_previous_storage(&scope, &modified_storage); + assert_eq!(chain.best_chain_vec(), vec![modified_candidate_a_hash, candidate_b_hash]); + assert_eq!(chain.unconnected_len(), 0); + assert_matches!( + chain.can_add_candidate_as_potential(&wrong_candidate_c_entry), + Err(Error::ForkWithCandidatePendingAvailability(_)) + ); + + // Test with candidates pending availability + { + // Valid options + for pending in [ + vec![PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }], + vec![ + PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }, + PendingAvailability { + candidate_hash: candidate_b_hash, + relay_parent: relay_parent_y_info.clone(), + }, + ], + vec![ + PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }, + PendingAvailability { + candidate_hash: candidate_b_hash, + relay_parent: relay_parent_y_info.clone(), + }, + PendingAvailability { + candidate_hash: candidate_c_hash, + relay_parent: relay_parent_z_info.clone(), + }, + ], + ] { + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + pending, + 3, + ancestors.clone(), + ) + .unwrap(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!( + chain.best_chain_vec(), + vec![candidate_a_hash, candidate_b_hash, candidate_c_hash] + ); + assert_eq!(chain.unconnected_len(), 0); + } + + // Relay parents of pending availability candidates can be out of scope + // Relay parent of candidate A is out of scope. + let ancestors_without_x = vec![relay_parent_y_info.clone()]; + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info.clone(), + }], + 4, + ancestors_without_x, + ) + .unwrap(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + + assert_eq!( + chain.best_chain_vec(), + vec![candidate_a_hash, candidate_b_hash, candidate_c_hash] + ); + assert_eq!(chain.unconnected_len(), 0); + + // Even relay parents of pending availability candidates which are out of scope cannot + // move backwards. + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), base_constraints.clone(), vec![ PendingAvailability { candidate_hash: candidate_a_hash, relay_parent: RelayChainBlockInfo { - hash: relay_parent_a_info.hash, + hash: relay_parent_x_info.hash, number: 1, - storage_root: relay_parent_a_info.storage_root, + storage_root: relay_parent_x_info.storage_root, }, }, PendingAvailability { candidate_hash: candidate_b_hash, relay_parent: RelayChainBlockInfo { - hash: relay_parent_b_info.hash, + hash: relay_parent_y_info.hash, number: 0, - storage_root: relay_parent_b_info.storage_root, + storage_root: relay_parent_y_info.storage_root, }, }, ], @@ -976,271 +961,418 @@ fn populate_and_extend_from_storage_with_existing_empty_to_vec() { vec![], ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); - - chain.extend_from_storage(&storage); - assert!(chain.to_vec().is_empty()); + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert!(chain.best_chain_vec().is_empty()); + assert_eq!(chain.unconnected_len(), 0); } -} -#[test] -fn extend_from_storage_with_existing_to_vec() { - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - let relay_parent_b = Hash::repeat_byte(2); - let relay_parent_d = Hash::repeat_byte(3); + // More complex case: + // max_depth is 2 (a chain of max depth 3). + // A -> B -> C are the best backable chain. + // D is backed but would exceed the max depth. + // F is unconnected and seconded. + // A1 has same parent as A, is backed but has a higher candidate hash. It'll therefore be + // deleted. + // A1 has underneath a subtree that will all need to be trimmed. A1 -> B1. B1 -> C1 + // and B1 -> C2. (C1 is backed). + // A2 is seconded but is kept because it has a lower candidate hash than A. + // A2 points to B2, which is backed. + // + // Check that D, F, A2 and B2 are kept as unconnected potential candidates. - let (pvd_a, candidate_a) = make_committed_candidate( + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 2, + ancestors.clone(), + ) + .unwrap(); + + // Candidate D + let (pvd_d, candidate_d) = make_committed_candidate( para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 0, + relay_parent_z_info.hash, + relay_parent_z_info.number, + vec![0x0d].into(), + vec![0x0e].into(), + relay_parent_z_info.number, ); - let candidate_a_hash = candidate_a.hash(); - - let (pvd_b, candidate_b) = make_committed_candidate( + let candidate_d_hash = candidate_d.hash(); + let candidate_d_entry = + CandidateEntry::new(candidate_d_hash, candidate_d, pvd_d, CandidateState::Backed).unwrap(); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_d_entry) + .is_ok()); + storage.add_candidate_entry(candidate_d_entry).unwrap(); + + // Candidate F + let (pvd_f, candidate_f) = make_committed_candidate( para_id, - relay_parent_b, - 1, - vec![0x0b].into(), - vec![0x0c].into(), - 1, + relay_parent_z_info.hash, + relay_parent_z_info.number, + vec![0x0f].into(), + vec![0xf1].into(), + 1000, ); - let candidate_b_hash = candidate_b.hash(); + let candidate_f_hash = candidate_f.hash(); + let candidate_f_entry = + CandidateEntry::new(candidate_f_hash, candidate_f, pvd_f, CandidateState::Seconded) + .unwrap(); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_f_entry) + .is_ok()); + storage.add_candidate_entry(candidate_f_entry.clone()).unwrap(); - let (pvd_c, candidate_c) = make_committed_candidate( + // Candidate A1 + let (pvd_a1, candidate_a1) = make_committed_candidate( para_id, - // Use the same relay parent number as B to test that it doesn't need to change between - // candidates. - relay_parent_b, - 1, - vec![0x0c].into(), - vec![0x0d].into(), - 1, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0a].into(), + vec![0xb1].into(), + relay_parent_x_info.number, ); - let candidate_c_hash = candidate_c.hash(); + let candidate_a1_hash = candidate_a1.hash(); + let candidate_a1_entry = + CandidateEntry::new(candidate_a1_hash, candidate_a1, pvd_a1, CandidateState::Backed) + .unwrap(); + // Candidate A1 is created so that its hash is greater than the candidate A hash. + assert_eq!(fork_selection_rule(&candidate_a_hash, &candidate_a1_hash), Ordering::Less); - // Candidate D will never be added to the chain. - let (pvd_d, candidate_d) = make_committed_candidate( - para_id, - relay_parent_d, - 2, - vec![0x0e].into(), - vec![0x0f].into(), - 1, + assert_matches!( + populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_a1_entry), + Err(Error::ForkChoiceRule(other)) if candidate_a_hash == other ); - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - let relay_parent_b_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number, - hash: relay_parent_b, - storage_root: pvd_b.relay_parent_storage_root, - }; - let relay_parent_d_info = RelayChainBlockInfo { - number: pvd_d.relay_parent_number, - hash: relay_parent_d, - storage_root: pvd_d.relay_parent_storage_root, - }; + storage.add_candidate_entry(candidate_a1_entry.clone()).unwrap(); - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); + // Candidate B1. + let (pvd_b1, candidate_b1) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0xb1].into(), + vec![0xc1].into(), + relay_parent_x_info.number, + ); + let candidate_b1_hash = candidate_b1.hash(); + let candidate_b1_entry = + CandidateEntry::new(candidate_b1_hash, candidate_b1, pvd_b1, CandidateState::Seconded) + .unwrap(); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_b1_entry) + .is_ok()); - let ancestors = vec![ - // These need to be ordered in reverse. - relay_parent_b_info.clone(), - relay_parent_a_info.clone(), - ]; + storage.add_candidate_entry(candidate_b1_entry).unwrap(); - // Already had A and C in the storage. Introduce B, which should add both B and C to the chain - // now. - { - let mut storage = CandidateStorage::default(); - storage - .add_candidate(candidate_a.clone(), pvd_a.clone(), CandidateState::Seconded) + // Candidate C1. + let (pvd_c1, candidate_c1) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0xc1].into(), + vec![0xd1].into(), + relay_parent_x_info.number, + ); + let candidate_c1_hash = candidate_c1.hash(); + let candidate_c1_entry = + CandidateEntry::new(candidate_c1_hash, candidate_c1, pvd_c1, CandidateState::Backed) .unwrap(); - storage - .add_candidate(candidate_c.clone(), pvd_c.clone(), CandidateState::Seconded) + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_c1_entry) + .is_ok()); + + storage.add_candidate_entry(candidate_c1_entry).unwrap(); + + // Candidate C2. + let (pvd_c2, candidate_c2) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0xc1].into(), + vec![0xd2].into(), + relay_parent_x_info.number, + ); + let candidate_c2_hash = candidate_c2.hash(); + let candidate_c2_entry = + CandidateEntry::new(candidate_c2_hash, candidate_c2, pvd_c2, CandidateState::Seconded) .unwrap(); - storage - .add_candidate(candidate_d.clone(), pvd_d.clone(), CandidateState::Seconded) + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_c2_entry) + .is_ok()); + storage.add_candidate_entry(candidate_c2_entry).unwrap(); + + // Candidate A2. + let (pvd_a2, candidate_a2) = make_committed_candidate( + para_id, + relay_parent_x_info.hash, + relay_parent_x_info.number, + vec![0x0a].into(), + vec![0xb3].into(), + relay_parent_x_info.number, + ); + let candidate_a2_hash = candidate_a2.hash(); + let candidate_a2_entry = + CandidateEntry::new(candidate_a2_hash, candidate_a2, pvd_a2, CandidateState::Seconded) .unwrap(); + // Candidate A2 is created so that its hash is greater than the candidate A hash. + assert_eq!(fork_selection_rule(&candidate_a2_hash, &candidate_a_hash), Ordering::Less); - let scope = Scope::with_ancestors( - para_id, - relay_parent_d_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), - ) - .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash]); + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_a2_entry) + .is_ok()); - storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Seconded) + storage.add_candidate_entry(candidate_a2_entry).unwrap(); + + // Candidate B2. + let (pvd_b2, candidate_b2) = make_committed_candidate( + para_id, + relay_parent_y_info.hash, + relay_parent_y_info.number, + vec![0xb3].into(), + vec![0xb4].into(), + relay_parent_y_info.number, + ); + let candidate_b2_hash = candidate_b2.hash(); + let candidate_b2_entry = + CandidateEntry::new(candidate_b2_hash, candidate_b2, pvd_b2, CandidateState::Backed) .unwrap(); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - } + assert!(populate_chain_from_previous_storage(&scope, &storage) + .can_add_candidate_as_potential(&candidate_b2_entry) + .is_ok()); + storage.add_candidate_entry(candidate_b2_entry).unwrap(); - // Already had A and B in the chain. Introduce C. + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_d_hash, candidate_f_hash, candidate_a2_hash, candidate_b2_hash] + .into_iter() + .collect() + ); + // Cannot add as potential an already present candidate (whether it's in the best chain or in + // unconnected storage) + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::CandidateAlreadyKnown) + ); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_f_entry), + Err(Error::CandidateAlreadyKnown) + ); + + // Simulate a best chain reorg by backing a2. { - let mut storage = CandidateStorage::default(); - storage - .add_candidate(candidate_a.clone(), pvd_a.clone(), CandidateState::Seconded) - .unwrap(); - storage - .add_candidate(candidate_b.clone(), pvd_b.clone(), CandidateState::Seconded) - .unwrap(); - storage - .add_candidate(candidate_d.clone(), pvd_d.clone(), CandidateState::Seconded) - .unwrap(); + let mut chain = chain.clone(); + chain.candidate_backed(&candidate_a2_hash); + assert_eq!(chain.best_chain_vec(), vec![candidate_a2_hash, candidate_b2_hash]); + // F is kept as it was truly unconnected. The rest will be trimmed. + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_f_hash].into_iter().collect() + ); - let scope = Scope::with_ancestors( - para_id, - relay_parent_d_info.clone(), - base_constraints.clone(), - pending_availability.clone(), - 4, - ancestors.clone(), + // A and A1 will never have potential again. + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a1_entry), + Err(Error::ForkChoiceRule(_)) + ); + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::ForkChoiceRule(_)) + ); + } + + // Candidate F has an invalid hrmp watermark. however, it was not checked beforehand as we don't + // have its parent yet. Add its parent now. This will not impact anything as E is not yet part + // of the best chain. + + let (pvd_e, candidate_e) = make_committed_candidate( + para_id, + relay_parent_z_info.hash, + relay_parent_z_info.number, + vec![0x0e].into(), + vec![0x0f].into(), + relay_parent_z_info.number, + ); + let candidate_e_hash = candidate_e.hash(); + storage + .add_candidate_entry( + CandidateEntry::new(candidate_e_hash, candidate_e, pvd_e, CandidateState::Seconded) + .unwrap(), ) .unwrap(); - let mut chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - storage - .add_candidate(candidate_c.clone(), pvd_c.clone(), CandidateState::Seconded) - .unwrap(); - chain.extend_from_storage(&storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); - } + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [ + candidate_d_hash, + candidate_f_hash, + candidate_a2_hash, + candidate_b2_hash, + candidate_e_hash + ] + .into_iter() + .collect() + ); + + // Simulate the fact that candidates A, B, C are now pending availability. + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![ + PendingAvailability { + candidate_hash: candidate_a_hash, + relay_parent: relay_parent_x_info, + }, + PendingAvailability { + candidate_hash: candidate_b_hash, + relay_parent: relay_parent_y_info, + }, + PendingAvailability { + candidate_hash: candidate_c_hash, + relay_parent: relay_parent_z_info.clone(), + }, + ], + 2, + ancestors.clone(), + ) + .unwrap(); + + // A2 and B2 will now be trimmed + let chain = populate_chain_from_previous_storage(&scope, &storage); + assert_eq!(chain.best_chain_vec(), vec![candidate_a_hash, candidate_b_hash, candidate_c_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_d_hash, candidate_f_hash, candidate_e_hash].into_iter().collect() + ); + // Cannot add as potential an already pending availability candidate + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_a_entry), + Err(Error::CandidateAlreadyKnown) + ); + + // Simulate the fact that candidates A, B and C have been included. + + let base_constraints = make_constraints(0, vec![0], HeadData(vec![0x0d])); + let scope = Scope::with_ancestors( + relay_parent_z_info.clone(), + base_constraints.clone(), + vec![], + 2, + ancestors.clone(), + ) + .unwrap(); + + let prev_chain = chain; + let mut chain = FragmentChain::init(scope, CandidateStorage::default()); + chain.populate_from_previous(&prev_chain); + assert_eq!(chain.best_chain_vec(), vec![candidate_d_hash]); + assert_eq!( + chain.unconnected().map(|c| c.candidate_hash).collect::>(), + [candidate_e_hash, candidate_f_hash].into_iter().collect() + ); + + // Mark E as backed. F will be dropped for invalid watermark. No other unconnected candidates. + chain.candidate_backed(&candidate_e_hash); + assert_eq!(chain.best_chain_vec(), vec![candidate_d_hash, candidate_e_hash]); + assert_eq!(chain.unconnected_len(), 0); + + assert_matches!( + chain.can_add_candidate_as_potential(&candidate_f_entry), + Err(Error::CheckAgainstConstraints(_)) + ); } #[test] -fn test_find_ancestor_path_and_find_backable_chain_empty_to_vec() { - let para_id = ParaId::from(5u32); +fn test_find_ancestor_path_and_find_backable_chain_empty_best_chain() { let relay_parent = Hash::repeat_byte(1); let required_parent: HeadData = vec![0xff].into(); let max_depth = 10; // Empty chain - let storage = CandidateStorage::default(); let base_constraints = make_constraints(0, vec![0], required_parent.clone()); let relay_parent_info = RelayChainBlockInfo { number: 0, hash: relay_parent, storage_root: Hash::zero() }; - let scope = Scope::with_ancestors( - para_id, - relay_parent_info, - base_constraints, - vec![], - max_depth, - vec![], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - assert!(chain.to_vec().is_empty()); + let scope = + Scope::with_ancestors(relay_parent_info, base_constraints, vec![], max_depth, vec![]) + .unwrap(); + let chain = FragmentChain::init(scope, CandidateStorage::default()); + assert_eq!(chain.best_chain_len(), 0); assert_eq!(chain.find_ancestor_path(Ancestors::new()), 0); - assert_eq!(chain.find_backable_chain(Ancestors::new(), 2, |_| true), vec![]); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 2), vec![]); // Invalid candidate. let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!(chain.find_backable_chain(ancestors, 2, |_| true), vec![]); + assert_eq!(chain.find_backable_chain(ancestors, 2), vec![]); } #[test] -fn test_find_ancestor_path_and_find_backable_to_vec() { +fn test_find_ancestor_path_and_find_backable_chain() { let para_id = ParaId::from(5u32); let relay_parent = Hash::repeat_byte(1); let required_parent: HeadData = vec![0xff].into(); let max_depth = 5; let relay_parent_number = 0; - let relay_parent_storage_root = Hash::repeat_byte(69); + let relay_parent_storage_root = Hash::zero(); let mut candidates = vec![]; - - // Candidate 0 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - required_parent.clone(), - vec![0].into(), - 0, - )); - // Candidate 1 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![0].into(), - vec![1].into(), - 0, - )); - // Candidate 2 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![1].into(), - vec![2].into(), - 0, - )); - // Candidate 3 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![2].into(), - vec![3].into(), - 0, - )); - // Candidate 4 - candidates.push(make_committed_candidate( - para_id, - relay_parent, - 0, - vec![3].into(), - vec![4].into(), - 0, - )); - // Candidate 5 + + // Candidate 0 candidates.push(make_committed_candidate( para_id, relay_parent, 0, - vec![4].into(), - vec![5].into(), + required_parent.clone(), + vec![0].into(), 0, )); - let base_constraints = make_constraints(0, vec![0], required_parent.clone()); + // Candidates 1..=5 + for index in 1..=5 { + candidates.push(make_committed_candidate( + para_id, + relay_parent, + 0, + vec![index - 1].into(), + vec![index].into(), + 0, + )); + } + let mut storage = CandidateStorage::default(); + for (pvd, candidate) in candidates.iter() { + storage + .add_candidate_entry( + CandidateEntry::new_seconded(candidate.hash(), candidate.clone(), pvd.clone()) + .unwrap(), + ) + .unwrap(); + } + + let candidates = candidates + .into_iter() + .map(|(_pvd, candidate)| candidate.hash()) + .collect::>(); + let hashes = + |range: Range| range.map(|i| (candidates[i], relay_parent)).collect::>(); + let relay_parent_info = RelayChainBlockInfo { number: relay_parent_number, hash: relay_parent, storage_root: relay_parent_storage_root, }; - for (pvd, candidate) in candidates.iter() { - storage - .add_candidate(candidate.clone(), pvd.clone(), CandidateState::Seconded) - .unwrap(); - } - let candidates = candidates.into_iter().map(|(_pvd, candidate)| candidate).collect::>(); + let base_constraints = make_constraints(0, vec![0], required_parent.clone()); let scope = Scope::with_ancestors( - para_id, relay_parent_info.clone(), base_constraints.clone(), vec![], @@ -1248,506 +1380,140 @@ fn test_find_ancestor_path_and_find_backable_to_vec() { vec![], ) .unwrap(); - let chain = FragmentChain::populate(scope, &storage); + let mut chain = populate_chain_from_previous_storage(&scope, &storage); + // For now, candidates are only seconded, not backed. So the best chain is empty and no + // candidate will be returned. assert_eq!(candidates.len(), 6); - assert_eq!(chain.to_vec().len(), 6); + assert_eq!(chain.best_chain_len(), 0); + assert_eq!(chain.unconnected_len(), 6); + + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + + // Do tests with only a couple of candidates being backed. + { + let mut chain = chain.clone(); + chain.candidate_backed(&&candidates[5]); + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + chain.candidate_backed(&&candidates[3]); + chain.candidate_backed(&&candidates[4]); + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + + chain.candidate_backed(&&candidates[1]); + for count in 0..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count).len(), 0); + } + + chain.candidate_backed(&&candidates[0]); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 1), hashes(0..1)); + for count in 2..10 { + assert_eq!(chain.find_backable_chain(Ancestors::new(), count), hashes(0..2)); + } + + // Now back the missing piece. + chain.candidate_backed(&&candidates[2]); + assert_eq!(chain.best_chain_len(), 6); + for count in 0..10 { + assert_eq!( + chain.find_backable_chain(Ancestors::new(), count), + (0..6) + .take(count as usize) + .map(|i| (candidates[i], relay_parent)) + .collect::>() + ); + } + } + + // Now back all candidates. Back them in a random order. The result should always be the same. + let mut candidates_shuffled = candidates.clone(); + candidates_shuffled.shuffle(&mut thread_rng()); + for candidate in candidates.iter() { + chain.candidate_backed(candidate); + storage.mark_backed(candidate); + } // No ancestors supplied. assert_eq!(chain.find_ancestor_path(Ancestors::new()), 0); - assert_eq!(chain.find_backable_chain(Ancestors::new(), 0, |_| true), vec![]); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 1, |_| true), - [0].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 2, |_| true), - [0, 1].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 5, |_| true), - [0, 1, 2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 0), vec![]); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 1), hashes(0..1)); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 2), hashes(0..2)); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 5), hashes(0..5)); for count in 6..10 { - assert_eq!( - chain.find_backable_chain(Ancestors::new(), count, |_| true), - [0, 1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(Ancestors::new(), count), hashes(0..6)); } - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 7, |_| true), - [0, 1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(Ancestors::new(), 10, |_| true), - [0, 1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 7), hashes(0..6)); + assert_eq!(chain.find_backable_chain(Ancestors::new(), 10), hashes(0..6)); // Ancestor which is not part of the chain. Will be ignored. let ancestors: Ancestors = [CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - let ancestors: Ancestors = - [candidates[1].hash(), CandidateHash::default()].into_iter().collect(); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(0..4)); + + let ancestors: Ancestors = [candidates[1], CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - let ancestors: Ancestors = - [candidates[0].hash(), CandidateHash::default()].into_iter().collect(); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(0..4)); + + let ancestors: Ancestors = [candidates[0], CandidateHash::default()].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 1); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [1, 2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(1..5)); // Ancestors which are part of the chain but don't form a path from root. Will be ignored. - let ancestors: Ancestors = [candidates[1].hash(), candidates[2].hash()].into_iter().collect(); + let ancestors: Ancestors = [candidates[1], candidates[2]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 0); - assert_eq!( - chain.find_backable_chain(ancestors, 4, |_| true), - [0, 1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors, 4), hashes(0..4)); // Valid ancestors. - let ancestors: Ancestors = [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] - .into_iter() - .collect(); + let ancestors: Ancestors = [candidates[2], candidates[0], candidates[1]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 3); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 2, |_| true), - [3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 2), hashes(3..5)); for count in 3..10 { - assert_eq!( - chain.find_backable_chain(ancestors.clone(), count, |_| true), - [3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), count), hashes(3..6)); } // Valid ancestors with candidates which have been omitted due to timeouts - let ancestors: Ancestors = [candidates[0].hash(), candidates[2].hash()].into_iter().collect(); + let ancestors: Ancestors = [candidates[0], candidates[2]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 1); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 3, |_| true), - [1, 2, 3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 4, |_| true), - [1, 2, 3, 4].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 3), hashes(1..4)); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 4), hashes(1..5)); for count in 5..10 { - assert_eq!( - chain.find_backable_chain(ancestors.clone(), count, |_| true), - [1, 2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), count), hashes(1..6)); } - let ancestors: Ancestors = [candidates[0].hash(), candidates[1].hash(), candidates[3].hash()] - .into_iter() - .collect(); + let ancestors: Ancestors = [candidates[0], candidates[1], candidates[3]].into_iter().collect(); assert_eq!(chain.find_ancestor_path(ancestors.clone()), 2); - assert_eq!( - chain.find_backable_chain(ancestors.clone(), 4, |_| true), - [2, 3, 4, 5].into_iter().map(|i| candidates[i].hash()).collect::>() - ); + assert_eq!(chain.find_backable_chain(ancestors.clone(), 4), hashes(2..6)); // Requested count is 0. - assert_eq!(chain.find_backable_chain(ancestors, 0, |_| true), vec![]); - - // Stop when we've found a candidate for which pred returns false. - let ancestors: Ancestors = [candidates[2].hash(), candidates[0].hash(), candidates[1].hash()] - .into_iter() - .collect(); - for count in 1..10 { - assert_eq!( - // Stop at 4. - chain.find_backable_chain(ancestors.clone(), count, |hash| hash != - &candidates[4].hash()), - [3].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - } + assert_eq!(chain.find_backable_chain(ancestors, 0), vec![]); // Stop when we've found a candidate which is pending availability { let scope = Scope::with_ancestors( - para_id, relay_parent_info.clone(), base_constraints, // Mark the third candidate as pending availability vec![PendingAvailability { - candidate_hash: candidates[3].hash(), + candidate_hash: candidates[3], relay_parent: relay_parent_info, }], max_depth, vec![], ) .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - let ancestors: Ancestors = - [candidates[0].hash(), candidates[1].hash()].into_iter().collect(); + let chain = populate_chain_from_previous_storage(&scope, &storage); + let ancestors: Ancestors = [candidates[0], candidates[1]].into_iter().collect(); assert_eq!( // Stop at 4. - chain.find_backable_chain(ancestors.clone(), 3, |_| true), - [2].into_iter().map(|i| candidates[i].hash()).collect::>() - ); - } -} - -#[test] -fn hypothetical_membership() { - let mut storage = CandidateStorage::default(); - - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - - let (pvd_a, candidate_a) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 0, - ); - let candidate_a_hash = candidate_a.hash(); - - let (pvd_b, candidate_b) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0b].into(), - vec![0x0c].into(), - 0, - ); - let candidate_b_hash = candidate_b.hash(); - - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - - let max_depth = 4; - storage.add_candidate(candidate_a, pvd_a, CandidateState::Seconded).unwrap(); - storage.add_candidate(candidate_b, pvd_b, CandidateState::Seconded).unwrap(); - let scope = Scope::with_ancestors( - para_id, - relay_parent_a_info.clone(), - base_constraints.clone(), - vec![], - max_depth, - vec![], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - - assert_eq!(chain.to_vec().len(), 2); - - // Check candidates which are already present - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: candidate_a_hash, - }, - &storage, - )); - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0b]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: candidate_b_hash, - }, - &storage, - )); - - // Forks not allowed. - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(21)), - }, - &storage, - )); - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0b]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(22)), - }, - &storage, - )); - - // Unknown candidate which builds on top of the current chain. - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0c]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(23)), - }, - &storage, - )); - - // Unknown unconnected candidate which may be valid. - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0e]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(23)), - }, - &storage, - )); - - // The number of unconnected candidates is limited (chain.len() + unconnected) <= max_depth - { - // C will be an unconnected candidate. - let (pvd_c, candidate_c) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0e].into(), - vec![0x0f].into(), - 0, - ); - let candidate_c_hash = candidate_c.hash(); - - // Add an invalid candidate in the storage. This would introduce a fork. Just to test that - // it's ignored. - let (invalid_pvd, invalid_candidate) = make_committed_candidate( - para_id, - relay_parent_a, - 1, - vec![0x0a].into(), - vec![0x0b].into(), - 0, + chain.find_backable_chain(ancestors.clone(), 3), + hashes(2..3) ); - - let scope = Scope::with_ancestors( - para_id, - relay_parent_a_info, - base_constraints, - vec![], - 2, - vec![], - ) - .unwrap(); - let mut storage = storage.clone(); - storage.add_candidate(candidate_c, pvd_c, CandidateState::Seconded).unwrap(); - - let chain = FragmentChain::populate(scope, &storage); - assert_eq!(chain.to_vec(), vec![candidate_a_hash, candidate_b_hash]); - - storage - .add_candidate(invalid_candidate, invalid_pvd, CandidateState::Seconded) - .unwrap(); - - // Check that C is accepted as a potential unconnected candidate. - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0e]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_hash: candidate_c_hash, - candidate_para: para_id - }, - &storage, - )); - - // Since C is already an unconnected candidate in the storage. - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0f]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: CandidateHash(Hash::repeat_byte(23)), - }, - &storage, - )); } } - -#[test] -fn hypothetical_membership_stricter_on_complete_candidates() { - let storage = CandidateStorage::default(); - - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - - let (pvd_a, candidate_a) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 1000, // watermark is illegal - ); - - let candidate_a_hash = candidate_a.hash(); - - let base_constraints = make_constraints(0, vec![0], vec![0x0a].into()); - let pending_availability = Vec::new(); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - - let max_depth = 4; - let scope = Scope::with_ancestors( - para_id, - relay_parent_a_info, - base_constraints, - pending_availability, - max_depth, - vec![], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_para: para_id, - candidate_hash: candidate_a_hash, - }, - &storage, - )); - - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Complete { - receipt: Arc::new(candidate_a), - persisted_validation_data: pvd_a, - candidate_hash: candidate_a_hash, - }, - &storage, - )); -} - -#[test] -fn hypothetical_membership_with_pending_availability_in_scope() { - let mut storage = CandidateStorage::default(); - - let para_id = ParaId::from(5u32); - let relay_parent_a = Hash::repeat_byte(1); - let relay_parent_b = Hash::repeat_byte(2); - let relay_parent_c = Hash::repeat_byte(3); - - let (pvd_a, candidate_a) = make_committed_candidate( - para_id, - relay_parent_a, - 0, - vec![0x0a].into(), - vec![0x0b].into(), - 0, - ); - let candidate_a_hash = candidate_a.hash(); - - let (pvd_b, candidate_b) = make_committed_candidate( - para_id, - relay_parent_b, - 1, - vec![0x0b].into(), - vec![0x0c].into(), - 1, - ); - - // Note that relay parent `a` is not allowed. - let base_constraints = make_constraints(1, vec![], vec![0x0a].into()); - - let relay_parent_a_info = RelayChainBlockInfo { - number: pvd_a.relay_parent_number, - hash: relay_parent_a, - storage_root: pvd_a.relay_parent_storage_root, - }; - let pending_availability = vec![PendingAvailability { - candidate_hash: candidate_a_hash, - relay_parent: relay_parent_a_info, - }]; - - let relay_parent_b_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number, - hash: relay_parent_b, - storage_root: pvd_b.relay_parent_storage_root, - }; - let relay_parent_c_info = RelayChainBlockInfo { - number: pvd_b.relay_parent_number + 1, - hash: relay_parent_c, - storage_root: Hash::zero(), - }; - - let max_depth = 4; - storage.add_candidate(candidate_a, pvd_a, CandidateState::Seconded).unwrap(); - storage.add_candidate(candidate_b, pvd_b, CandidateState::Backed).unwrap(); - storage.mark_backed(&candidate_a_hash); - - let scope = Scope::with_ancestors( - para_id, - relay_parent_c_info, - base_constraints, - pending_availability, - max_depth, - vec![relay_parent_b_info], - ) - .unwrap(); - let chain = FragmentChain::populate(scope, &storage); - - assert_eq!(chain.to_vec().len(), 2); - - let candidate_d_hash = CandidateHash(Hash::repeat_byte(0xAA)); - - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_a, - candidate_hash: candidate_a_hash, - candidate_para: para_id - }, - &storage, - )); - - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0a]).hash(), - candidate_relay_parent: relay_parent_c, - candidate_para: para_id, - candidate_hash: candidate_d_hash, - }, - &storage, - )); - - assert!(!chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0b]).hash(), - candidate_relay_parent: relay_parent_c, - candidate_para: para_id, - candidate_hash: candidate_d_hash, - }, - &storage, - )); - - assert!(chain.hypothetical_membership( - HypotheticalCandidate::Incomplete { - parent_head_data_hash: HeadData::from(vec![0x0c]).hash(), - candidate_relay_parent: relay_parent_b, - candidate_para: para_id, - candidate_hash: candidate_d_hash, - }, - &storage, - )); -} diff --git a/polkadot/node/core/prospective-parachains/src/lib.rs b/polkadot/node/core/prospective-parachains/src/lib.rs index e4b6deffdf4a..ecb1f3a476ec 100644 --- a/polkadot/node/core/prospective-parachains/src/lib.rs +++ b/polkadot/node/core/prospective-parachains/src/lib.rs @@ -26,9 +26,11 @@ //! //! This subsystem also handles concerns such as the relay-chain being forkful and session changes. +#![deny(unused_crate_dependencies)] + use std::collections::{HashMap, HashSet}; -use fragment_chain::{FragmentChain, PotentialAddition}; +use fragment_chain::CandidateStorage; use futures::{channel::oneshot, prelude::*}; use polkadot_node_subsystem::{ @@ -41,6 +43,7 @@ use polkadot_node_subsystem::{ overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_util::{ + backing_implicit_view::{BlockInfoProspectiveParachains as BlockInfo, View as ImplicitView}, inclusion_emulator::{Constraints, RelayChainBlockInfo}, request_session_index_for_child, runtime::{prospective_parachains_mode, ProspectiveParachainsMode}, @@ -55,8 +58,7 @@ use polkadot_primitives::{ use crate::{ error::{FatalError, FatalResult, JfyiError, JfyiErrorResult, Result}, fragment_chain::{ - CandidateState, CandidateStorage, CandidateStorageInsertionError, - Scope as FragmentChainScope, + CandidateEntry, Error as FragmentChainError, FragmentChain, Scope as FragmentChainScope, }, }; @@ -71,20 +73,33 @@ use self::metrics::Metrics; const LOG_TARGET: &str = "parachain::prospective-parachains"; struct RelayBlockViewData { - // Scheduling info for paras and upcoming paras. + // The fragment chains for current and upcoming scheduled paras. fragment_chains: HashMap, - pending_availability: HashSet, } struct View { - // Active or recent relay-chain blocks by block hash. - active_leaves: HashMap, - candidate_storage: HashMap, + // Per relay parent fragment chains. These includes all relay parents under the implicit view. + per_relay_parent: HashMap, + // The hashes of the currently active leaves. This is a subset of the keys in + // `per_relay_parent`. + active_leaves: HashSet, + // The backing implicit view. + implicit_view: ImplicitView, } impl View { + // Initialize with empty values. fn new() -> Self { - View { active_leaves: HashMap::new(), candidate_storage: HashMap::new() } + View { + per_relay_parent: HashMap::new(), + active_leaves: HashSet::new(), + implicit_view: ImplicitView::default(), + } + } + + // Get the fragment chains of this leaf. + fn get_fragment_chains(&self, leaf: &Hash) -> Option<&HashMap> { + self.per_relay_parent.get(&leaf).map(|view_data| &view_data.fragment_chains) } } @@ -142,9 +157,9 @@ async fn run_iteration( FromOrchestra::Signal(OverseerSignal::BlockFinalized(..)) => {}, FromOrchestra::Communication { msg } => match msg { ProspectiveParachainsMessage::IntroduceSecondedCandidate(request, tx) => - handle_introduce_seconded_candidate(&mut *ctx, view, request, tx, metrics).await, + handle_introduce_seconded_candidate(view, request, tx, metrics).await, ProspectiveParachainsMessage::CandidateBacked(para, candidate_hash) => - handle_candidate_backed(&mut *ctx, view, para, candidate_hash).await, + handle_candidate_backed(view, para, candidate_hash, metrics).await, ProspectiveParachainsMessage::GetBackableCandidates( relay_parent, para, @@ -170,17 +185,32 @@ async fn handle_active_leaves_update( update: ActiveLeavesUpdate, metrics: &Metrics, ) -> JfyiErrorResult<()> { - // 1. clean up inactive leaves - // 2. determine all scheduled paras at the new block - // 3. construct new fragment chain for each para for each new leaf - // 4. prune candidate storage. + // For any new active leaf: + // - determine the scheduled paras + // - pre-populate the candidate storage with pending availability candidates and candidates from + // the parent leaf + // - populate the fragment chain + // - add it to the implicit view + // + // Then mark the newly-deactivated leaves as deactivated and update the implicit view. + // Finally, remove any relay parents that are no longer part of the implicit view. + + let _timer = metrics.time_handle_active_leaves_update(); - for deactivated in &update.deactivated { - view.active_leaves.remove(deactivated); - } + gum::trace!( + target: LOG_TARGET, + activated = ?update.activated, + deactivated = ?update.deactivated, + "Handle ActiveLeavesUpdate" + ); let mut temp_header_cache = HashMap::new(); + // There can only be one newly activated leaf, `update.activated` is an `Option`. for activated in update.activated.into_iter() { + if update.deactivated.contains(&activated.hash) { + continue + } + let hash = activated.hash; let mode = prospective_parachains_mode(ctx.sender(), hash) @@ -199,38 +229,34 @@ async fn handle_active_leaves_update( return Ok(()) }; - let scheduled_paras = fetch_upcoming_paras(&mut *ctx, hash).await?; + let scheduled_paras = fetch_upcoming_paras(ctx, hash).await?; - let block_info: RelayChainBlockInfo = - match fetch_block_info(&mut *ctx, &mut temp_header_cache, hash).await? { - None => { - gum::warn!( - target: LOG_TARGET, - block_hash = ?hash, - "Failed to get block info for newly activated leaf block." - ); + let block_info = match fetch_block_info(ctx, &mut temp_header_cache, hash).await? { + None => { + gum::warn!( + target: LOG_TARGET, + block_hash = ?hash, + "Failed to get block info for newly activated leaf block." + ); - // `update.activated` is an option, but we can use this - // to exit the 'loop' and skip this block without skipping - // pruning logic. - continue - }, - Some(info) => info, - }; + // `update.activated` is an option, but we can use this + // to exit the 'loop' and skip this block without skipping + // pruning logic. + continue + }, + Some(info) => info, + }; let ancestry = - fetch_ancestry(&mut *ctx, &mut temp_header_cache, hash, allowed_ancestry_len).await?; + fetch_ancestry(ctx, &mut temp_header_cache, hash, allowed_ancestry_len).await?; - let mut all_pending_availability = HashSet::new(); + let prev_fragment_chains = + ancestry.first().and_then(|prev_leaf| view.get_fragment_chains(&prev_leaf.hash)); - // Find constraints. let mut fragment_chains = HashMap::new(); for para in scheduled_paras { - let candidate_storage = - view.candidate_storage.entry(para).or_insert_with(CandidateStorage::default); - - let backing_state = fetch_backing_state(&mut *ctx, hash, para).await?; - + // Find constraints and pending availability candidates. + let backing_state = fetch_backing_state(ctx, hash, para).await?; let Some((constraints, pending_availability)) = backing_state else { // This indicates a runtime conflict of some kind. gum::debug!( @@ -243,8 +269,6 @@ async fn handle_active_leaves_update( continue }; - all_pending_availability.extend(pending_availability.iter().map(|c| c.candidate_hash)); - let pending_availability = preprocess_candidates_pending_availability( ctx, &mut temp_header_cache, @@ -254,16 +278,18 @@ async fn handle_active_leaves_update( .await?; let mut compact_pending = Vec::with_capacity(pending_availability.len()); + let mut pending_availability_storage = CandidateStorage::default(); + for c in pending_availability { - let res = candidate_storage.add_candidate( + let candidate_hash = c.compact.candidate_hash; + let res = pending_availability_storage.add_pending_availability_candidate( + candidate_hash, c.candidate, c.persisted_validation_data, - CandidateState::Backed, ); - let candidate_hash = c.compact.candidate_hash; match res { - Ok(_) | Err(CandidateStorageInsertionError::CandidateAlreadyKnown(_)) => {}, + Ok(_) | Err(FragmentChainError::CandidateAlreadyKnown) => {}, Err(err) => { gum::warn!( target: LOG_TARGET, @@ -280,119 +306,138 @@ async fn handle_active_leaves_update( compact_pending.push(c.compact); } - let scope = FragmentChainScope::with_ancestors( - para, - block_info.clone(), + let scope = match FragmentChainScope::with_ancestors( + block_info.clone().into(), constraints, compact_pending, max_candidate_depth, - ancestry.iter().cloned(), - ) - .expect("ancestors are provided in reverse order and correctly; qed"); + ancestry + .iter() + .map(|a| RelayChainBlockInfo::from(a.clone())) + .collect::>(), + ) { + Ok(scope) => scope, + Err(unexpected_ancestors) => { + gum::warn!( + target: LOG_TARGET, + para_id = ?para, + max_candidate_depth, + ?ancestry, + leaf = ?hash, + "Relay chain ancestors have wrong order: {:?}", + unexpected_ancestors + ); + continue + }, + }; gum::trace!( target: LOG_TARGET, relay_parent = ?hash, min_relay_parent = scope.earliest_relay_parent().number, para_id = ?para, + ancestors = ?ancestry, "Creating fragment chain" ); - let chain = FragmentChain::populate(scope, &*candidate_storage); + let number_of_pending_candidates = pending_availability_storage.len(); + + // Init the fragment chain with the pending availability candidates. + let mut chain = FragmentChain::init(scope, pending_availability_storage); + + if chain.best_chain_len() < number_of_pending_candidates { + gum::warn!( + target: LOG_TARGET, + relay_parent = ?hash, + para_id = ?para, + "Not all pending availability candidates could be introduced. Actual vs expected count: {}, {}", + chain.best_chain_len(), + number_of_pending_candidates + ) + } + + // If we know the previous fragment chain, use that for further populating the fragment + // chain. + if let Some(prev_fragment_chain) = + prev_fragment_chains.and_then(|chains| chains.get(¶)) + { + chain.populate_from_previous(prev_fragment_chain); + } gum::trace!( target: LOG_TARGET, relay_parent = ?hash, para_id = ?para, - "Populated fragment chain with {} candidates", - chain.len() + "Populated fragment chain with {} candidates: {:?}", + chain.best_chain_len(), + chain.best_chain_vec() + ); + + gum::trace!( + target: LOG_TARGET, + relay_parent = ?hash, + para_id = ?para, + "Potential candidate storage for para: {:?}", + chain.unconnected().map(|candidate| candidate.hash()).collect::>() ); fragment_chains.insert(para, chain); } - view.active_leaves.insert( - hash, - RelayBlockViewData { fragment_chains, pending_availability: all_pending_availability }, - ); - } + view.per_relay_parent.insert(hash, RelayBlockViewData { fragment_chains }); - if !update.deactivated.is_empty() { - // This has potential to be a hotspot. - prune_view_candidate_storage(view, metrics); - } + view.active_leaves.insert(hash); - Ok(()) -} + view.implicit_view + .activate_leaf_from_prospective_parachains(block_info, &ancestry); + } -fn prune_view_candidate_storage(view: &mut View, metrics: &Metrics) { - let _timer = metrics.time_prune_view_candidate_storage(); + for deactivated in update.deactivated { + view.active_leaves.remove(&deactivated); + view.implicit_view.deactivate_leaf(deactivated); + } - let active_leaves = &view.active_leaves; - let mut live_candidates = HashSet::new(); - let mut live_paras = HashSet::new(); - for sub_view in active_leaves.values() { - live_candidates.extend(sub_view.pending_availability.iter().cloned()); + { + let remaining: HashSet<_> = view.implicit_view.all_allowed_relay_parents().collect(); - for (para_id, fragment_chain) in &sub_view.fragment_chains { - live_candidates.extend(fragment_chain.to_vec()); - live_paras.insert(*para_id); - } + view.per_relay_parent.retain(|r, _| remaining.contains(&r)); } - let connected_candidates_count = live_candidates.len(); - for (leaf, sub_view) in active_leaves.iter() { - for (para_id, fragment_chain) in &sub_view.fragment_chains { - if let Some(storage) = view.candidate_storage.get(para_id) { - let unconnected_potential = - fragment_chain.find_unconnected_potential_candidates(storage, None); - if !unconnected_potential.is_empty() { - gum::trace!( - target: LOG_TARGET, - ?leaf, - "Keeping {} unconnected candidates for paraid {} in storage: {:?}", - unconnected_potential.len(), - para_id, - unconnected_potential - ); + if metrics.0.is_some() { + let mut active_connected = 0; + let mut active_unconnected = 0; + let mut candidates_in_implicit_view = 0; + + for (hash, RelayBlockViewData { fragment_chains, .. }) in view.per_relay_parent.iter() { + if view.active_leaves.contains(hash) { + for chain in fragment_chains.values() { + active_connected += chain.best_chain_len(); + active_unconnected += chain.unconnected_len(); + } + } else { + for chain in fragment_chains.values() { + candidates_in_implicit_view += chain.best_chain_len(); + candidates_in_implicit_view += chain.unconnected_len(); } - live_candidates.extend(unconnected_potential); } } - } - - view.candidate_storage.retain(|para_id, storage| { - if !live_paras.contains(¶_id) { - return false - } - - storage.retain(|h| live_candidates.contains(&h)); - - // Even if `storage` is now empty, we retain. - // This maintains a convenient invariant that para-id storage exists - // as long as there's an active head which schedules the para. - true - }); - for (para_id, storage) in view.candidate_storage.iter() { - gum::trace!( - target: LOG_TARGET, - "Keeping a total of {} connected candidates for paraid {} in storage", - storage.candidates().count(), - para_id, - ); + metrics.record_candidate_count(active_connected as u64, active_unconnected as u64); + metrics.record_candidate_count_in_implicit_view(candidates_in_implicit_view as u64); } - metrics.record_candidate_storage_size( - connected_candidates_count as u64, - live_candidates.len().saturating_sub(connected_candidates_count) as u64, - ); + let num_active_leaves = view.active_leaves.len() as u64; + let num_inactive_leaves = + (view.per_relay_parent.len() as u64).saturating_sub(num_active_leaves); + metrics.record_leaves_count(num_active_leaves, num_inactive_leaves); + + Ok(()) } struct ImportablePendingAvailability { candidate: CommittedCandidateReceipt, persisted_validation_data: PersistedValidationData, - compact: crate::fragment_chain::PendingAvailability, + compact: fragment_chain::PendingAvailability, } #[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] @@ -435,9 +480,9 @@ async fn preprocess_candidates_pending_availability( relay_parent_number: relay_parent.number, relay_parent_storage_root: relay_parent.storage_root, }, - compact: crate::fragment_chain::PendingAvailability { + compact: fragment_chain::PendingAvailability { candidate_hash: pending.candidate_hash, - relay_parent, + relay_parent: relay_parent.into(), }, }); @@ -447,9 +492,7 @@ async fn preprocess_candidates_pending_availability( Ok(importable) } -#[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] -async fn handle_introduce_seconded_candidate( - _ctx: &mut Context, +async fn handle_introduce_seconded_candidate( view: &mut View, request: IntroduceSecondedCandidateRequest, tx: oneshot::Sender, @@ -463,167 +506,163 @@ async fn handle_introduce_seconded_candidate( persisted_validation_data: pvd, } = request; - let Some(storage) = view.candidate_storage.get_mut(¶) else { - gum::warn!( - target: LOG_TARGET, - para_id = ?para, - candidate_hash = ?candidate.hash(), - "Received seconded candidate for inactive para", - ); + let candidate_hash = candidate.hash(); + let candidate_entry = match CandidateEntry::new_seconded(candidate_hash, candidate, pvd) { + Ok(candidate) => candidate, + Err(err) => { + gum::warn!( + target: LOG_TARGET, + para = ?para, + "Cannot add seconded candidate: {}", + err + ); - let _ = tx.send(false); - return + let _ = tx.send(false); + return + }, }; - let parent_head_hash = pvd.parent_head.hash(); - let output_head_hash = Some(candidate.commitments.head_data.hash()); + let mut added = false; + let mut para_scheduled = false; + // We don't iterate only through the active leaves. We also update the deactivated parents in + // the implicit view, so that their upcoming children may see these candidates. + for (relay_parent, rp_data) in view.per_relay_parent.iter_mut() { + let Some(chain) = rp_data.fragment_chains.get_mut(¶) else { continue }; + let is_active_leaf = view.active_leaves.contains(relay_parent); - // We first introduce the candidate in the storage and then try to extend the chain. - // If the candidate gets included in the chain, we can keep it in storage. - // If it doesn't, check that it's still a potential candidate in at least one fragment chain. - // If it's not, we can remove it. + para_scheduled = true; - let candidate_hash = - match storage.add_candidate(candidate.clone(), pvd, CandidateState::Seconded) { - Ok(c) => c, - Err(CandidateStorageInsertionError::CandidateAlreadyKnown(_)) => { + match chain.try_adding_seconded_candidate(&candidate_entry) { + Ok(()) => { gum::debug!( target: LOG_TARGET, - para = ?para, - "Attempting to introduce an already known candidate: {:?}", - candidate.hash() + ?para, + ?relay_parent, + ?is_active_leaf, + "Added seconded candidate {:?}", + candidate_hash ); - // Candidate already known. - let _ = tx.send(true); - return + added = true; }, - Err(CandidateStorageInsertionError::PersistedValidationDataMismatch) => { - // We can't log the candidate hash without either doing more ~expensive - // hashing but this branch indicates something is seriously wrong elsewhere - // so it's doubtful that it would affect debugging. - - gum::warn!( + Err(FragmentChainError::CandidateAlreadyKnown) => { + gum::debug!( target: LOG_TARGET, - para = ?para, - "Received seconded candidate had mismatching validation data", + ?para, + ?relay_parent, + ?is_active_leaf, + "Attempting to introduce an already known candidate: {:?}", + candidate_hash ); - - let _ = tx.send(false); - return + added = true; }, - }; - - let mut keep_in_storage = false; - for (relay_parent, leaf_data) in view.active_leaves.iter_mut() { - if let Some(chain) = leaf_data.fragment_chains.get_mut(¶) { - gum::trace!( - target: LOG_TARGET, - para = ?para, - ?relay_parent, - "Candidates in chain before trying to introduce a new one: {:?}", - chain.to_vec() - ); - chain.extend_from_storage(&*storage); - if chain.contains_candidate(&candidate_hash) { - keep_in_storage = true; - - gum::trace!( + Err(err) => { + gum::debug!( target: LOG_TARGET, + ?para, ?relay_parent, - para = ?para, ?candidate_hash, - "Added candidate to chain.", - ); - } else { - match chain.can_add_candidate_as_potential( - &storage, - &candidate_hash, - &candidate.descriptor.relay_parent, - parent_head_hash, - output_head_hash, - ) { - PotentialAddition::Anyhow => { - gum::trace!( - target: LOG_TARGET, - para = ?para, - ?relay_parent, - ?candidate_hash, - "Kept candidate as unconnected potential.", - ); - - keep_in_storage = true; - }, - _ => { - gum::trace!( - target: LOG_TARGET, - para = ?para, - ?relay_parent, - "Not introducing a new candidate: {:?}", - candidate_hash - ); - }, - } - } + ?is_active_leaf, + "Cannot introduce seconded candidate: {}", + err + ) + }, } } - // If there is at least one leaf where this candidate can be added or potentially added in the - // future, keep it in storage. - if !keep_in_storage { - storage.remove_candidate(&candidate_hash); + if !para_scheduled { + gum::warn!( + target: LOG_TARGET, + para_id = ?para, + ?candidate_hash, + "Received seconded candidate for inactive para", + ); + } + if !added { gum::debug!( target: LOG_TARGET, para = ?para, candidate = ?candidate_hash, - "Newly-seconded candidate cannot be kept under any active leaf", + "Newly-seconded candidate cannot be kept under any relay parent", ); } - let _ = tx.send(keep_in_storage); + let _ = tx.send(added); } -#[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] -async fn handle_candidate_backed( - _ctx: &mut Context, +async fn handle_candidate_backed( view: &mut View, para: ParaId, candidate_hash: CandidateHash, + metrics: &Metrics, ) { - let Some(storage) = view.candidate_storage.get_mut(¶) else { - gum::warn!( - target: LOG_TARGET, - para_id = ?para, - ?candidate_hash, - "Received instruction to back a candidate for unscheduled para", - ); + let _timer = metrics.time_candidate_backed(); - return - }; + let mut found_candidate = false; + let mut found_para = false; + + // We don't iterate only through the active leaves. We also update the deactivated parents in + // the implicit view, so that their upcoming children may see these candidates. + for (relay_parent, rp_data) in view.per_relay_parent.iter_mut() { + let Some(chain) = rp_data.fragment_chains.get_mut(¶) else { continue }; + let is_active_leaf = view.active_leaves.contains(relay_parent); + + found_para = true; + if chain.is_candidate_backed(&candidate_hash) { + gum::debug!( + target: LOG_TARGET, + ?para, + ?candidate_hash, + ?is_active_leaf, + "Received redundant instruction to mark as backed an already backed candidate", + ); + found_candidate = true; + } else if chain.contains_unconnected_candidate(&candidate_hash) { + found_candidate = true; + // Mark the candidate as backed. This can recreate the fragment chain. + chain.candidate_backed(&candidate_hash); + + gum::trace!( + target: LOG_TARGET, + ?relay_parent, + ?para, + ?is_active_leaf, + "Candidate backed. Candidate chain for para: {:?}", + chain.best_chain_vec() + ); + + gum::trace!( + target: LOG_TARGET, + ?relay_parent, + ?para, + ?is_active_leaf, + "Potential candidate storage for para: {:?}", + chain.unconnected().map(|candidate| candidate.hash()).collect::>() + ); + } + } - if !storage.contains(&candidate_hash) { + if !found_para { gum::warn!( target: LOG_TARGET, - para_id = ?para, + ?para, ?candidate_hash, - "Received instruction to back unknown candidate", + "Received instruction to back a candidate for unscheduled para", ); return } - if storage.is_backed(&candidate_hash) { + if !found_candidate { + // This can be harmless. It can happen if we received a better backed candidate before and + // dropped this other candidate already. gum::debug!( target: LOG_TARGET, - para_id = ?para, + ?para, ?candidate_hash, - "Received redundant instruction to mark candidate as backed", + "Received instruction to back unknown candidate", ); - - return } - - storage.mark_backed(&candidate_hash); } fn answer_get_backable_candidates( @@ -634,7 +673,7 @@ fn answer_get_backable_candidates( ancestors: Ancestors, tx: oneshot::Sender>, ) { - let Some(data) = view.active_leaves.get(&relay_parent) else { + if !view.active_leaves.contains(&relay_parent) { gum::debug!( target: LOG_TARGET, ?relay_parent, @@ -644,26 +683,25 @@ fn answer_get_backable_candidates( let _ = tx.send(vec![]); return - }; - - let Some(chain) = data.fragment_chains.get(¶) else { + } + let Some(data) = view.per_relay_parent.get(&relay_parent) else { gum::debug!( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "Requested backable candidate for inactive para." + "Requested backable candidate for inexistent relay-parent." ); let _ = tx.send(vec![]); return }; - let Some(storage) = view.candidate_storage.get(¶) else { - gum::warn!( + let Some(chain) = data.fragment_chains.get(¶) else { + gum::debug!( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "No candidate storage for active para", + "Requested backable candidate for inactive para." ); let _ = tx.send(vec![]); @@ -674,38 +712,19 @@ fn answer_get_backable_candidates( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "Candidate storage for para: {:?}", - storage.candidates().map(|candidate| candidate.hash()).collect::>() + "Candidate chain for para: {:?}", + chain.best_chain_vec() ); gum::trace!( target: LOG_TARGET, ?relay_parent, para_id = ?para, - "Candidate chain for para: {:?}", - chain.to_vec() + "Potential candidate storage for para: {:?}", + chain.unconnected().map(|candidate| candidate.hash()).collect::>() ); - let backable_candidates: Vec<_> = chain - .find_backable_chain(ancestors.clone(), count, |candidate| storage.is_backed(candidate)) - .into_iter() - .filter_map(|child_hash| { - storage.relay_parent_of_candidate(&child_hash).map_or_else( - || { - // Here, we'd actually need to trim all of the candidates that follow. Or - // not, the runtime will do this. Impossible scenario anyway. - gum::error!( - target: LOG_TARGET, - ?child_hash, - para_id = ?para, - "Candidate is present in fragment chain but not in candidate's storage!", - ); - None - }, - |parent_hash| Some((child_hash, parent_hash)), - ) - }) - .collect(); + let backable_candidates = chain.find_backable_chain(ancestors.clone(), count); if backable_candidates.is_empty() { gum::trace!( @@ -742,19 +761,32 @@ fn answer_hypothetical_membership_request( } let required_active_leaf = request.fragment_chain_relay_parent; - for (active_leaf, leaf_view) in view + for active_leaf in view .active_leaves .iter() - .filter(|(h, _)| required_active_leaf.as_ref().map_or(true, |x| h == &x)) + .filter(|h| required_active_leaf.as_ref().map_or(true, |x| h == &x)) { + let Some(leaf_view) = view.per_relay_parent.get(&active_leaf) else { continue }; for &mut (ref candidate, ref mut membership) in &mut response { let para_id = &candidate.candidate_para(); let Some(fragment_chain) = leaf_view.fragment_chains.get(para_id) else { continue }; - let Some(candidate_storage) = view.candidate_storage.get(para_id) else { continue }; - if fragment_chain.hypothetical_membership(candidate.clone(), candidate_storage) { - membership.push(*active_leaf); - } + let res = fragment_chain.can_add_candidate_as_potential(candidate); + match res { + Err(FragmentChainError::CandidateAlreadyKnown) | Ok(()) => { + membership.push(*active_leaf); + }, + Err(err) => { + gum::debug!( + target: LOG_TARGET, + para = ?para_id, + leaf = ?active_leaf, + candidate = ?candidate.candidate_hash(), + "Candidate is not a hypothetical member: {}", + err + ) + }, + }; } } @@ -767,9 +799,11 @@ fn answer_minimum_relay_parents_request( tx: oneshot::Sender>, ) { let mut v = Vec::new(); - if let Some(leaf_data) = view.active_leaves.get(&relay_parent) { - for (para_id, fragment_chain) in &leaf_data.fragment_chains { - v.push((*para_id, fragment_chain.scope().earliest_relay_parent().number)); + if view.active_leaves.contains(&relay_parent) { + if let Some(leaf_data) = view.per_relay_parent.get(&relay_parent) { + for (para_id, fragment_chain) in &leaf_data.fragment_chains { + v.push((*para_id, fragment_chain.scope().earliest_relay_parent().number)); + } } } @@ -781,37 +815,21 @@ fn answer_prospective_validation_data_request( request: ProspectiveValidationDataRequest, tx: oneshot::Sender>, ) { - // 1. Try to get the head-data from the candidate store if known. - // 2. Otherwise, it might exist as the base in some relay-parent and we can find it by iterating - // fragment chains. - // 3. Otherwise, it is unknown. - // 4. Also try to find the relay parent block info by scanning fragment chains. - // 5. If head data and relay parent block info are found - success. Otherwise, failure. - - let storage = match view.candidate_storage.get(&request.para_id) { - None => { - let _ = tx.send(None); - return - }, - Some(s) => s, - }; + // Try getting the needed data from any fragment chain. let (mut head_data, parent_head_data_hash) = match request.parent_head_data { - ParentHeadData::OnlyHash(parent_head_data_hash) => ( - storage.head_data_by_hash(&parent_head_data_hash).map(|x| x.clone()), - parent_head_data_hash, - ), + ParentHeadData::OnlyHash(parent_head_data_hash) => (None, parent_head_data_hash), ParentHeadData::WithData { head_data, hash } => (Some(head_data), hash), }; let mut relay_parent_info = None; let mut max_pov_size = None; - for fragment_chain in view - .active_leaves - .values() - .filter_map(|x| x.fragment_chains.get(&request.para_id)) - { + for fragment_chain in view.active_leaves.iter().filter_map(|x| { + view.per_relay_parent + .get(&x) + .and_then(|data| data.fragment_chains.get(&request.para_id)) + }) { if head_data.is_some() && relay_parent_info.is_some() && max_pov_size.is_some() { break } @@ -819,10 +837,7 @@ fn answer_prospective_validation_data_request( relay_parent_info = fragment_chain.scope().ancestor(&request.candidate_relay_parent); } if head_data.is_none() { - let required_parent = &fragment_chain.scope().base_constraints().required_parent; - if required_parent.hash() == parent_head_data_hash { - head_data = Some(required_parent.clone()); - } + head_data = fragment_chain.get_head_data_by_hash(&parent_head_data_hash); } if max_pov_size.is_none() { let contains_ancestor = @@ -925,7 +940,7 @@ async fn fetch_ancestry( cache: &mut HashMap, relay_hash: Hash, ancestors: usize, -) -> JfyiErrorResult> { +) -> JfyiErrorResult> { if ancestors == 0 { return Ok(Vec::new()) } @@ -1004,12 +1019,13 @@ async fn fetch_block_info( ctx: &mut Context, cache: &mut HashMap, relay_hash: Hash, -) -> JfyiErrorResult> { +) -> JfyiErrorResult> { let header = fetch_block_header_with_cache(ctx, cache, relay_hash).await?; - Ok(header.map(|header| RelayChainBlockInfo { + Ok(header.map(|header| BlockInfo { hash: relay_hash, number: header.number, + parent_hash: header.parent_hash, storage_root: header.state_root, })) } diff --git a/polkadot/node/core/prospective-parachains/src/metrics.rs b/polkadot/node/core/prospective-parachains/src/metrics.rs index 5abd9f56f306..78561bc878ac 100644 --- a/polkadot/node/core/prospective-parachains/src/metrics.rs +++ b/polkadot/node/core/prospective-parachains/src/metrics.rs @@ -17,15 +17,18 @@ use polkadot_node_subsystem::prometheus::Opts; use polkadot_node_subsystem_util::metrics::{ self, - prometheus::{self, GaugeVec, U64}, + prometheus::{self, Gauge, GaugeVec, U64}, }; #[derive(Clone)] pub(crate) struct MetricsInner { - prune_view_candidate_storage: prometheus::Histogram, - introduce_seconded_candidate: prometheus::Histogram, - hypothetical_membership: prometheus::Histogram, - candidate_storage_count: prometheus::GaugeVec, + time_active_leaves_update: prometheus::Histogram, + time_introduce_seconded_candidate: prometheus::Histogram, + time_candidate_backed: prometheus::Histogram, + time_hypothetical_membership: prometheus::Histogram, + candidate_count: prometheus::GaugeVec, + active_leaves_count: prometheus::GaugeVec, + implicit_view_candidate_count: prometheus::Gauge, } /// Candidate backing metrics. @@ -33,13 +36,11 @@ pub(crate) struct MetricsInner { pub struct Metrics(pub(crate) Option); impl Metrics { - /// Provide a timer for handling `prune_view_candidate_storage` which observes on drop. - pub fn time_prune_view_candidate_storage( + /// Provide a timer for handling `ActiveLeavesUpdate` which observes on drop. + pub fn time_handle_active_leaves_update( &self, ) -> Option { - self.0 - .as_ref() - .map(|metrics| metrics.prune_view_candidate_storage.start_timer()) + self.0.as_ref().map(|metrics| metrics.time_active_leaves_update.start_timer()) } /// Provide a timer for handling `IntroduceSecondedCandidate` which observes on drop. @@ -48,31 +49,47 @@ impl Metrics { ) -> Option { self.0 .as_ref() - .map(|metrics| metrics.introduce_seconded_candidate.start_timer()) + .map(|metrics| metrics.time_introduce_seconded_candidate.start_timer()) + } + + /// Provide a timer for handling `CandidateBacked` which observes on drop. + pub fn time_candidate_backed(&self) -> Option { + self.0.as_ref().map(|metrics| metrics.time_candidate_backed.start_timer()) } /// Provide a timer for handling `GetHypotheticalMembership` which observes on drop. pub fn time_hypothetical_membership_request( &self, ) -> Option { - self.0.as_ref().map(|metrics| metrics.hypothetical_membership.start_timer()) + self.0 + .as_ref() + .map(|metrics| metrics.time_hypothetical_membership.start_timer()) } - /// Record the size of the candidate storage. First param is the connected candidates count, - /// second param is the unconnected candidates count. - pub fn record_candidate_storage_size(&self, connected_count: u64, unconnected_count: u64) { + /// Record number of candidates across all fragment chains. First param is the connected + /// candidates count, second param is the unconnected candidates count. + pub fn record_candidate_count(&self, connected_count: u64, unconnected_count: u64) { self.0.as_ref().map(|metrics| { + metrics.candidate_count.with_label_values(&["connected"]).set(connected_count); metrics - .candidate_storage_count - .with_label_values(&["connected"]) - .set(connected_count) + .candidate_count + .with_label_values(&["unconnected"]) + .set(unconnected_count); }); + } + /// Record the number of candidates present in the implicit view of the subsystem. + pub fn record_candidate_count_in_implicit_view(&self, count: u64) { self.0.as_ref().map(|metrics| { - metrics - .candidate_storage_count - .with_label_values(&["unconnected"]) - .set(unconnected_count) + metrics.implicit_view_candidate_count.set(count); + }); + } + + /// Record the number of active/inactive leaves kept by the subsystem. + pub fn record_leaves_count(&self, active_count: u64, inactive_count: u64) { + self.0.as_ref().map(|metrics| { + metrics.active_leaves_count.with_label_values(&["active"]).set(active_count); + metrics.active_leaves_count.with_label_values(&["inactive"]).set(inactive_count); }); } } @@ -80,37 +97,61 @@ impl Metrics { impl metrics::Metrics for Metrics { fn try_register(registry: &prometheus::Registry) -> Result { let metrics = MetricsInner { - prune_view_candidate_storage: prometheus::register( + time_active_leaves_update: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_prospective_parachains_prune_view_candidate_storage", - "Time spent within `prospective_parachains::prune_view_candidate_storage`", + "polkadot_parachain_prospective_parachains_time_active_leaves_update", + "Time spent within `prospective_parachains::handle_active_leaves_update`", ))?, registry, )?, - introduce_seconded_candidate: prometheus::register( + time_introduce_seconded_candidate: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_prospective_parachains_introduce_seconded_candidate", + "polkadot_parachain_prospective_parachains_time_introduce_seconded_candidate", "Time spent within `prospective_parachains::handle_introduce_seconded_candidate`", ))?, registry, )?, - hypothetical_membership: prometheus::register( + time_candidate_backed: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_prospective_parachains_hypothetical_membership", + "polkadot_parachain_prospective_parachains_time_candidate_backed", + "Time spent within `prospective_parachains::handle_candidate_backed`", + ))?, + registry, + )?, + time_hypothetical_membership: prometheus::register( + prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( + "polkadot_parachain_prospective_parachains_time_hypothetical_membership", "Time spent responding to `GetHypotheticalMembership`", ))?, registry, )?, - candidate_storage_count: prometheus::register( + candidate_count: prometheus::register( GaugeVec::new( Opts::new( - "polkadot_parachain_prospective_parachains_candidate_storage_count", - "Number of candidates present in the candidate storage, split by connected and unconnected" + "polkadot_parachain_prospective_parachains_candidate_count", + "Number of candidates present across all fragment chains, split by connected and unconnected" ), &["type"], )?, registry, )?, + active_leaves_count: prometheus::register( + GaugeVec::new( + Opts::new( + "polkadot_parachain_prospective_parachains_active_leaves_count", + "Number of leaves kept by the subsystem, split by active/inactive" + ), + &["type"], + )?, + registry, + )?, + implicit_view_candidate_count: prometheus::register( + Gauge::new( + "polkadot_parachain_prospective_parachains_implicit_view_candidate_count", + "Number of candidates present in the implicit view" + )?, + registry + )?, }; Ok(Metrics(Some(metrics))) } diff --git a/polkadot/node/core/prospective-parachains/src/tests.rs b/polkadot/node/core/prospective-parachains/src/tests.rs index 221fbf4c4e60..14a093239e8e 100644 --- a/polkadot/node/core/prospective-parachains/src/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/tests.rs @@ -111,6 +111,8 @@ fn get_parent_hash(hash: Hash) -> Hash { fn test_harness>( test: impl FnOnce(VirtualOverseer) -> T, ) -> View { + sp_tracing::init_for_tests(); + let pool = sp_core::testing::TaskExecutor::new(); let (mut context, virtual_overseer) = @@ -203,6 +205,32 @@ async fn activate_leaf( activate_leaf_with_params(virtual_overseer, leaf, test_state, ASYNC_BACKING_PARAMETERS).await; } +async fn activate_leaf_with_parent_hash_fn( + virtual_overseer: &mut VirtualOverseer, + leaf: &TestLeaf, + test_state: &TestState, + parent_hash_fn: impl Fn(Hash) -> Hash, +) { + let TestLeaf { number, hash, .. } = leaf; + + let activated = new_leaf(*hash, *number); + + virtual_overseer + .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work( + activated, + )))) + .await; + + handle_leaf_activation( + virtual_overseer, + leaf, + test_state, + ASYNC_BACKING_PARAMETERS, + parent_hash_fn, + ) + .await; +} + async fn activate_leaf_with_params( virtual_overseer: &mut VirtualOverseer, leaf: &TestLeaf, @@ -219,7 +247,14 @@ async fn activate_leaf_with_params( )))) .await; - handle_leaf_activation(virtual_overseer, leaf, test_state, async_backing_params).await; + handle_leaf_activation( + virtual_overseer, + leaf, + test_state, + async_backing_params, + get_parent_hash, + ) + .await; } async fn handle_leaf_activation( @@ -227,6 +262,7 @@ async fn handle_leaf_activation( leaf: &TestLeaf, test_state: &TestState, async_backing_params: AsyncBackingParams, + parent_hash_fn: impl Fn(Hash) -> Hash, ) { let TestLeaf { number, hash, para_data } = leaf; @@ -281,7 +317,7 @@ async fn handle_leaf_activation( let min_min = para_data.iter().map(|(_, data)| data.min_relay_parent).min().unwrap_or(*number); let ancestry_len = number - min_min; let ancestry_hashes: Vec = - std::iter::successors(Some(*hash), |h| Some(get_parent_hash(*h))) + std::iter::successors(Some(*hash), |h| Some(parent_hash_fn(*h))) .skip(1) .take(ancestry_len as usize) .collect(); @@ -307,16 +343,20 @@ async fn handle_leaf_activation( ); } + let mut used_relay_parents = HashSet::new(); for (hash, number) in ancestry_iter { - send_block_header(virtual_overseer, hash, number).await; - assert_matches!( - virtual_overseer.recv().await, - AllMessages::RuntimeApi( - RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx)) - ) if parent == hash => { - tx.send(Ok(1)).unwrap(); - } - ); + if !used_relay_parents.contains(&hash) { + send_block_header(virtual_overseer, hash, number).await; + assert_matches!( + virtual_overseer.recv().await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx)) + ) if parent == hash => { + tx.send(Ok(1)).unwrap(); + } + ); + used_relay_parents.insert(hash); + } } let paras: HashSet<_> = test_state.claim_queue.values().flatten().collect(); @@ -353,12 +393,16 @@ async fn handle_leaf_activation( ); for pending in pending_availability { - send_block_header( - virtual_overseer, - pending.descriptor.relay_parent, - pending.relay_parent_number, - ) - .await; + if !used_relay_parents.contains(&pending.descriptor.relay_parent) { + send_block_header( + virtual_overseer, + pending.descriptor.relay_parent, + pending.relay_parent_number, + ) + .await; + + used_relay_parents.insert(pending.descriptor.relay_parent); + } } } @@ -513,6 +557,26 @@ async fn get_pvd( assert_eq!(resp, expected_pvd); } +macro_rules! make_and_back_candidate { + ($test_state:ident, $virtual_overseer:ident, $leaf:ident, $parent:expr, $index:expr) => {{ + let (mut candidate, pvd) = make_candidate( + $leaf.hash, + $leaf.number, + 1.into(), + $parent.commitments.head_data.clone(), + HeadData(vec![$index]), + $test_state.validation_code_hash, + ); + // Set a field to make this candidate unique. + candidate.descriptor.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; + + (candidate, candidate_hash) + }}; +} + #[test] fn should_do_no_work_if_async_backing_disabled_for_leaf() { async fn activate_leaf_async_backing_disabled(virtual_overseer: &mut VirtualOverseer) { @@ -542,7 +606,6 @@ fn should_do_no_work_if_async_backing_disabled_for_leaf() { }); assert!(view.active_leaves.is_empty()); - assert!(view.candidate_storage.is_empty()); } // Send some candidates and make sure all are found: @@ -718,10 +781,6 @@ fn introduce_candidates_basic() { }); assert_eq!(view.active_leaves.len(), 3); - assert_eq!(view.candidate_storage.len(), 2); - // Two parents and two candidates per para. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (2, 2)); } #[test] @@ -766,28 +825,36 @@ fn introduce_candidate_multiple_times() { 1.into(), Ancestors::default(), 5, - response_a, + response_a.clone(), ) .await; - // Introduce the same candidate multiple times. It'll return true but it won't be added. - // We'll check below that the candidate count remains 1. + // Introduce the same candidate multiple times. It'll return true but it will only be added + // once. for _ in 0..5 { introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) .await; } + // Check candidate tree membership. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + 1.into(), + Ancestors::default(), + 5, + response_a, + ) + .await; + virtual_overseer }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (1, 1)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } #[test] -fn fragment_chain_length_is_bounded() { +fn fragment_chain_best_chain_length_is_bounded() { let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -835,12 +902,11 @@ fn fragment_chain_length_is_bounded() { ); // Introduce candidates A and B. Since max depth is 1, only these two will be allowed. - introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) - .await; - introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b.clone()) - .await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await; - // Back candidates. Otherwise, we cannot check membership with GetBackableCandidates. + // Back candidates. Otherwise, we cannot check membership with GetBackableCandidates and + // they won't be part of the best chain. back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_b.hash()).await; @@ -855,103 +921,25 @@ fn fragment_chain_length_is_bounded() { ) .await; - // Introducing C will fail. - introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_c, pvd_c.clone()) - .await; - - virtual_overseer - }); - - assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); -} - -#[test] -fn unconnected_candidate_count_is_bounded() { - let test_state = TestState::default(); - let view = test_harness(|mut virtual_overseer| async move { - // Leaf A - let leaf_a = TestLeaf { - number: 100, - hash: Hash::from_low_u64_be(130), - para_data: vec![ - (1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))), - (2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))), - ], - }; - // Activate leaves. - activate_leaf_with_params( - &mut virtual_overseer, - &leaf_a, - &test_state, - AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 3 }, - ) - .await; - - // Candidates A, B and C are all potential candidates but don't form a chain. - let (candidate_a, pvd_a) = make_candidate( - leaf_a.hash, - leaf_a.number, - 1.into(), - HeadData(vec![1]), - HeadData(vec![2]), - test_state.validation_code_hash, - ); - let (candidate_b, pvd_b) = make_candidate( - leaf_a.hash, - leaf_a.number, - 1.into(), - HeadData(vec![3]), - HeadData(vec![4]), - test_state.validation_code_hash, - ); - let (candidate_c, pvd_c) = make_candidate( - leaf_a.hash, - leaf_a.number, - 1.into(), - HeadData(vec![4]), - HeadData(vec![5]), - test_state.validation_code_hash, - ); - - // Introduce candidates A and B. Although max depth is 1 (which should allow for two - // candidates), only 1 is allowed, because the last candidate must be a connected candidate. - introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) - .await; - introduce_seconded_candidate_failed( - &mut virtual_overseer, - candidate_b.clone(), - pvd_b.clone(), - ) - .await; - - // Back candidates. Otherwise, we cannot check membership with GetBackableCandidates. - back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await; + // Introducing C will not fail. It will be kept as unconnected storage. + introduce_seconded_candidate(&mut virtual_overseer, candidate_c.clone(), pvd_c).await; + // When being backed, candidate C will be dropped. + back_candidate(&mut virtual_overseer, &candidate_c, candidate_c.hash()).await; - // Check candidate tree membership. Should be empty. get_backable_candidates( &mut virtual_overseer, &leaf_a, 1.into(), Ancestors::default(), 5, - vec![], + vec![(candidate_a.hash(), leaf_a.hash), (candidate_b.hash(), leaf_a.hash)], ) .await; - // Introducing C will also fail. - introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_c, pvd_c.clone()) - .await; - virtual_overseer }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (1, 1)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Send some candidates, check if the candidate won't be found once its relay parent leaves the @@ -1178,7 +1166,6 @@ fn introduce_candidate_parent_leaving_view() { }); assert_eq!(view.active_leaves.len(), 0); - assert_eq!(view.candidate_storage.len(), 0); } // Introduce a candidate to multiple forks, see how the membership is returned. @@ -1249,13 +1236,12 @@ fn introduce_candidate_on_multiple_forks() { }); assert_eq!(view.active_leaves.len(), 2); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (1, 1)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } #[test] fn unconnected_candidates_become_connected() { + // This doesn't test all the complicated cases with many unconnected candidates, as it's more + // extensively tested in the `fragment_chain::tests` module. let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -1351,9 +1337,6 @@ fn unconnected_candidates_become_connected() { }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (4, 4)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Backs some candidates and tests `GetBackableCandidates` when requesting a single candidate. @@ -1435,6 +1418,10 @@ fn check_backable_query_single_candidate() { back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; + // Back an unknown candidate. It doesn't return anything but it's ignored. Will not have any + // effect on the backable candidates. + back_candidate(&mut virtual_overseer, &candidate_b, CandidateHash(Hash::random())).await; + // Should not get any backable candidates for the other para. get_backable_candidates( &mut virtual_overseer, @@ -1490,35 +1477,11 @@ fn check_backable_query_single_candidate() { }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - // Two parents and two candidates on para 1. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Backs some candidates and tests `GetBackableCandidates` when requesting a multiple candidates. #[test] fn check_backable_query_multiple_candidates() { - macro_rules! make_and_back_candidate { - ($test_state:ident, $virtual_overseer:ident, $leaf:ident, $parent:expr, $index:expr) => {{ - let (mut candidate, pvd) = make_candidate( - $leaf.hash, - $leaf.number, - 1.into(), - $parent.commitments.head_data.clone(), - HeadData(vec![$index]), - $test_state.validation_code_hash, - ); - // Set a field to make this candidate unique. - candidate.descriptor.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; - - (candidate, candidate_hash) - }}; - } - let test_state = TestState::default(); let view = test_harness(|mut virtual_overseer| async move { // Leaf A @@ -1786,10 +1749,6 @@ fn check_backable_query_multiple_candidates() { }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); - // 4 candidates on para 1. - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (4, 4)); - assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0)); } // Test hypothetical membership query. @@ -1885,11 +1844,13 @@ fn check_hypothetical_membership_query() { introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) .await; - // Get membership of candidates after adding A. C is not a potential candidate because we - // may only add one more candidate, which must be a connected candidate. - for (candidate, pvd) in - [(candidate_a.clone(), pvd_a.clone()), (candidate_b.clone(), pvd_b.clone())] - { + // Get membership of candidates after adding A. They all are still unconnected candidates + // (not part of the best backable chain). + for (candidate, pvd) in [ + (candidate_a.clone(), pvd_a.clone()), + (candidate_b.clone(), pvd_b.clone()), + (candidate_c.clone(), pvd_c.clone()), + ] { get_hypothetical_membership( &mut virtual_overseer, candidate.hash(), @@ -1900,14 +1861,24 @@ fn check_hypothetical_membership_query() { .await; } - get_hypothetical_membership( - &mut virtual_overseer, - candidate_c.hash(), - candidate_c.clone(), - pvd_c.clone(), - vec![], - ) - .await; + // Back A. Now A is part of the best chain the rest can be added as unconnected. + + back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await; + + for (candidate, pvd) in [ + (candidate_a.clone(), pvd_a.clone()), + (candidate_b.clone(), pvd_b.clone()), + (candidate_c.clone(), pvd_c.clone()), + ] { + get_hypothetical_membership( + &mut virtual_overseer, + candidate.hash(), + candidate, + pvd, + vec![leaf_a.hash, leaf_b.hash], + ) + .await; + } // Candidate D has invalid relay parent. let (candidate_d, pvd_d) = make_candidate( @@ -1920,14 +1891,17 @@ fn check_hypothetical_membership_query() { ); introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_d, pvd_d).await; - // Add candidate B. + // Add candidate B and back it. introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b.clone()) .await; + back_candidate(&mut virtual_overseer, &candidate_b, candidate_b.hash()).await; // Get membership of candidates after adding B. - for (candidate, pvd) in - [(candidate_a.clone(), pvd_a.clone()), (candidate_b.clone(), pvd_b.clone())] - { + for (candidate, pvd) in [ + (candidate_a.clone(), pvd_a.clone()), + (candidate_b.clone(), pvd_b.clone()), + (candidate_c.clone(), pvd_c.clone()), + ] { get_hypothetical_membership( &mut virtual_overseer, candidate.hash(), @@ -1938,24 +1912,10 @@ fn check_hypothetical_membership_query() { .await; } - get_hypothetical_membership( - &mut virtual_overseer, - candidate_c.hash(), - candidate_c.clone(), - pvd_c.clone(), - vec![], - ) - .await; - - // Add candidate C. It will fail because we have enough candidates for the configured depth. - introduce_seconded_candidate_failed(&mut virtual_overseer, candidate_c, pvd_c).await; - virtual_overseer }); assert_eq!(view.active_leaves.len(), 2); - assert_eq!(view.candidate_storage.len(), 2); - assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2)); } #[test] @@ -2005,6 +1965,16 @@ fn check_pvd_query() { test_state.validation_code_hash, ); + // Candidate E. + let (candidate_e, pvd_e) = make_candidate( + leaf_a.hash, + leaf_a.number, + 1.into(), + HeadData(vec![5]), + HeadData(vec![6]), + test_state.validation_code_hash, + ); + // Get pvd of candidate A before adding it. get_pvd( &mut virtual_overseer, @@ -2067,20 +2037,20 @@ fn check_pvd_query() { introduce_seconded_candidate(&mut virtual_overseer, candidate_c, pvd_c.clone()).await; // Get pvd of candidate C after adding it. - get_pvd( - &mut virtual_overseer, - 1.into(), - leaf_a.hash, - HeadData(vec![2]), - Some(pvd_c.clone()), - ) - .await; + get_pvd(&mut virtual_overseer, 1.into(), leaf_a.hash, HeadData(vec![2]), Some(pvd_c)).await; + + // Get pvd of candidate E before adding it. It won't be found, as we don't have its parent. + get_pvd(&mut virtual_overseer, 1.into(), leaf_a.hash, HeadData(vec![5]), None).await; + + // Add candidate E and check again. Should succeed this time. + introduce_seconded_candidate(&mut virtual_overseer, candidate_e, pvd_e.clone()).await; + + get_pvd(&mut virtual_overseer, 1.into(), leaf_a.hash, HeadData(vec![5]), Some(pvd_e)).await; virtual_overseer }); assert_eq!(view.active_leaves.len(), 1); - assert_eq!(view.candidate_storage.len(), 2); } // Test simultaneously activating and deactivating leaves, and simultaneously deactivating @@ -2150,6 +2120,7 @@ fn correctly_updates_leaves(#[case] runtime_api_version: u32) { &leaf_c, &test_state, ASYNC_BACKING_PARAMETERS, + get_parent_hash, ) .await; @@ -2171,13 +2142,6 @@ fn correctly_updates_leaves(#[case] runtime_api_version: u32) { virtual_overseer .send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update))) .await; - handle_leaf_activation( - &mut virtual_overseer, - &leaf_a, - &test_state, - ASYNC_BACKING_PARAMETERS, - ) - .await; // Remove the leaf again. Send some unnecessary hashes. let update = ActiveLeavesUpdate { @@ -2192,7 +2156,322 @@ fn correctly_updates_leaves(#[case] runtime_api_version: u32) { }); assert_eq!(view.active_leaves.len(), 0); - assert_eq!(view.candidate_storage.len(), 0); +} + +#[test] +fn handle_active_leaves_update_gets_candidates_from_parent() { + let para_id = ParaId::from(1); + let mut test_state = TestState::default(); + test_state.claim_queue = test_state + .claim_queue + .into_iter() + .filter(|(_, paras)| matches!(paras.front(), Some(para) if para == ¶_id)) + .collect(); + assert_eq!(test_state.claim_queue.len(), 1); + let view = test_harness(|mut virtual_overseer| async move { + // Leaf A + let leaf_a = TestLeaf { + number: 100, + hash: Hash::from_low_u64_be(130), + para_data: vec![(para_id, PerParaData::new(97, HeadData(vec![1, 2, 3])))], + }; + // Activate leaf A. + activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await; + + // Candidates A, B, C and D all form a chain + let (candidate_a, pvd_a) = make_candidate( + leaf_a.hash, + leaf_a.number, + para_id, + HeadData(vec![1, 2, 3]), + HeadData(vec![1]), + test_state.validation_code_hash, + ); + let candidate_hash_a = candidate_a.hash(); + introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; + + let (candidate_b, candidate_hash_b) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_a, 2); + let (candidate_c, candidate_hash_c) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_b, 3); + let (candidate_d, candidate_hash_d) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_c, 4); + + let mut all_candidates_resp = vec![ + (candidate_hash_a, leaf_a.hash), + (candidate_hash_b, leaf_a.hash), + (candidate_hash_c, leaf_a.hash), + (candidate_hash_d, leaf_a.hash), + ]; + + // Check candidate tree membership. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + para_id, + Ancestors::default(), + 5, + all_candidates_resp.clone(), + ) + .await; + + // Activate leaf B, which makes candidates A and B pending availability. + // Leaf B + let leaf_b = TestLeaf { + number: 101, + hash: Hash::from_low_u64_be(129), + para_data: vec![( + para_id, + PerParaData::new_with_pending( + 98, + HeadData(vec![1, 2, 3]), + vec![ + CandidatePendingAvailability { + candidate_hash: candidate_a.hash(), + descriptor: candidate_a.descriptor.clone(), + commitments: candidate_a.commitments.clone(), + relay_parent_number: leaf_a.number, + max_pov_size: MAX_POV_SIZE, + }, + CandidatePendingAvailability { + candidate_hash: candidate_b.hash(), + descriptor: candidate_b.descriptor.clone(), + commitments: candidate_b.commitments.clone(), + relay_parent_number: leaf_a.number, + max_pov_size: MAX_POV_SIZE, + }, + ], + ), + )], + }; + // Activate leaf B. + activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await; + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::default(), + 5, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![(candidate_c.hash(), leaf_a.hash), (candidate_d.hash(), leaf_a.hash)], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::default(), + 5, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + para_id, + Ancestors::default(), + 5, + all_candidates_resp.clone(), + ) + .await; + + // Now deactivate leaf A. + deactivate_leaf(&mut virtual_overseer, leaf_a.hash).await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + Ancestors::default(), + 5, + vec![], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![(candidate_c.hash(), leaf_a.hash), (candidate_d.hash(), leaf_a.hash)], + ) + .await; + + // Now add leaf C, which will be a sibling (fork) of leaf B. It should also inherit the + // candidates of leaf A (their common parent). + let leaf_c = TestLeaf { + number: 101, + hash: Hash::from_low_u64_be(12), + para_data: vec![( + para_id, + PerParaData::new_with_pending(98, HeadData(vec![1, 2, 3]), vec![]), + )], + }; + + activate_leaf_with_parent_hash_fn(&mut virtual_overseer, &leaf_c, &test_state, |hash| { + if hash == leaf_c.hash { + leaf_a.hash + } else { + get_parent_hash(hash) + } + }) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![(candidate_c.hash(), leaf_a.hash), (candidate_d.hash(), leaf_a.hash)], + ) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_c, + para_id, + Ancestors::new(), + 5, + all_candidates_resp.clone(), + ) + .await; + + // Deactivate C and add another candidate that will be present on the deactivated parent A. + // When activating C again it should also get the new candidate. Deactivated leaves are + // still updated with new candidates. + deactivate_leaf(&mut virtual_overseer, leaf_c.hash).await; + + let (candidate_e, _) = + make_and_back_candidate!(test_state, virtual_overseer, leaf_a, &candidate_d, 5); + activate_leaf_with_parent_hash_fn(&mut virtual_overseer, &leaf_c, &test_state, |hash| { + if hash == leaf_c.hash { + leaf_a.hash + } else { + get_parent_hash(hash) + } + }) + .await; + + get_backable_candidates( + &mut virtual_overseer, + &leaf_b, + para_id, + [candidate_a.hash(), candidate_b.hash()].into_iter().collect(), + 5, + vec![ + (candidate_c.hash(), leaf_a.hash), + (candidate_d.hash(), leaf_a.hash), + (candidate_e.hash(), leaf_a.hash), + ], + ) + .await; + + all_candidates_resp.push((candidate_e.hash(), leaf_a.hash)); + get_backable_candidates( + &mut virtual_overseer, + &leaf_c, + para_id, + Ancestors::new(), + 5, + all_candidates_resp, + ) + .await; + + // Querying the backable candidates for deactivated leaf won't work. + get_backable_candidates( + &mut virtual_overseer, + &leaf_a, + para_id, + Ancestors::new(), + 5, + vec![], + ) + .await; + + virtual_overseer + }); + + assert_eq!(view.active_leaves.len(), 2); + assert_eq!(view.per_relay_parent.len(), 3); +} + +#[test] +fn handle_active_leaves_update_bounded_implicit_view() { + let para_id = ParaId::from(1); + let mut test_state = TestState::default(); + test_state.claim_queue = test_state + .claim_queue + .into_iter() + .filter(|(_, paras)| matches!(paras.front(), Some(para) if para == ¶_id)) + .collect(); + assert_eq!(test_state.claim_queue.len(), 1); + + let mut leaves = vec![TestLeaf { + number: 100, + hash: Hash::from_low_u64_be(130), + para_data: vec![( + para_id, + PerParaData::new(100 - ALLOWED_ANCESTRY_LEN, HeadData(vec![1, 2, 3])), + )], + }]; + + for index in 1..10 { + let prev_leaf = &leaves[index - 1]; + leaves.push(TestLeaf { + number: prev_leaf.number - 1, + hash: get_parent_hash(prev_leaf.hash), + para_data: vec![( + para_id, + PerParaData::new( + prev_leaf.number - 1 - ALLOWED_ANCESTRY_LEN, + HeadData(vec![1, 2, 3]), + ), + )], + }); + } + leaves.reverse(); + + let view = test_harness(|mut virtual_overseer| async { + // Activate first 10 leaves. + for leaf in &leaves[0..10] { + activate_leaf(&mut virtual_overseer, leaf, &test_state).await; + } + + // Now deactivate first 9 leaves. + for leaf in &leaves[0..9] { + deactivate_leaf(&mut virtual_overseer, leaf.hash).await; + } + + virtual_overseer + }); + + // Only latest leaf is active. + assert_eq!(view.active_leaves.len(), 1); + // We keep allowed_ancestry_len implicit leaves. The latest leaf is also present here. + assert_eq!( + view.per_relay_parent.len() as u32, + ASYNC_BACKING_PARAMETERS.allowed_ancestry_len + 1 + ); + + assert_eq!(view.active_leaves, [leaves[9].hash].into_iter().collect()); + assert_eq!( + view.per_relay_parent.into_keys().collect::>(), + leaves[6..].into_iter().map(|l| l.hash).collect::>() + ); } #[test] @@ -2251,7 +2530,8 @@ fn persists_pending_availability_candidate() { ); let candidate_hash_b = candidate_b.hash(); - introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()) + .await; back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await; let candidate_a_pending_av = CandidatePendingAvailability { @@ -2275,6 +2555,15 @@ fn persists_pending_availability_candidate() { }; activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await; + get_hypothetical_membership( + &mut virtual_overseer, + candidate_hash_a, + candidate_a, + pvd_a, + vec![leaf_a.hash, leaf_b.hash], + ) + .await; + introduce_seconded_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await; back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await; diff --git a/polkadot/node/core/provisioner/src/lib.rs b/polkadot/node/core/provisioner/src/lib.rs index 3f622a60a059..ffc5859b7756 100644 --- a/polkadot/node/core/provisioner/src/lib.rs +++ b/polkadot/node/core/provisioner/src/lib.rs @@ -273,7 +273,7 @@ async fn handle_communication( let span = state.span.child("provisionable-data"); let _timer = metrics.time_provisionable_data(); - gum::trace!(target: LOG_TARGET, ?relay_parent, "Received provisionable data."); + gum::trace!(target: LOG_TARGET, ?relay_parent, "Received provisionable data: {:?}", &data); note_provisionable_data(state, &span, data); } @@ -794,9 +794,11 @@ async fn select_candidates( relay_parent: Hash, sender: &mut impl overseer::ProvisionerSenderTrait, ) -> Result, Error> { - gum::trace!(target: LOG_TARGET, + gum::trace!( + target: LOG_TARGET, leaf_hash=?relay_parent, - "before GetBackedCandidates"); + "before GetBackedCandidates" + ); let selected_candidates = match prospective_parachains_mode { ProspectiveParachainsMode::Enabled { .. } => diff --git a/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs b/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs index 97a03e6596d1..342128b7cca2 100644 --- a/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs +++ b/polkadot/node/core/pvf/benches/host_prepare_rococo_runtime.rs @@ -116,7 +116,7 @@ fn host_prepare_rococo_runtime(c: &mut Criterion) { cfg.prepare_workers_hard_max_num = 1; }) .await, - pvf.clone().code(), + pvf.clone().maybe_compressed_code(), ) }, |result| async move { diff --git a/polkadot/node/core/pvf/common/src/error.rs b/polkadot/node/core/pvf/common/src/error.rs index 7ee05448d3c5..b0cdba9501db 100644 --- a/polkadot/node/core/pvf/common/src/error.rs +++ b/polkadot/node/core/pvf/common/src/error.rs @@ -94,6 +94,10 @@ pub enum PrepareError { #[codec(index = 11)] #[error("prepare: error interfacing with the kernel: {0}")] Kernel(String), + /// Code blob failed to decompress + #[codec(index = 12)] + #[error("prepare: could not decompress code blob: {0}")] + CouldNotDecompressCodeBlob(String), } impl PrepareError { @@ -106,7 +110,11 @@ impl PrepareError { pub fn is_deterministic(&self) -> bool { use PrepareError::*; match self { - Prevalidation(_) | Preparation(_) | JobError(_) | OutOfMemory => true, + Prevalidation(_) | + Preparation(_) | + JobError(_) | + OutOfMemory | + CouldNotDecompressCodeBlob(_) => true, IoErr(_) | JobDied { .. } | CreateTmpFile(_) | diff --git a/polkadot/node/core/pvf/common/src/execute.rs b/polkadot/node/core/pvf/common/src/execute.rs index 46862f9f80b6..cff3f3b86e95 100644 --- a/polkadot/node/core/pvf/common/src/execute.rs +++ b/polkadot/node/core/pvf/common/src/execute.rs @@ -35,6 +35,8 @@ pub struct WorkerResponse { pub job_response: JobResponse, /// The amount of CPU time taken by the job. pub duration: Duration, + /// The uncompressed PoV size. + pub pov_size: u32, } /// An error occurred in the worker process. @@ -77,6 +79,8 @@ pub enum JobResponse { RuntimeConstruction(String), /// The candidate is invalid. InvalidCandidate(String), + /// PoV decompression failed + PoVDecompressionFailure, } impl JobResponse { diff --git a/polkadot/node/core/pvf/common/src/prepare.rs b/polkadot/node/core/pvf/common/src/prepare.rs index 81e165a7b8a4..4cd1beb30991 100644 --- a/polkadot/node/core/pvf/common/src/prepare.rs +++ b/polkadot/node/core/pvf/common/src/prepare.rs @@ -44,6 +44,8 @@ pub struct PrepareStats { pub cpu_time_elapsed: std::time::Duration, /// The observed memory statistics for the preparation job. pub memory_stats: MemoryStats, + /// The decompressed Wasm code length observed during the preparation. + pub observed_wasm_code_len: u32, } /// Helper struct to contain all the memory stats, including `MemoryAllocationStats` and, if diff --git a/polkadot/node/core/pvf/common/src/pvf.rs b/polkadot/node/core/pvf/common/src/pvf.rs index e2ac36a2406a..4019a8d8b0d0 100644 --- a/polkadot/node/core/pvf/common/src/pvf.rs +++ b/polkadot/node/core/pvf/common/src/pvf.rs @@ -26,9 +26,9 @@ use std::{fmt, sync::Arc, time::Duration}; /// Should be cheap to clone. #[derive(Clone, Encode, Decode)] pub struct PvfPrepData { - /// Wasm code (uncompressed) - code: Arc>, - /// Wasm code hash + /// Wasm code (maybe compressed) + maybe_compressed_code: Arc>, + /// Wasm code hash. code_hash: ValidationCodeHash, /// Executor environment parameters for the session for which artifact is prepared executor_params: Arc, @@ -46,20 +46,20 @@ impl PvfPrepData { prep_timeout: Duration, prep_kind: PrepareJobKind, ) -> Self { - let code = Arc::new(code); - let code_hash = sp_crypto_hashing::blake2_256(&code).into(); + let maybe_compressed_code = Arc::new(code); + let code_hash = sp_crypto_hashing::blake2_256(&maybe_compressed_code).into(); let executor_params = Arc::new(executor_params); - Self { code, code_hash, executor_params, prep_timeout, prep_kind } + Self { maybe_compressed_code, code_hash, executor_params, prep_timeout, prep_kind } } - /// Returns validation code hash for the PVF + /// Returns validation code hash pub fn code_hash(&self) -> ValidationCodeHash { self.code_hash } - /// Returns PVF code - pub fn code(&self) -> Arc> { - self.code.clone() + /// Returns PVF code blob + pub fn maybe_compressed_code(&self) -> Arc> { + self.maybe_compressed_code.clone() } /// Returns executor params diff --git a/polkadot/node/core/pvf/execute-worker/Cargo.toml b/polkadot/node/core/pvf/execute-worker/Cargo.toml index f24b66dc4a0e..6ad340d25612 100644 --- a/polkadot/node/core/pvf/execute-worker/Cargo.toml +++ b/polkadot/node/core/pvf/execute-worker/Cargo.toml @@ -19,8 +19,11 @@ libc = { workspace = true } codec = { features = ["derive"], workspace = true } polkadot-node-core-pvf-common = { workspace = true, default-features = true } +polkadot-node-primitives = { workspace = true, default-features = true } polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } +sp-maybe-compressed-blob = { workspace = true, default-features = true } + [features] builder = [] diff --git a/polkadot/node/core/pvf/execute-worker/src/lib.rs b/polkadot/node/core/pvf/execute-worker/src/lib.rs index 35858ab36cec..4b7c167cc9ec 100644 --- a/polkadot/node/core/pvf/execute-worker/src/lib.rs +++ b/polkadot/node/core/pvf/execute-worker/src/lib.rs @@ -22,6 +22,7 @@ pub use polkadot_node_core_pvf_common::{ error::ExecuteError, executor_interface::execute_artifact, }; +use polkadot_parachain_primitives::primitives::ValidationParams; // NOTE: Initializing logging in e.g. tests will not have an effect in the workers, as they are // separate spawned processes. Run with e.g. `RUST_LOG=parachain::pvf-execute-worker=trace`. @@ -50,8 +51,9 @@ use polkadot_node_core_pvf_common::{ }, worker_dir, }; +use polkadot_node_primitives::{BlockData, PoV, POV_BOMB_LIMIT}; use polkadot_parachain_primitives::primitives::ValidationResult; -use polkadot_primitives::ExecutorParams; +use polkadot_primitives::{ExecutorParams, PersistedValidationData}; use std::{ io::{self, Read}, os::{ @@ -85,8 +87,23 @@ fn recv_execute_handshake(stream: &mut UnixStream) -> io::Result { Ok(handshake) } -fn recv_request(stream: &mut UnixStream) -> io::Result<(Vec, Duration)> { - let params = framed_recv_blocking(stream)?; +fn recv_request(stream: &mut UnixStream) -> io::Result<(PersistedValidationData, PoV, Duration)> { + let pvd = framed_recv_blocking(stream)?; + let pvd = PersistedValidationData::decode(&mut &pvd[..]).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + "execute pvf recv_request: failed to decode persisted validation data".to_string(), + ) + })?; + + let pov = framed_recv_blocking(stream)?; + let pov = PoV::decode(&mut &pov[..]).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + "execute pvf recv_request: failed to decode PoV".to_string(), + ) + })?; + let execution_timeout = framed_recv_blocking(stream)?; let execution_timeout = Duration::decode(&mut &execution_timeout[..]).map_err(|_| { io::Error::new( @@ -94,7 +111,7 @@ fn recv_request(stream: &mut UnixStream) -> io::Result<(Vec, Duration)> { "execute pvf recv_request: failed to decode duration".to_string(), ) })?; - Ok((params, execution_timeout)) + Ok((pvd, pov, execution_timeout)) } /// Sends an error to the host and returns the original error wrapped in `io::Error`. @@ -149,7 +166,7 @@ pub fn worker_entrypoint( let execute_thread_stack_size = max_stack_size(&executor_params); loop { - let (params, execution_timeout) = recv_request(&mut stream).map_err(|e| { + let (pvd, pov, execution_timeout) = recv_request(&mut stream).map_err(|e| { map_and_send_err!( e, InternalValidationError::HostCommunication, @@ -197,7 +214,33 @@ pub fn worker_entrypoint( let stream_fd = stream.as_raw_fd(); let compiled_artifact_blob = Arc::new(compiled_artifact_blob); - let params = Arc::new(params); + + let raw_block_data = + match sp_maybe_compressed_blob::decompress(&pov.block_data.0, POV_BOMB_LIMIT) { + Ok(data) => data, + Err(_) => { + send_result::( + &mut stream, + Ok(WorkerResponse { + job_response: JobResponse::PoVDecompressionFailure, + duration: Duration::ZERO, + pov_size: 0, + }), + worker_info, + )?; + continue; + }, + }; + + let pov_size = raw_block_data.len() as u32; + + let params = ValidationParams { + parent_head: pvd.parent_head.clone(), + block_data: BlockData(raw_block_data.to_vec()), + relay_parent_number: pvd.relay_parent_number, + relay_parent_storage_root: pvd.relay_parent_storage_root, + }; + let params = Arc::new(params.encode()); cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { @@ -214,6 +257,7 @@ pub fn worker_entrypoint( worker_info, security_status.can_unshare_user_namespace_and_change_root, usage_before, + pov_size, )? } else { // Fall back to using fork. @@ -228,6 +272,7 @@ pub fn worker_entrypoint( execute_thread_stack_size, worker_info, usage_before, + pov_size, )? }; } else { @@ -242,6 +287,7 @@ pub fn worker_entrypoint( execute_thread_stack_size, worker_info, usage_before, + pov_size, )?; } } @@ -300,6 +346,7 @@ fn handle_clone( worker_info: &WorkerInfo, have_unshare_newuser: bool, usage_before: Usage, + pov_size: u32, ) -> io::Result> { use polkadot_node_core_pvf_common::worker::security; @@ -329,6 +376,7 @@ fn handle_clone( worker_info, child, usage_before, + pov_size, execution_timeout, ), Err(security::clone::Error::Clone(errno)) => @@ -347,6 +395,7 @@ fn handle_fork( execute_worker_stack_size: usize, worker_info: &WorkerInfo, usage_before: Usage, + pov_size: u32, ) -> io::Result> { // SAFETY: new process is spawned within a single threaded process. This invariant // is enforced by tests. @@ -367,6 +416,7 @@ fn handle_fork( worker_info, child, usage_before, + pov_size, execution_timeout, ), Err(errno) => Ok(Err(internal_error_from_errno("fork", errno))), @@ -513,6 +563,7 @@ fn handle_parent_process( worker_info: &WorkerInfo, job_pid: Pid, usage_before: Usage, + pov_size: u32, timeout: Duration, ) -> io::Result> { // the read end will wait until all write ends have been closed, @@ -578,7 +629,7 @@ fn handle_parent_process( )))); } - Ok(Ok(WorkerResponse { job_response, duration: cpu_tv })) + Ok(Ok(WorkerResponse { job_response, pov_size, duration: cpu_tv })) }, Err(job_error) => { gum::warn!( diff --git a/polkadot/node/core/pvf/prepare-worker/Cargo.toml b/polkadot/node/core/pvf/prepare-worker/Cargo.toml index 9e0d01fc438b..56235bd82192 100644 --- a/polkadot/node/core/pvf/prepare-worker/Cargo.toml +++ b/polkadot/node/core/pvf/prepare-worker/Cargo.toml @@ -23,10 +23,12 @@ nix = { features = ["process", "resource", "sched"], workspace = true } codec = { features = ["derive"], workspace = true } polkadot-node-core-pvf-common = { workspace = true, default-features = true } +polkadot-node-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } sc-executor-common = { workspace = true, default-features = true } sc-executor-wasmtime = { workspace = true, default-features = true } +sp-maybe-compressed-blob = { workspace = true, default-features = true } [target.'cfg(target_os = "linux")'.dependencies] tikv-jemallocator = "0.5.0" diff --git a/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs b/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs index d531c90b64b5..49b30dc33ceb 100644 --- a/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs +++ b/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs @@ -24,7 +24,11 @@ use polkadot_primitives::ExecutorParams; use std::time::Duration; fn do_prepare_runtime(pvf: PvfPrepData) { - let blob = match prevalidate(&pvf.code()) { + let maybe_compressed_code = pvf.maybe_compressed_code(); + let raw_validation_code = + sp_maybe_compressed_blob::decompress(&maybe_compressed_code, usize::MAX).unwrap(); + + let blob = match prevalidate(&raw_validation_code) { Err(err) => panic!("{:?}", err), Ok(b) => b, }; diff --git a/polkadot/node/core/pvf/prepare-worker/src/lib.rs b/polkadot/node/core/pvf/prepare-worker/src/lib.rs index ef33d11720eb..f8ebb6effcec 100644 --- a/polkadot/node/core/pvf/prepare-worker/src/lib.rs +++ b/polkadot/node/core/pvf/prepare-worker/src/lib.rs @@ -38,6 +38,7 @@ use polkadot_node_core_pvf_common::{ executor_interface::{prepare, prevalidate}, worker::{pipe2_cloexec, PipeFd, WorkerInfo}, }; +use polkadot_node_primitives::VALIDATION_CODE_BOMB_LIMIT; use codec::{Decode, Encode}; use polkadot_node_core_pvf_common::{ @@ -105,6 +106,12 @@ impl AsRef<[u8]> for CompiledArtifact { } } +#[derive(Encode, Decode)] +pub struct PrepareOutcome { + pub compiled_artifact: CompiledArtifact, + pub observed_wasm_code_len: u32, +} + /// Get a worker request. fn recv_request(stream: &mut UnixStream) -> io::Result { let pvf = framed_recv_blocking(stream)?; @@ -294,14 +301,23 @@ pub fn worker_entrypoint( ); } -fn prepare_artifact(pvf: PvfPrepData) -> Result { - let blob = match prevalidate(&pvf.code()) { +fn prepare_artifact(pvf: PvfPrepData) -> Result { + let maybe_compressed_code = pvf.maybe_compressed_code(); + let raw_validation_code = + sp_maybe_compressed_blob::decompress(&maybe_compressed_code, VALIDATION_CODE_BOMB_LIMIT) + .map_err(|e| PrepareError::CouldNotDecompressCodeBlob(e.to_string()))?; + let observed_wasm_code_len = raw_validation_code.len() as u32; + + let blob = match prevalidate(&raw_validation_code) { Err(err) => return Err(PrepareError::Prevalidation(format!("{:?}", err))), Ok(b) => b, }; match prepare(blob, &pvf.executor_params()) { - Ok(compiled_artifact) => Ok(CompiledArtifact::new(compiled_artifact)), + Ok(compiled_artifact) => Ok(PrepareOutcome { + compiled_artifact: CompiledArtifact::new(compiled_artifact), + observed_wasm_code_len, + }), Err(err) => Err(PrepareError::Preparation(format!("{:?}", err))), } } @@ -322,6 +338,7 @@ fn runtime_construction_check( struct JobResponse { artifact: CompiledArtifact, memory_stats: MemoryStats, + observed_wasm_code_len: u32, } #[cfg(target_os = "linux")] @@ -500,11 +517,11 @@ fn handle_child_process( "prepare worker", move || { #[allow(unused_mut)] - let mut result = prepare_artifact(pvf); + let mut result = prepare_artifact(pvf).map(|o| (o,)); // Get the `ru_maxrss` stat. If supported, call getrusage for the thread. #[cfg(target_os = "linux")] - let mut result = result.map(|artifact| (artifact, get_max_rss_thread())); + let mut result = result.map(|outcome| (outcome.0, get_max_rss_thread())); // If we are pre-checking, check for runtime construction errors. // @@ -513,7 +530,10 @@ fn handle_child_process( // anyway. if let PrepareJobKind::Prechecking = prepare_job_kind { result = result.and_then(|output| { - runtime_construction_check(output.0.as_ref(), &executor_params)?; + runtime_construction_check( + output.0.compiled_artifact.as_ref(), + &executor_params, + )?; Ok(output) }); } @@ -553,9 +573,9 @@ fn handle_child_process( Ok(ok) => { cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { - let (artifact, max_rss) = ok; + let (PrepareOutcome { compiled_artifact, observed_wasm_code_len }, max_rss) = ok; } else { - let artifact = ok; + let (PrepareOutcome { compiled_artifact, observed_wasm_code_len },) = ok; } } @@ -574,7 +594,11 @@ fn handle_child_process( peak_tracked_alloc: if peak_alloc > 0 { peak_alloc as u64 } else { 0u64 }, }; - Ok(JobResponse { artifact, memory_stats }) + Ok(JobResponse { + artifact: compiled_artifact, + observed_wasm_code_len, + memory_stats, + }) }, } }, @@ -665,7 +689,7 @@ fn handle_parent_process( match result { Err(err) => Err(err), - Ok(JobResponse { artifact, memory_stats }) => { + Ok(JobResponse { artifact, memory_stats, observed_wasm_code_len }) => { // The exit status should have been zero if no error occurred. if exit_status != 0 { return Err(PrepareError::JobError(format!( @@ -696,7 +720,11 @@ fn handle_parent_process( let checksum = blake3::hash(&artifact.as_ref()).to_hex().to_string(); Ok(PrepareWorkerSuccess { checksum, - stats: PrepareStats { memory_stats, cpu_time_elapsed: cpu_tv }, + stats: PrepareStats { + memory_stats, + cpu_time_elapsed: cpu_tv, + observed_wasm_code_len, + }, }) }, } diff --git a/polkadot/node/core/pvf/src/error.rs b/polkadot/node/core/pvf/src/error.rs index 8dc96305eadb..a0634106052d 100644 --- a/polkadot/node/core/pvf/src/error.rs +++ b/polkadot/node/core/pvf/src/error.rs @@ -52,6 +52,9 @@ pub enum InvalidCandidate { /// PVF execution (compilation is not included) took more time than was allotted. #[error("invalid: hard timeout")] HardTimeout, + /// Proof-of-validity failed to decompress correctly + #[error("invalid: PoV failed to decompress")] + PoVDecompressionFailure, } /// Possibly transient issue that may resolve after retries. diff --git a/polkadot/node/core/pvf/src/execute/queue.rs b/polkadot/node/core/pvf/src/execute/queue.rs index bb00a5a652d6..11031bf1074a 100644 --- a/polkadot/node/core/pvf/src/execute/queue.rs +++ b/polkadot/node/core/pvf/src/execute/queue.rs @@ -34,12 +34,14 @@ use polkadot_node_core_pvf_common::{ execute::{JobResponse, WorkerError, WorkerResponse}, SecurityStatus, }; -use polkadot_primitives::{ExecutorParams, ExecutorParamsHash}; +use polkadot_node_primitives::PoV; +use polkadot_primitives::{ExecutorParams, ExecutorParamsHash, PersistedValidationData}; use slotmap::HopSlotMap; use std::{ collections::VecDeque, fmt, path::PathBuf, + sync::Arc, time::{Duration, Instant}, }; @@ -68,7 +70,8 @@ pub enum FromQueue { #[derive(Debug)] pub struct PendingExecutionRequest { pub exec_timeout: Duration, - pub params: Vec, + pub pvd: Arc, + pub pov: Arc, pub executor_params: ExecutorParams, pub result_tx: ResultSender, } @@ -76,7 +79,8 @@ pub struct PendingExecutionRequest { struct ExecuteJob { artifact: ArtifactPathId, exec_timeout: Duration, - params: Vec, + pvd: Arc, + pov: Arc, executor_params: ExecutorParams, result_tx: ResultSender, waiting_since: Instant, @@ -293,18 +297,20 @@ async fn purge_dead(metrics: &Metrics, workers: &mut Workers) { fn handle_to_queue(queue: &mut Queue, to_queue: ToQueue) { let ToQueue::Enqueue { artifact, pending_execution_request } = to_queue; - let PendingExecutionRequest { exec_timeout, params, executor_params, result_tx } = + let PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx } = pending_execution_request; gum::debug!( target: LOG_TARGET, validation_code_hash = ?artifact.id.code_hash, "enqueueing an artifact for execution", ); + queue.metrics.observe_pov_size(pov.block_data.0.len(), true); queue.metrics.execute_enqueued(); let job = ExecuteJob { artifact, exec_timeout, - params, + pvd, + pov, executor_params, result_tx, waiting_since: Instant::now(), @@ -352,15 +358,19 @@ async fn handle_job_finish( artifact_id: ArtifactId, result_tx: ResultSender, ) { - let (idle_worker, result, duration, sync_channel) = match worker_result { + let (idle_worker, result, duration, sync_channel, pov_size) = match worker_result { Ok(WorkerInterfaceResponse { worker_response: - WorkerResponse { job_response: JobResponse::Ok { result_descriptor }, duration }, + WorkerResponse { + job_response: JobResponse::Ok { result_descriptor }, + duration, + pov_size, + }, idle_worker, }) => { // TODO: propagate the soft timeout - (Some(idle_worker), Ok(result_descriptor), Some(duration), None) + (Some(idle_worker), Ok(result_descriptor), Some(duration), None, Some(pov_size)) }, Ok(WorkerInterfaceResponse { worker_response: WorkerResponse { job_response: JobResponse::InvalidCandidate(err), .. }, @@ -370,6 +380,18 @@ async fn handle_job_finish( Err(ValidationError::Invalid(InvalidCandidate::WorkerReportedInvalid(err))), None, None, + None, + ), + Ok(WorkerInterfaceResponse { + worker_response: + WorkerResponse { job_response: JobResponse::PoVDecompressionFailure, .. }, + idle_worker, + }) => ( + Some(idle_worker), + Err(ValidationError::Invalid(InvalidCandidate::PoVDecompressionFailure)), + None, + None, + None, ), Ok(WorkerInterfaceResponse { worker_response: @@ -393,39 +415,46 @@ async fn handle_job_finish( ))), None, Some(result_rx), + None, ) }, Err(WorkerInterfaceError::InternalError(err)) | Err(WorkerInterfaceError::WorkerError(WorkerError::InternalError(err))) => - (None, Err(ValidationError::Internal(err)), None, None), + (None, Err(ValidationError::Internal(err)), None, None, None), // Either the worker or the job timed out. Kill the worker in either case. Treated as // definitely-invalid, because if we timed out, there's no time left for a retry. Err(WorkerInterfaceError::HardTimeout) | Err(WorkerInterfaceError::WorkerError(WorkerError::JobTimedOut)) => - (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None, None), + (None, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout)), None, None, None), // "Maybe invalid" errors (will retry). Err(WorkerInterfaceError::CommunicationErr(_err)) => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousWorkerDeath)), None, None, + None, ), Err(WorkerInterfaceError::WorkerError(WorkerError::JobDied { err, .. })) => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::AmbiguousJobDeath(err))), None, None, + None, ), Err(WorkerInterfaceError::WorkerError(WorkerError::JobError(err))) => ( None, Err(ValidationError::PossiblyInvalid(PossiblyInvalidError::JobError(err.to_string()))), None, None, + None, ), }; queue.metrics.execute_finished(); + if let Some(pov_size) = pov_size { + queue.metrics.observe_pov_size(pov_size as usize, false) + } if let Err(ref err) = result { gum::warn!( target: LOG_TARGET, @@ -573,7 +602,8 @@ fn assign(queue: &mut Queue, worker: Worker, job: ExecuteJob) { idle, job.artifact.clone(), job.exec_timeout, - job.params, + job.pvd, + job.pov, ) .await; QueueEvent::StartWork(worker, result, job.artifact.id, job.result_tx) diff --git a/polkadot/node/core/pvf/src/execute/worker_interface.rs b/polkadot/node/core/pvf/src/execute/worker_interface.rs index d15d7c15426e..77bd6bedd75c 100644 --- a/polkadot/node/core/pvf/src/execute/worker_interface.rs +++ b/polkadot/node/core/pvf/src/execute/worker_interface.rs @@ -32,8 +32,9 @@ use polkadot_node_core_pvf_common::{ execute::{Handshake, WorkerError, WorkerResponse}, worker_dir, SecurityStatus, }; -use polkadot_primitives::ExecutorParams; -use std::{path::Path, time::Duration}; +use polkadot_node_primitives::PoV; +use polkadot_primitives::{ExecutorParams, PersistedValidationData}; +use std::{path::Path, sync::Arc, time::Duration}; use tokio::{io, net::UnixStream}; /// Spawns a new worker with the given program path that acts as the worker and the spawn timeout. @@ -123,7 +124,8 @@ pub async fn start_work( worker: IdleWorker, artifact: ArtifactPathId, execution_timeout: Duration, - validation_params: Vec, + pvd: Arc, + pov: Arc, ) -> Result { let IdleWorker { mut stream, pid, worker_dir } = worker; @@ -137,18 +139,16 @@ pub async fn start_work( ); with_worker_dir_setup(worker_dir, pid, &artifact.path, |worker_dir| async move { - send_request(&mut stream, &validation_params, execution_timeout).await.map_err( - |error| { - gum::warn!( - target: LOG_TARGET, - worker_pid = %pid, - validation_code_hash = ?artifact.id.code_hash, - "failed to send an execute request: {}", - error, - ); - Error::InternalError(InternalValidationError::HostCommunication(error.to_string())) - }, - )?; + send_request(&mut stream, pvd, pov, execution_timeout).await.map_err(|error| { + gum::warn!( + target: LOG_TARGET, + worker_pid = %pid, + validation_code_hash = ?artifact.id.code_hash, + "failed to send an execute request: {}", + error, + ); + Error::InternalError(InternalValidationError::HostCommunication(error.to_string())) + })?; // We use a generous timeout here. This is in addition to the one in the child process, in // case the child stalls. We have a wall clock timeout here in the host, but a CPU timeout @@ -288,10 +288,12 @@ async fn send_execute_handshake(stream: &mut UnixStream, handshake: Handshake) - async fn send_request( stream: &mut UnixStream, - validation_params: &[u8], + pvd: Arc, + pov: Arc, execution_timeout: Duration, ) -> io::Result<()> { - framed_send(stream, validation_params).await?; + framed_send(stream, &pvd.encode()).await?; + framed_send(stream, &pov.encode()).await?; framed_send(stream, &execution_timeout.encode()).await } diff --git a/polkadot/node/core/pvf/src/host.rs b/polkadot/node/core/pvf/src/host.rs index 462631d33b52..44a4cba2fbf8 100644 --- a/polkadot/node/core/pvf/src/host.rs +++ b/polkadot/node/core/pvf/src/host.rs @@ -36,11 +36,14 @@ use polkadot_node_core_pvf_common::{ prepare::PrepareSuccess, pvf::PvfPrepData, }; +use polkadot_node_primitives::PoV; use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; use polkadot_parachain_primitives::primitives::ValidationResult; +use polkadot_primitives::PersistedValidationData; use std::{ collections::HashMap, path::PathBuf, + sync::Arc, time::{Duration, SystemTime}, }; @@ -108,7 +111,8 @@ impl ValidationHost { &mut self, pvf: PvfPrepData, exec_timeout: Duration, - params: Vec, + pvd: Arc, + pov: Arc, priority: Priority, result_tx: ResultSender, ) -> Result<(), String> { @@ -116,7 +120,8 @@ impl ValidationHost { .send(ToHost::ExecutePvf(ExecutePvfInputs { pvf, exec_timeout, - params, + pvd, + pov, priority, result_tx, })) @@ -147,7 +152,8 @@ enum ToHost { struct ExecutePvfInputs { pvf: PvfPrepData, exec_timeout: Duration, - params: Vec, + pvd: Arc, + pov: Arc, priority: Priority, result_tx: ResultSender, } @@ -539,7 +545,7 @@ async fn handle_execute_pvf( awaiting_prepare: &mut AwaitingPrepare, inputs: ExecutePvfInputs, ) -> Result<(), Fatal> { - let ExecutePvfInputs { pvf, exec_timeout, params, priority, result_tx } = inputs; + let ExecutePvfInputs { pvf, exec_timeout, pvd, pov, priority, result_tx } = inputs; let artifact_id = ArtifactId::from_pvf_prep_data(&pvf); let executor_params = (*pvf.executor_params()).clone(); @@ -558,7 +564,8 @@ async fn handle_execute_pvf( artifact: ArtifactPathId::new(artifact_id, path), pending_execution_request: PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -587,7 +594,8 @@ async fn handle_execute_pvf( artifact_id, PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -598,7 +606,7 @@ async fn handle_execute_pvf( ArtifactState::Preparing { .. } => { awaiting_prepare.add( artifact_id, - PendingExecutionRequest { exec_timeout, params, executor_params, result_tx }, + PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx }, ); }, ArtifactState::FailedToProcess { last_time_failed, num_failures, error } => { @@ -627,7 +635,8 @@ async fn handle_execute_pvf( artifact_id, PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -648,7 +657,7 @@ async fn handle_execute_pvf( pvf, priority, artifact_id, - PendingExecutionRequest { exec_timeout, params, executor_params, result_tx }, + PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx }, ) .await?; } @@ -770,7 +779,7 @@ async fn handle_prepare_done( // It's finally time to dispatch all the execution requests that were waiting for this artifact // to be prepared. let pending_requests = awaiting_prepare.take(&artifact_id); - for PendingExecutionRequest { exec_timeout, params, executor_params, result_tx } in + for PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx } in pending_requests { if result_tx.is_canceled() { @@ -793,7 +802,8 @@ async fn handle_prepare_done( artifact: ArtifactPathId::new(artifact_id.clone(), &path), pending_execution_request: PendingExecutionRequest { exec_timeout, - params, + pvd, + pov, executor_params, result_tx, }, @@ -967,6 +977,8 @@ pub(crate) mod tests { use assert_matches::assert_matches; use futures::future::BoxFuture; use polkadot_node_core_pvf_common::prepare::PrepareStats; + use polkadot_node_primitives::BlockData; + use sp_core::H256; const TEST_EXECUTION_TIMEOUT: Duration = Duration::from_secs(3); pub(crate) const TEST_PREPARATION_TIMEOUT: Duration = Duration::from_secs(30); @@ -1223,12 +1235,21 @@ pub(crate) mod tests { async fn execute_pvf_requests() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov1 = Arc::new(PoV { block_data: BlockData(b"pov1".to_vec()) }); + let pov2 = Arc::new(PoV { block_data: BlockData(b"pov2".to_vec()) }); let (result_tx, result_rx_pvf_1_1) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf1".to_vec(), + pvd.clone(), + pov1.clone(), Priority::Normal, result_tx, ) @@ -1239,7 +1260,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf1".to_vec(), + pvd.clone(), + pov1, Priority::Critical, result_tx, ) @@ -1250,7 +1272,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(2), TEST_EXECUTION_TIMEOUT, - b"pvf2".to_vec(), + pvd, + pov2, Priority::Normal, result_tx, ) @@ -1382,6 +1405,13 @@ pub(crate) mod tests { async fn test_prepare_done() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); // Test mixed cases of receiving execute and precheck requests // for the same PVF. @@ -1391,7 +1421,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf2".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx, ) @@ -1438,7 +1469,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(2), TEST_EXECUTION_TIMEOUT, - b"pvf2".to_vec(), + pvd, + pov, Priority::Critical, result_tx, ) @@ -1534,13 +1566,21 @@ pub(crate) mod tests { async fn test_execute_prepare_retry() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); // Submit a execute request that fails. let (result_tx, result_rx) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx, ) @@ -1570,7 +1610,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_2, ) @@ -1592,7 +1633,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_3, ) @@ -1636,13 +1678,21 @@ pub(crate) mod tests { async fn test_execute_prepare_no_retry() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); // Submit an execute request that fails. let (result_tx, result_rx) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx, ) @@ -1672,7 +1722,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_2, ) @@ -1694,7 +1745,8 @@ pub(crate) mod tests { host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf".to_vec(), + pvd.clone(), + pov.clone(), Priority::Critical, result_tx_3, ) @@ -1755,12 +1807,20 @@ pub(crate) mod tests { async fn cancellation() { let mut test = Builder::default().build(); let mut host = test.host_handle(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); let (result_tx, result_rx) = oneshot::channel(); host.execute_pvf( PvfPrepData::from_discriminator(1), TEST_EXECUTION_TIMEOUT, - b"pvf1".to_vec(), + pvd, + pov, Priority::Normal, result_tx, ) diff --git a/polkadot/node/core/pvf/src/metrics.rs b/polkadot/node/core/pvf/src/metrics.rs index bc8d300037fe..c59cab464180 100644 --- a/polkadot/node/core/pvf/src/metrics.rs +++ b/polkadot/node/core/pvf/src/metrics.rs @@ -105,6 +105,21 @@ impl Metrics { .observe((memory_stats.peak_tracked_alloc / 1024) as f64); } } + + pub(crate) fn observe_code_size(&self, code_size: usize) { + if let Some(metrics) = &self.0 { + metrics.code_size.observe(code_size as f64); + } + } + + pub(crate) fn observe_pov_size(&self, pov_size: usize, compressed: bool) { + if let Some(metrics) = &self.0 { + metrics + .pov_size + .with_label_values(&[if compressed { "true" } else { "false" }]) + .observe(pov_size as f64); + } + } } #[derive(Clone)] @@ -129,6 +144,8 @@ struct MetricsInner { preparation_max_resident: prometheus::Histogram, // Peak allocation value, tracked by tracking-allocator preparation_peak_tracked_allocation: prometheus::Histogram, + pov_size: prometheus::HistogramVec, + code_size: prometheus::Histogram, } impl metrics::Metrics for Metrics { @@ -323,6 +340,35 @@ impl metrics::Metrics for Metrics { )?, registry, )?, + // The following metrics was moved here from the candidate valiidation subsystem. + // Names are kept to avoid breaking dashboards and stuff. + pov_size: prometheus::register( + prometheus::HistogramVec::new( + prometheus::HistogramOpts::new( + "polkadot_parachain_candidate_validation_pov_size", + "The compressed and decompressed size of the proof of validity of a candidate", + ) + .buckets( + prometheus::exponential_buckets(16384.0, 2.0, 10) + .expect("arguments are always valid; qed"), + ), + &["compressed"], + )?, + registry, + )?, + code_size: prometheus::register( + prometheus::Histogram::with_opts( + prometheus::HistogramOpts::new( + "polkadot_parachain_candidate_validation_code_size", + "The size of the decompressed WASM validation blob used for checking a candidate", + ) + .buckets( + prometheus::exponential_buckets(16384.0, 2.0, 10) + .expect("arguments are always valid; qed"), + ), + )?, + registry, + )?, }; Ok(Metrics(Some(inner))) } diff --git a/polkadot/node/core/pvf/src/prepare/worker_interface.rs b/polkadot/node/core/pvf/src/prepare/worker_interface.rs index 22ee93319d84..d29d2717c4b6 100644 --- a/polkadot/node/core/pvf/src/prepare/worker_interface.rs +++ b/polkadot/node/core/pvf/src/prepare/worker_interface.rs @@ -211,7 +211,7 @@ async fn handle_response( // https://github.com/paritytech/polkadot-sdk/issues/2399 let PrepareWorkerSuccess { checksum: _, - stats: PrepareStats { cpu_time_elapsed, memory_stats }, + stats: PrepareStats { cpu_time_elapsed, memory_stats, observed_wasm_code_len }, } = match result.clone() { Ok(result) => result, // Timed out on the child. This should already be logged by the child. @@ -221,6 +221,8 @@ async fn handle_response( Err(err) => return Outcome::Concluded { worker, result: Err(err) }, }; + metrics.observe_code_size(observed_wasm_code_len as usize); + if cpu_time_elapsed > preparation_timeout { // The job didn't complete within the timeout. gum::warn!( @@ -267,7 +269,11 @@ async fn handle_response( result: Ok(PrepareSuccess { path: artifact_path, size, - stats: PrepareStats { cpu_time_elapsed, memory_stats: memory_stats.clone() }, + stats: PrepareStats { + cpu_time_elapsed, + memory_stats: memory_stats.clone(), + observed_wasm_code_len, + }, }), }, Err(err) => { diff --git a/polkadot/node/core/pvf/tests/it/adder.rs b/polkadot/node/core/pvf/tests/it/adder.rs index 455e8c36c88d..1a95a28fe077 100644 --- a/polkadot/node/core/pvf/tests/it/adder.rs +++ b/polkadot/node/core/pvf/tests/it/adder.rs @@ -18,29 +18,33 @@ use super::TestHost; use codec::{Decode, Encode}; +use polkadot_node_primitives::PoV; use polkadot_parachain_primitives::primitives::{ - BlockData as GenericBlockData, HeadData as GenericHeadData, RelayChainBlockNumber, - ValidationParams, + BlockData as GenericBlockData, HeadData as GenericHeadData, }; +use polkadot_primitives::PersistedValidationData; +use sp_core::H256; use test_parachain_adder::{hash_state, BlockData, HeadData}; #[tokio::test] async fn execute_good_block_on_parent() { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; - let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let host = TestHost::new().await; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -63,18 +67,20 @@ async fn execute_good_chain_on_parent() { for (number, add) in (0..10).enumerate() { let parent_head = HeadData { number: number as u64, parent_hash, post_state: hash_state(last_state) }; - let block_data = BlockData { state: last_state, add }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: number as RelayChainBlockNumber + 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -94,23 +100,25 @@ async fn execute_good_chain_on_parent() { #[tokio::test] async fn execute_bad_block_on_parent() { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; - let block_data = BlockData { state: 256, // start state is wrong. add: 256, }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let host = TestHost::new().await; let _err = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -124,15 +132,18 @@ async fn stress_spawn() { async fn execute(host: std::sync::Arc) { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -161,15 +172,18 @@ async fn execute_can_run_serially() { async fn execute(host: std::sync::Arc) { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await diff --git a/polkadot/node/core/pvf/tests/it/main.rs b/polkadot/node/core/pvf/tests/it/main.rs index 9ad486657512..a4a085318957 100644 --- a/polkadot/node/core/pvf/tests/it/main.rs +++ b/polkadot/node/core/pvf/tests/it/main.rs @@ -17,7 +17,6 @@ //! General PVF host integration tests checking the functionality of the PVF host itself. use assert_matches::assert_matches; -use codec::Encode as _; #[cfg(all(feature = "ci-only-tests", target_os = "linux"))] use polkadot_node_core_pvf::SecurityStatus; use polkadot_node_core_pvf::{ @@ -25,10 +24,14 @@ use polkadot_node_core_pvf::{ PossiblyInvalidError, PrepareError, PrepareJobKind, PvfPrepData, ValidationError, ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR, }; -use polkadot_parachain_primitives::primitives::{BlockData, ValidationParams, ValidationResult}; -use polkadot_primitives::{ExecutorParam, ExecutorParams, PvfExecKind, PvfPrepKind}; +use polkadot_node_primitives::{PoV, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT}; +use polkadot_parachain_primitives::primitives::{BlockData, ValidationResult}; +use polkadot_primitives::{ + ExecutorParam, ExecutorParams, PersistedValidationData, PvfExecKind, PvfPrepKind, +}; +use sp_core::H256; -use std::{io::Write, time::Duration}; +use std::{io::Write, sync::Arc, time::Duration}; use tokio::sync::Mutex; mod adder; @@ -80,9 +83,6 @@ impl TestHost { ) -> Result<(), PrepareError> { let (result_tx, result_rx) = futures::channel::oneshot::channel(); - let code = sp_maybe_compressed_blob::decompress(code, 16 * 1024 * 1024) - .expect("Compression works"); - self.host .lock() .await @@ -103,14 +103,12 @@ impl TestHost { async fn validate_candidate( &self, code: &[u8], - params: ValidationParams, + pvd: PersistedValidationData, + pov: PoV, executor_params: ExecutorParams, ) -> Result { let (result_tx, result_rx) = futures::channel::oneshot::channel(); - let code = sp_maybe_compressed_blob::decompress(code, 16 * 1024 * 1024) - .expect("Compression works"); - self.host .lock() .await @@ -122,7 +120,8 @@ impl TestHost { PrepareJobKind::Compilation, ), TEST_EXECUTION_TIMEOUT, - params.encode(), + Arc::new(pvd), + Arc::new(pov), polkadot_node_core_pvf::Priority::Normal, result_tx, ) @@ -159,19 +158,17 @@ async fn prepare_job_terminates_on_timeout() { #[tokio::test] async fn execute_job_terminates_on_timeout() { let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let start = std::time::Instant::now(); let result = host - .validate_candidate( - test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, - Default::default(), - ) + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) .await; match result { @@ -189,24 +186,23 @@ async fn execute_job_terminates_on_timeout() { async fn ensure_parallel_execution() { // Run some jobs that do not complete, thus timing out. let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let execute_pvf_future_1 = host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd.clone(), + pov.clone(), Default::default(), ); let execute_pvf_future_2 = host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ); @@ -237,6 +233,13 @@ async fn execute_queue_doesnt_stall_if_workers_died() { cfg.execute_workers_max_num = 5; }) .await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; // Here we spawn 8 validation jobs for the `halt` PVF and share those between 5 workers. The // first five jobs should timeout and the workers killed. For the next 3 jobs a new batch of @@ -245,12 +248,8 @@ async fn execute_queue_doesnt_stall_if_workers_died() { futures::future::join_all((0u8..=8).map(|_| { host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd.clone(), + pov.clone(), Default::default(), ) })) @@ -275,6 +274,13 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { cfg.execute_workers_max_num = 2; }) .await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let executor_params_1 = ExecutorParams::default(); let executor_params_2 = ExecutorParams::from(&[ExecutorParam::StackLogicalMax(1024)][..]); @@ -288,12 +294,8 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { futures::future::join_all((0u8..6).map(|i| { host.validate_candidate( test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd.clone(), + pov.clone(), match i % 3 { 0 => executor_params_1.clone(), _ => executor_params_2.clone(), @@ -324,6 +326,13 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() { async fn deleting_prepared_artifact_does_not_dispute() { let host = TestHost::new().await; let cache_dir = host.cache_dir.path(); + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let _stats = host .precheck_pvf(test_parachain_halt::wasm_binary_unwrap(), Default::default()) @@ -347,16 +356,7 @@ async fn deleting_prepared_artifact_does_not_dispute() { // Try to validate, artifact should get recreated. let result = host - .validate_candidate( - test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, - Default::default(), - ) + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) .await; assert_matches!(result, Err(ValidationError::Invalid(InvalidCandidate::HardTimeout))); @@ -367,6 +367,13 @@ async fn deleting_prepared_artifact_does_not_dispute() { async fn corrupted_prepared_artifact_does_not_dispute() { let host = TestHost::new().await; let cache_dir = host.cache_dir.path(); + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; let _stats = host .precheck_pvf(test_parachain_halt::wasm_binary_unwrap(), Default::default()) @@ -400,16 +407,7 @@ async fn corrupted_prepared_artifact_does_not_dispute() { // Try to validate, artifact should get removed because of the corruption. let result = host - .validate_candidate( - test_parachain_halt::wasm_binary_unwrap(), - ValidationParams { - block_data: BlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, - Default::default(), - ) + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) .await; assert_matches!( @@ -652,3 +650,65 @@ async fn artifact_does_reprepare_on_meaningful_exec_parameter_change() { assert_eq!(cache_dir_contents.len(), 3); // new artifact has been added } + +// Checks that we cannot prepare oversized compressed code +#[tokio::test] +async fn invalid_compressed_code_fails_prechecking() { + let host = TestHost::new().await; + let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; + let validation_code = + sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1).unwrap(); + + let res = host.precheck_pvf(&validation_code, Default::default()).await; + + assert_matches!(res, Err(PrepareError::CouldNotDecompressCodeBlob(_))); +} + +// Checks that we cannot validate with oversized compressed code +#[tokio::test] +async fn invalid_compressed_code_fails_validation() { + let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: BlockData(Vec::new()) }; + + let raw_code = vec![2u8; VALIDATION_CODE_BOMB_LIMIT + 1]; + let validation_code = + sp_maybe_compressed_blob::compress(&raw_code, VALIDATION_CODE_BOMB_LIMIT + 1).unwrap(); + + let result = host.validate_candidate(&validation_code, pvd, pov, Default::default()).await; + + assert_matches!( + result, + Err(ValidationError::Preparation(PrepareError::CouldNotDecompressCodeBlob(_))) + ); +} + +// Checks that we cannot validate with an oversized PoV +#[tokio::test] +async fn invalid_compressed_pov_fails_validation() { + let host = TestHost::new().await; + let pvd = PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let raw_block_data = vec![1u8; POV_BOMB_LIMIT + 1]; + let block_data = + sp_maybe_compressed_blob::compress(&raw_block_data, POV_BOMB_LIMIT + 1).unwrap(); + let pov = PoV { block_data: BlockData(block_data) }; + + let result = host + .validate_candidate(test_parachain_halt::wasm_binary_unwrap(), pvd, pov, Default::default()) + .await; + + assert_matches!( + result, + Err(ValidationError::Invalid(InvalidCandidate::PoVDecompressionFailure)) + ); +} diff --git a/polkadot/node/core/pvf/tests/it/process.rs b/polkadot/node/core/pvf/tests/it/process.rs index b8fd2cdce0ce..b3023c8a45c3 100644 --- a/polkadot/node/core/pvf/tests/it/process.rs +++ b/polkadot/node/core/pvf/tests/it/process.rs @@ -23,11 +23,14 @@ use codec::Encode; use polkadot_node_core_pvf::{ InvalidCandidate, PossiblyInvalidError, PrepareError, ValidationError, }; +use polkadot_node_primitives::PoV; use polkadot_parachain_primitives::primitives::{ - BlockData as GenericBlockData, HeadData as GenericHeadData, ValidationParams, + BlockData as GenericBlockData, HeadData as GenericHeadData, }; +use polkadot_primitives::PersistedValidationData; use procfs::process; use rusty_fork::rusty_fork_test; +use sp_core::H256; use std::{future::Future, sync::Arc, time::Duration}; use test_parachain_adder::{hash_state, BlockData, HeadData}; @@ -125,15 +128,18 @@ rusty_fork_test! { test_wrapper(|host, _sid| async move { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; + let pvd = PersistedValidationData { + parent_head: GenericHeadData(parent_head.encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), - ValidationParams { - parent_head: GenericHeadData(parent_head.encode()), - block_data: GenericBlockData(block_data.encode()), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ) .await @@ -166,17 +172,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let (result, _) = futures::join!( // Choose an job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Send a stop signal to pause the worker. @@ -218,17 +227,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let (result, _) = futures::join!( // Choose an job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Run a future that kills the job while it's running. @@ -274,17 +286,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let (result, _) = futures::join!( // Choose a job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Run a future that kills the job while it's running. @@ -342,17 +357,20 @@ rusty_fork_test! { // Prepare the artifact ahead of time. let binary = test_parachain_halt::wasm_binary_unwrap(); host.precheck_pvf(binary, Default::default()).await.unwrap(); + let pvd = PersistedValidationData { + parent_head: GenericHeadData(HeadData::default().encode()), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }; + let pov = PoV { block_data: GenericBlockData(Vec::new()) }; let _ = futures::join!( // Choose a job that would normally take the entire timeout. host.validate_candidate( binary, - ValidationParams { - block_data: GenericBlockData(Vec::new()), - parent_head: Default::default(), - relay_parent_number: 1, - relay_parent_storage_root: Default::default(), - }, + pvd, + pov, Default::default(), ), // Run a future that tests the thread count while the worker is running. diff --git a/polkadot/node/network/availability-distribution/src/lib.rs b/polkadot/node/network/availability-distribution/src/lib.rs index ec2c01f99b01..d3185e0af809 100644 --- a/polkadot/node/network/availability-distribution/src/lib.rs +++ b/polkadot/node/network/availability-distribution/src/lib.rs @@ -25,7 +25,7 @@ use polkadot_node_subsystem::{ jaeger, messages::AvailabilityDistributionMessage, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; -use polkadot_primitives::Hash; +use polkadot_primitives::{BlockNumber, Hash}; use std::collections::HashMap; /// Error and [`Result`] type for this subsystem. @@ -104,7 +104,7 @@ impl AvailabilityDistributionSubsystem { /// Start processing work as passed on from the Overseer. async fn run(self, mut ctx: Context) -> std::result::Result<(), FatalError> { let Self { mut runtime, recvs, metrics, req_protocol_names } = self; - let mut spans: HashMap = HashMap::new(); + let mut spans: HashMap = HashMap::new(); let IncomingRequestReceivers { pov_req_receiver, @@ -162,7 +162,7 @@ impl AvailabilityDistributionSubsystem { }; let span = jaeger::PerLeafSpan::new(cloned_leaf.span, "availability-distribution"); - spans.insert(cloned_leaf.hash, span); + spans.insert(cloned_leaf.hash, (cloned_leaf.number, span)); log_error( requester .get_mut() @@ -172,8 +172,8 @@ impl AvailabilityDistributionSubsystem { &mut warn_freq, )?; }, - FromOrchestra::Signal(OverseerSignal::BlockFinalized(hash, _)) => { - spans.remove(&hash); + FromOrchestra::Signal(OverseerSignal::BlockFinalized(_hash, finalized_number)) => { + spans.retain(|_hash, (block_number, _span)| *block_number > finalized_number); }, FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()), FromOrchestra::Communication { @@ -189,7 +189,7 @@ impl AvailabilityDistributionSubsystem { } => { let span = spans .get(&relay_parent) - .map(|span| span.child("fetch-pov")) + .map(|(_, span)| span.child("fetch-pov")) .unwrap_or_else(|| jaeger::Span::new(&relay_parent, "fetch-pov")) .with_trace_id(candidate_hash) .with_candidate(candidate_hash) diff --git a/polkadot/node/network/availability-distribution/src/requester/mod.rs b/polkadot/node/network/availability-distribution/src/requester/mod.rs index efbdceb43bdd..0175161af70d 100644 --- a/polkadot/node/network/availability-distribution/src/requester/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/mod.rs @@ -39,7 +39,9 @@ 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::{ + BlockNumber, CandidateHash, CoreIndex, Hash, OccupiedCore, SessionIndex, +}; use super::{FatalError, Metrics, Result, LOG_TARGET}; @@ -112,14 +114,14 @@ impl Requester { ctx: &mut Context, runtime: &mut RuntimeInfo, update: ActiveLeavesUpdate, - spans: &HashMap, + spans: &HashMap, ) -> Result<()> { gum::trace!(target: LOG_TARGET, ?update, "Update fetching heads"); let ActiveLeavesUpdate { activated, deactivated } = update; if let Some(leaf) = activated { let span = spans .get(&leaf.hash) - .map(|span| span.child("update-fetching-heads")) + .map(|(_, span)| span.child("update-fetching-heads")) .unwrap_or_else(|| jaeger::Span::new(&leaf.hash, "update-fetching-heads")) .with_string_tag("leaf", format!("{:?}", leaf.hash)) .with_stage(jaeger::Stage::AvailabilityDistribution); diff --git a/polkadot/node/network/availability-distribution/src/requester/tests.rs b/polkadot/node/network/availability-distribution/src/requester/tests.rs index 09567a8f87d3..decb3156004e 100644 --- a/polkadot/node/network/availability-distribution/src/requester/tests.rs +++ b/polkadot/node/network/availability-distribution/src/requester/tests.rs @@ -208,7 +208,7 @@ fn check_ancestry_lookup_in_same_session() { test_harness(test_state.clone(), |mut ctx| async move { let chain = &test_state.relay_chain; - let spans: HashMap = HashMap::new(); + let spans: HashMap = HashMap::new(); let block_number = 1; let update = ActiveLeavesUpdate { activated: Some(new_leaf(chain[block_number], block_number as u32)), @@ -281,7 +281,7 @@ fn check_ancestry_lookup_in_different_sessions() { test_harness(test_state.clone(), |mut ctx| async move { let chain = &test_state.relay_chain; - let spans: HashMap = HashMap::new(); + let spans: HashMap = HashMap::new(); let block_number = 3; let update = ActiveLeavesUpdate { activated: Some(new_leaf(chain[block_number], block_number as u32)), diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index 47d350849b20..109c29f520c5 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -2237,7 +2237,9 @@ async fn fragment_chain_update_inner( // 2. find out which are in the frontier gum::debug!( target: LOG_TARGET, - "Calling getHypotheticalMembership from statement distribution" + active_leaf_hash = ?active_leaf_hash, + "Calling getHypotheticalMembership from statement distribution for candidates: {:?}", + &hypotheticals.iter().map(|hypo| hypo.candidate_hash()).collect::>() ); let candidate_memberships = { let (tx, rx) = oneshot::channel(); diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index 4e13d5eda76f..baaff9c7c9f6 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -466,7 +466,7 @@ pub async fn forward_events>(client: Arc

, mut hand message_capacity=2048, )] pub struct Overseer { - #[subsystem(blocking, CandidateValidationMessage, sends: [ + #[subsystem(CandidateValidationMessage, sends: [ RuntimeApiMessage, ])] candidate_validation: CandidateValidation, diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 4d27ac9b70e3..d067ca468011 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -43,13 +43,13 @@ use polkadot_node_primitives::{ }; use polkadot_primitives::{ async_backing, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BackedCandidate, - BlockNumber, 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, SessionIndex, SessionInfo, - SignedAvailabilityBitfield, SignedAvailabilityBitfields, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + 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, + SessionIndex, SessionInfo, SignedAvailabilityBitfield, SignedAvailabilityBitfields, + ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use polkadot_statement_table::v2::Misbehavior; use std::{ @@ -1126,6 +1126,32 @@ impl HypotheticalCandidate { HypotheticalCandidate::Incomplete { .. } => None, } } + + /// Get the candidate commitments, if the candidate is complete. + pub fn commitments(&self) -> Option<&CandidateCommitments> { + match *self { + HypotheticalCandidate::Complete { ref receipt, .. } => Some(&receipt.commitments), + HypotheticalCandidate::Incomplete { .. } => None, + } + } + + /// Get the persisted validation data, if the candidate is complete. + pub fn persisted_validation_data(&self) -> Option<&PersistedValidationData> { + match *self { + HypotheticalCandidate::Complete { ref persisted_validation_data, .. } => + Some(persisted_validation_data), + HypotheticalCandidate::Incomplete { .. } => None, + } + } + + /// Get the validation code hash, if the candidate is complete. + pub fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + match *self { + HypotheticalCandidate::Complete { ref receipt, .. } => + Some(&receipt.descriptor.validation_code_hash), + HypotheticalCandidate::Incomplete { .. } => None, + } + } } /// Request specifying which candidates are either already included diff --git a/polkadot/node/subsystem-util/src/backing_implicit_view.rs b/polkadot/node/subsystem-util/src/backing_implicit_view.rs index 23a758d25715..a805ef8165e5 100644 --- a/polkadot/node/subsystem-util/src/backing_implicit_view.rs +++ b/polkadot/node/subsystem-util/src/backing_implicit_view.rs @@ -25,6 +25,7 @@ use polkadot_primitives::{BlockNumber, Hash, Id as ParaId}; use std::collections::HashMap; use crate::{ + inclusion_emulator::RelayChainBlockInfo, request_session_index_for_child, runtime::{self, prospective_parachains_mode, recv_runtime, ProspectiveParachainsMode}, }; @@ -121,6 +122,26 @@ struct BlockInfo { parent_hash: Hash, } +/// Information about a relay-chain block, to be used when calling this module from prospective +/// parachains. +#[derive(Debug, Clone, PartialEq)] +pub struct BlockInfoProspectiveParachains { + /// The hash of the relay-chain block. + pub hash: Hash, + /// The hash of the parent relay-chain block. + pub parent_hash: Hash, + /// The number of the relay-chain block. + pub number: BlockNumber, + /// The storage-root of the relay-chain block. + pub storage_root: Hash, +} + +impl From for RelayChainBlockInfo { + fn from(value: BlockInfoProspectiveParachains) -> Self { + Self { hash: value.hash, number: value.number, storage_root: value.storage_root } + } +} + impl View { /// Get an iterator over active leaves in the view. pub fn leaves(&self) -> impl Iterator { @@ -178,6 +199,61 @@ impl View { } } + /// Activate a leaf in the view. To be used by the prospective parachains subsystem. + /// + /// This will not request any additional data, as prospective parachains already provides all + /// the required info. + /// NOTE: using `activate_leaf` instead of this function will result in a + /// deadlock, as it calls prospective-parachains under the hood. + /// + /// No-op for known leaves. + pub fn activate_leaf_from_prospective_parachains( + &mut self, + leaf: BlockInfoProspectiveParachains, + ancestors: &[BlockInfoProspectiveParachains], + ) { + if self.leaves.contains_key(&leaf.hash) { + return + } + + // Retain at least `MINIMUM_RETAIN_LENGTH` blocks in storage. + // This helps to avoid Chain API calls when activating leaves in the + // same chain. + let retain_minimum = std::cmp::min( + ancestors.last().map(|a| a.number).unwrap_or(0), + leaf.number.saturating_sub(MINIMUM_RETAIN_LENGTH), + ); + + self.leaves.insert(leaf.hash, ActiveLeafPruningInfo { retain_minimum }); + let mut allowed_relay_parents = AllowedRelayParents { + allowed_relay_parents_contiguous: Vec::with_capacity(ancestors.len()), + // In this case, initialise this to an empty map, as prospective parachains already has + // this data and it won't query the implicit view for it. + minimum_relay_parents: HashMap::new(), + }; + + for ancestor in ancestors { + self.block_info_storage.insert( + ancestor.hash, + BlockInfo { + block_number: ancestor.number, + maybe_allowed_relay_parents: None, + parent_hash: ancestor.parent_hash, + }, + ); + allowed_relay_parents.allowed_relay_parents_contiguous.push(ancestor.hash); + } + + self.block_info_storage.insert( + leaf.hash, + BlockInfo { + block_number: leaf.number, + maybe_allowed_relay_parents: Some(allowed_relay_parents), + parent_hash: leaf.parent_hash, + }, + ); + } + /// Deactivate a leaf in the view. This prunes any outdated implicit ancestors as well. /// /// Returns hashes of blocks pruned from storage. diff --git a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs index 2272f048089c..0c3b40743495 100644 --- a/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs +++ b/polkadot/node/subsystem-util/src/inclusion_emulator/mod.rs @@ -39,8 +39,8 @@ /// /// # Usage /// -/// It's expected that the users of this module will be building up chains of -/// [`Fragment`]s and consistently pruning and adding to the chains. +/// It's expected that the users of this module will be building up chains or trees of +/// [`Fragment`]s and consistently pruning and adding to them. /// /// ## Operating Constraints /// @@ -56,55 +56,19 @@ /// /// ## Fragment Chains /// -/// For simplicity and practicality, we expect that collators of the same parachain are -/// cooperating and don't create parachain forks or cycles on the same relay chain active leaf. -/// Therefore, higher-level code should maintain one fragment chain for each active leaf (not a -/// fragment tree). If parachains do create forks, their performance in regards to async -/// backing and elastic scaling will suffer, because different validators will have different -/// predictions of the future. +/// For the sake of this module, we don't care how higher-level code is managing parachain +/// fragments, whether or not they're kept as a chain or tree. In reality, +/// prospective-parachains is maintaining for every active leaf, a chain of the "best" backable +/// candidates and a storage of potential candidates which may be added to this chain in the +/// future. /// /// As the relay-chain grows, some predictions come true and others come false. -/// And new predictions get made. These three changes correspond distinctly to the -/// 3 primary operations on fragment chains. +/// And new predictions get made. Higher-level code is responsible for adding and pruning the +/// fragments chains. /// /// Avoiding fragment-chain blowup is beyond the scope of this module. Higher-level must ensure /// proper spam protection. /// -/// ### Pruning Fragment Chains -/// -/// When the relay-chain advances, we want to compare the new constraints of that relay-parent -/// to the root of the fragment chain we have. There are 3 cases: -/// -/// 1. The root fragment is still valid under the new constraints. In this case, we do nothing. -/// This is the "prediction still uncertain" case. (Corresponds to some candidates still -/// being pending availability). -/// -/// 2. The root fragment (potentially along with a number of descendants) is invalid under the -/// new constraints because it has been included by the relay-chain. In this case, we can -/// discard the included chain and split & re-root the chain under its descendants and -/// compare to the new constraints again. This is the "prediction came true" case. -/// -/// 3. The root fragment becomes invalid under the new constraints for any reason (if for -/// example the parachain produced a fork and the block producer picked a different -/// candidate to back). In this case we can discard the entire fragment chain. This is the -/// "prediction came false" case. -/// -/// This is all a bit of a simplification because it assumes that the relay-chain advances -/// without forks and is finalized instantly. In practice, the set of fragment-chains needs to -/// be observable from the perspective of a few different possible forks of the relay-chain and -/// not pruned too eagerly. -/// -/// Note that the fragments themselves don't need to change and the only thing we care about -/// is whether the predictions they represent are still valid. -/// -/// ### Extending Fragment Chains -/// -/// As predictions fade into the past, new ones should be stacked on top. -/// -/// Every new relay-chain block is an opportunity to make a new prediction about the future. -/// Higher-level logic should decide whether to build upon an existing chain or whether -/// to create a new fragment-chain. -/// /// ### Code Upgrades /// /// Code upgrades are the main place where this emulation fails. The on-chain PVF upgrade @@ -116,9 +80,11 @@ /// /// That means a few blocks of execution time lost, which is not a big deal for code upgrades /// in practice at most once every few weeks. +use polkadot_node_subsystem::messages::HypotheticalCandidate; use polkadot_primitives::{ - async_backing::Constraints as PrimitiveConstraints, BlockNumber, CandidateCommitments, Hash, - HeadData, Id as ParaId, PersistedValidationData, UpgradeRestriction, ValidationCodeHash, + async_backing::Constraints as PrimitiveConstraints, BlockNumber, CandidateCommitments, + CandidateHash, Hash, HeadData, Id as ParaId, PersistedValidationData, UpgradeRestriction, + ValidationCodeHash, }; use std::{collections::HashMap, sync::Arc}; @@ -702,6 +668,11 @@ impl Fragment { &self.candidate } + /// Get a cheap ref-counted copy of the underlying prospective candidate. + pub fn candidate_clone(&self) -> Arc { + self.candidate.clone() + } + /// Modifications to constraints based on the outputs of the candidate. pub fn constraint_modifications(&self) -> &ConstraintModifications { &self.modifications @@ -791,6 +762,55 @@ fn validate_against_constraints( .map_err(FragmentValidityError::OutputsInvalid) } +/// Trait for a hypothetical or concrete candidate, as needed when assessing the validity of a +/// potential candidate. +pub trait HypotheticalOrConcreteCandidate { + /// Return a reference to the candidate commitments, if present. + fn commitments(&self) -> Option<&CandidateCommitments>; + /// 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>; + /// Return the parent head hash. + fn parent_head_data_hash(&self) -> Hash; + /// Return the output head hash, if present. + fn output_head_data_hash(&self) -> Option; + /// Return the relay parent hash. + fn relay_parent(&self) -> Hash; + /// Return the candidate hash. + fn candidate_hash(&self) -> CandidateHash; +} + +impl HypotheticalOrConcreteCandidate for HypotheticalCandidate { + fn commitments(&self) -> Option<&CandidateCommitments> { + self.commitments() + } + + fn persisted_validation_data(&self) -> Option<&PersistedValidationData> { + self.persisted_validation_data() + } + + fn validation_code_hash(&self) -> Option<&ValidationCodeHash> { + self.validation_code_hash() + } + + fn parent_head_data_hash(&self) -> Hash { + self.parent_head_data_hash() + } + + fn output_head_data_hash(&self) -> Option { + self.output_head_data_hash() + } + + fn relay_parent(&self) -> Hash { + self.relay_parent() + } + + fn candidate_hash(&self) -> CandidateHash { + self.candidate_hash() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 60c40429b1ac..7d743b209124 100644 --- a/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/polkadot/runtime/rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-nbnwcyh-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -55,8 +55,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 23_189_000 picoseconds. - Weight::from_parts(23_896_000, 3593) + // Minimum execution time: 30_672_000 picoseconds. + Weight::from_parts(31_677_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,8 +66,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 50_299_000 picoseconds. - Weight::from_parts(50_962_000, 6196) + // Minimum execution time: 41_132_000 picoseconds. + Weight::from_parts(41_654_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -83,10 +83,10 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `243` + // Measured: `281` // Estimated: `6196` - // Minimum execution time: 71_748_000 picoseconds. - Weight::from_parts(74_072_000, 6196) + // Minimum execution time: 97_174_000 picoseconds. + Weight::from_parts(99_537_000, 6196) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -105,16 +105,18 @@ impl WeightInfo { /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 27_806_000 picoseconds. - Weight::from_parts(28_594_000, 3607) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `281` + // Estimated: `3746` + // Minimum execution time: 67_105_000 picoseconds. + Weight::from_parts(68_659_000, 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`) @@ -122,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 21_199_000 picoseconds. - Weight::from_parts(21_857_000, 3593) + // Minimum execution time: 30_780_000 picoseconds. + Weight::from_parts(31_496_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -133,27 +135,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 23_578_000 picoseconds. - Weight::from_parts(24_060_000, 3593) + // Minimum execution time: 23_411_000 picoseconds. + Weight::from_parts(23_891_000, 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`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 48_522_000 picoseconds. - Weight::from_parts(49_640_000, 3607) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 61_541_000 picoseconds. + Weight::from_parts(63_677_000, 3645) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -169,10 +171,10 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 50_429_000 picoseconds. - Weight::from_parts(51_295_000, 3607) + // Measured: `180` + // Estimated: `3645` + // Minimum execution time: 48_574_000 picoseconds. + Weight::from_parts(49_469_000, 3645) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 9939f16aa29f..e0c61c8e2bf2 100644 --- a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-nbnwcyh-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 // Executed Command: @@ -55,8 +55,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 24_815_000 picoseconds. - Weight::from_parts(25_098_000, 3593) + // Minimum execution time: 31_780_000 picoseconds. + Weight::from_parts(32_602_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,12 +66,12 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 51_268_000 picoseconds. - Weight::from_parts(51_857_000, 6196) + // Minimum execution time: 41_818_000 picoseconds. + Weight::from_parts(42_902_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } - /// Storage: `System::Account` (r:2 w:2) + /// Storage: `System::Account` (r:3 w:3) /// 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`) @@ -83,12 +83,12 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn transfer_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `210` - // Estimated: `6196` - // Minimum execution time: 74_113_000 picoseconds. - Weight::from_parts(74_721_000, 6196) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(4)) + // Measured: `351` + // Estimated: `8799` + // Minimum execution time: 101_949_000 picoseconds. + Weight::from_parts(104_190_000, 8799) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `Benchmark::Override` (r:0 w:0) /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -105,16 +105,18 @@ impl WeightInfo { /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_reserve_withdraw() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3574` - // Minimum execution time: 28_919_000 picoseconds. - Weight::from_parts(29_703_000, 3574) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)) + // Measured: `351` + // Estimated: `6196` + // Minimum execution time: 70_123_000 picoseconds. + Weight::from_parts(72_564_000, 6196) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -122,8 +124,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 21_685_000 picoseconds. - Weight::from_parts(22_528_000, 3593) + // Minimum execution time: 31_868_000 picoseconds. + Weight::from_parts(32_388_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -133,27 +135,27 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 25_192_000 picoseconds. - Weight::from_parts(25_445_000, 3593) + // Minimum execution time: 24_532_000 picoseconds. + Weight::from_parts(25_166_000, 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`) /// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0) /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `XcmPallet::SupportedVersion` (r:1 w:0) /// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1) /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn deposit_reserve_asset() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3593` - // Minimum execution time: 49_349_000 picoseconds. - Weight::from_parts(50_476_000, 3593) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 63_378_000 picoseconds. + Weight::from_parts(65_002_000, 3612) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -169,10 +171,10 @@ impl WeightInfo { /// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`) pub(crate) fn initiate_teleport() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `3593` - // Minimum execution time: 51_386_000 picoseconds. - Weight::from_parts(52_141_000, 3593) + // Measured: `147` + // Estimated: `3612` + // Minimum execution time: 49_174_000 picoseconds. + Weight::from_parts(50_356_000, 3612) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 1daf5ae750cf..5101a97dc842 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -858,14 +858,7 @@ impl XcmExecutor { let old_holding = self.holding.clone(); let result = Config::TransactionalProcessor::process(|| { let deposited = self.holding.saturating_take(assets); - for asset in deposited.into_assets_iter() { - Config::AssetTransactor::deposit_asset( - &asset, - &beneficiary, - Some(&self.context), - )?; - } - Ok(()) + self.deposit_assets_with_retry(&deposited, &beneficiary) }); if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { self.holding = old_holding; @@ -890,9 +883,7 @@ impl XcmExecutor { // now take assets to deposit (excluding transport_fee) let deposited = self.holding.saturating_take(assets); - for asset in deposited.assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &dest, Some(&self.context))?; - } + self.deposit_assets_with_retry(&deposited, &dest)?; // Note that we pass `None` as `maybe_failed_bin` and drop any assets which // cannot be reanchored because we have already called `deposit_asset` on all // assets. @@ -1282,4 +1273,46 @@ impl XcmExecutor { }), } } + + /// Deposit `to_deposit` assets to `beneficiary`, without giving up on the first (transient) + /// error, and retrying once just in case one of the subsequently deposited assets satisfy some + /// requirement. + /// + /// Most common transient error is: `beneficiary` account does not yet exist and the first + /// asset(s) in the (sorted) list does not satisfy ED, but a subsequent one in the list does. + /// + /// This function can write into storage and also return an error at the same time, it should + /// always be called within a transactional context. + fn deposit_assets_with_retry( + &mut self, + to_deposit: &AssetsInHolding, + beneficiary: &Location, + ) -> Result<(), XcmError> { + let mut failed_deposits = Vec::with_capacity(to_deposit.len()); + + let mut deposit_result = Ok(()); + for asset in to_deposit.assets_iter() { + deposit_result = + Config::AssetTransactor::deposit_asset(&asset, &beneficiary, Some(&self.context)); + // if deposit failed for asset, mark it for retry after depositing the others. + if deposit_result.is_err() { + failed_deposits.push(asset); + } + } + if failed_deposits.len() == to_deposit.len() { + tracing::debug!( + target: "xcm::execute", + ?deposit_result, + "Deposit for each asset failed, returning the last error as there is no point in retrying any of them", + ); + return deposit_result; + } + tracing::trace!(target: "xcm::execute", ?failed_deposits, "Deposits to retry"); + + // retry previously failed deposits, this time short-circuiting on any error. + for asset in failed_deposits { + Config::AssetTransactor::deposit_asset(&asset, &beneficiary, Some(&self.context))?; + } + Ok(()) + } } diff --git a/prdoc/pr_4460.prdoc b/prdoc/pr_4460.prdoc new file mode 100644 index 000000000000..81636c3313fc --- /dev/null +++ b/prdoc/pr_4460.prdoc @@ -0,0 +1,24 @@ +# 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: "xcm-executor: allow deposit of multiple assets if at least one of them satisfies ED" + +doc: + - audience: Runtime Dev + description: | + XCM programs that deposit assets to some new (empty) account will now succeed if at least + one of the deposited assets satisfies ED. Before this change, the requirement was that the + _first_ asset had to satisfy ED, but assets order can be changed during reanchoring so it + is not reliable. Now, ordering doesn't matter, any one(s) of them can satisfy ED for the + whole deposit to work. + - audience: Runtime User + description: | + XCM programs that deposit assets to some new (empty) account will now succeed if at least + one of the deposited assets satisfies ED. Before this change, the requirement was that the + _first_ asset had to satisfy ED, but assets order can be changed during reanchoring so it + is not reliable. Now, ordering doesn't matter, any one(s) of them can satisfy ED for the + whole deposit to work. + +crates: + - name: staging-xcm-executor + bump: patch diff --git a/prdoc/pr_4937.prdoc b/prdoc/pr_4937.prdoc new file mode 100644 index 000000000000..37b7bc3dda59 --- /dev/null +++ b/prdoc/pr_4937.prdoc @@ -0,0 +1,21 @@ +title: "prospective-parachains rework: take II" + +doc: + - audience: Node Dev + description: | + Add back support for backing parachain forks. Once a candidate reaches the backing quorum, + validators use a shared way of picking the winning fork to back on-chain. This was done in + order to increase the likelihood that all backers will vote on the winning fork. + The functionality of backing unconnected candidates introduced by the previous rework is preserved. + +crates: + - name: polkadot-node-core-prospective-parachains + bump: minor + - name: polkadot-node-subsystem-types + bump: minor + - name: polkadot-node-subsystem-util + bump: minor + - name: polkadot-node-core-provisioner + bump: none + - name: polkadot-statement-distribution + bump: none diff --git a/prdoc/pr_5142.prdoc b/prdoc/pr_5142.prdoc new file mode 100644 index 000000000000..4083e5bf53cd --- /dev/null +++ b/prdoc/pr_5142.prdoc @@ -0,0 +1,26 @@ +# 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: "Move decompression to worker processes" + +doc: + - audience: Node Dev + description: | + Candidate validation subsystem performed the PVF code decompression as well as the PoV + decompression itself which might affect the subsystem main loop performance and required + it to run on the blocking threadpool. This change moves the decompression to PVF host + workers running synchronously in separate processes. + +crates: + - name: polkadot-node-core-candidate-validation + bump: patch + - name: polkadot-overseer + bump: patch + - name: polkadot-node-core-pvf + bump: major + - name: polkadot-node-core-pvf-common + bump: major + - name: polkadot-node-core-pvf-execute-worker + bump: patch + - name: polkadot-node-core-pvf-prepare-worker + bump: patch diff --git a/prdoc/pr_5273.prdoc b/prdoc/pr_5273.prdoc new file mode 100644 index 000000000000..981172c6c13f --- /dev/null +++ b/prdoc/pr_5273.prdoc @@ -0,0 +1,10 @@ +title: Fix storage weight reclaim bug. + +doc: + - audience: Runtime Dev + description: | + A bug in storage weight reclaim signed extension is fixed. The bug was causing an underestimate of the proof size when the post dispatch info was underestimating the proof size and the pre dispatch info was overestimating the proof size at the same time. + +crates: + - name: cumulus-primitives-storage-weight-reclaim + bump: patch diff --git a/prdoc/pr_5321.prdoc b/prdoc/pr_5321.prdoc new file mode 100644 index 000000000000..97f75d28dd52 --- /dev/null +++ b/prdoc/pr_5321.prdoc @@ -0,0 +1,11 @@ +title: fix availability-distribution Jaeger spans memory leak + +doc: + - audience: Node Dev + description: | + Fixes a memory leak which caused the Jaeger span storage in availability-distribution to never be pruned and therefore increasing indefinitely. + This was caused by improper handling of finalized heads. More info in https://github.com/paritytech/polkadot-sdk/issues/5258 + +crates: + - name: polkadot-availability-distribution + bump: patch diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index c43f9e89b8a9..5451428d3481 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -172,6 +172,12 @@ //! //! //! +//! The main purpose of the `RuntimeGenesisConfig` patch is to: +//! - minimize the maintenance effort when RuntimeGenesisConfig is changed in the future (e.g. new +//! pallets added to the runtime or pallet's genesis config changed), +//! - increase the readability - it only contains the relevant fields, +//! - allow to apply numerous changes in distinct domains (e.g. for zombienet). +//! //! For production or long-lasting blockchains, using the `raw` format in the chain specification is //! recommended. Only the `raw` format guarantees that storage root hash will remain unchanged when //! the `RuntimeGenesisConfig` format changes due to software upgrade. diff --git a/substrate/client/consensus/common/src/import_queue.rs b/substrate/client/consensus/common/src/import_queue.rs index 35fc8ad4a402..1baa67398a49 100644 --- a/substrate/client/consensus/common/src/import_queue.rs +++ b/substrate/client/consensus/common/src/import_queue.rs @@ -225,7 +225,7 @@ pub async fn import_single_block>( import_handle: &mut impl BlockImport, block_origin: BlockOrigin, block: IncomingBlock, - verifier: &mut V, + verifier: &V, ) -> BlockImportResult { match verify_single_block_metered(import_handle, block_origin, block, verifier, None).await? { SingleBlockVerificationOutcome::Imported(import_status) => Ok(import_status), @@ -295,7 +295,7 @@ pub(crate) async fn verify_single_block_metered>( import_handle: &impl BlockImport, block_origin: BlockOrigin, block: IncomingBlock, - verifier: &mut V, + verifier: &V, metrics: Option<&Metrics>, ) -> Result, BlockImportError> { let peer = block.origin; diff --git a/substrate/client/consensus/common/src/import_queue/basic_queue.rs b/substrate/client/consensus/common/src/import_queue/basic_queue.rs index 05f2b2527961..6a307acb2517 100644 --- a/substrate/client/consensus/common/src/import_queue/basic_queue.rs +++ b/substrate/client/consensus/common/src/import_queue/basic_queue.rs @@ -222,7 +222,7 @@ mod worker_messages { /// Returns when `block_import` ended. async fn block_import_process( mut block_import: BoxBlockImport, - mut verifier: impl Verifier, + verifier: impl Verifier, mut result_sender: BufferedLinkSender, mut block_import_receiver: TracingUnboundedReceiver>, metrics: Option, @@ -241,8 +241,7 @@ async fn block_import_process( }; let res = - import_many_blocks(&mut block_import, origin, blocks, &mut verifier, metrics.clone()) - .await; + import_many_blocks(&mut block_import, origin, blocks, &verifier, metrics.clone()).await; result_sender.blocks_processed(res.imported, res.block_count, res.results); } @@ -388,7 +387,7 @@ async fn import_many_blocks>( import_handle: &mut BoxBlockImport, blocks_origin: BlockOrigin, blocks: Vec>, - verifier: &mut V, + verifier: &V, metrics: Option, ) -> ImportManyBlocksResult { let count = blocks.len(); diff --git a/substrate/client/network/test/src/block_import.rs b/substrate/client/network/test/src/block_import.rs index 690a579e0272..dad5a27e9535 100644 --- a/substrate/client/network/test/src/block_import.rs +++ b/substrate/client/network/test/src/block_import.rs @@ -78,7 +78,7 @@ fn import_single_good_block_works() { &mut substrate_test_runtime_client::new(), BlockOrigin::File, block, - &mut PassThroughVerifier::new(true), + &PassThroughVerifier::new(true), )) { Ok(BlockImportStatus::ImportedUnknown(ref num, ref aux, ref org)) if *num == number && *aux == expected_aux && *org == Some(peer_id.into()) => {}, @@ -93,7 +93,7 @@ fn import_single_good_known_block_is_ignored() { &mut client, BlockOrigin::File, block, - &mut PassThroughVerifier::new(true), + &PassThroughVerifier::new(true), )) { Ok(BlockImportStatus::ImportedKnown(ref n, _)) if *n == number => {}, _ => panic!(), @@ -108,7 +108,7 @@ fn import_single_good_block_without_header_fails() { &mut substrate_test_runtime_client::new(), BlockOrigin::File, block, - &mut PassThroughVerifier::new(true), + &PassThroughVerifier::new(true), )) { Err(BlockImportError::IncompleteHeader(ref org)) if *org == Some(peer_id.into()) => {}, _ => panic!(), diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index e9b9a7b1e3cc..6f8ad0c29393 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -1775,7 +1775,7 @@ pub mod pallet { /// (false), or transfer everything except at least the minimum balance, which will /// guarantee to keep the sender asset account alive (true). #[pallet::call_index(32)] - #[pallet::weight(T::WeightInfo::refund_other())] + #[pallet::weight(T::WeightInfo::transfer_all())] pub fn transfer_all( origin: OriginFor, id: T::AssetIdParameter, diff --git a/templates/minimal/README.md b/templates/minimal/README.md index b556a4536089..180c229e744e 100644 --- a/templates/minimal/README.md +++ b/templates/minimal/README.md @@ -42,7 +42,7 @@ packages required to compile this template - please take note of the Rust compil 🔨 Use the following command to build the node without launching it: ```sh -cargo build --package minimal-template-node --release +cargo build --release ``` 🐳 Alternatively, build the docker image: diff --git a/templates/minimal/node/Cargo.toml b/templates/minimal/node/Cargo.toml index 70b24c19f8e7..da5073ea687c 100644 --- a/templates/minimal/node/Cargo.toml +++ b/templates/minimal/node/Cargo.toml @@ -57,4 +57,7 @@ minimal-template-runtime = { workspace = true } substrate-build-script-utils = { workspace = true, default-features = true } [features] -default = [] +default = ["std"] +std = [ + "minimal-template-runtime/std", +] diff --git a/templates/parachain/README.md b/templates/parachain/README.md index 802d8586b39e..b7fa2bed0ed9 100644 --- a/templates/parachain/README.md +++ b/templates/parachain/README.md @@ -44,7 +44,7 @@ packages required to compile this template - please take note of the Rust compil 🔨 Use the following command to build the node without launching it: ```sh -cargo build --package parachain-template-node --release +cargo build --release ``` 🐳 Alternatively, build the docker image: diff --git a/templates/parachain/node/Cargo.toml b/templates/parachain/node/Cargo.toml index 7cf1f1fddc7b..c782888a3e89 100644 --- a/templates/parachain/node/Cargo.toml +++ b/templates/parachain/node/Cargo.toml @@ -79,7 +79,12 @@ color-print = { workspace = true } substrate-build-script-utils = { workspace = true, default-features = true } [features] -default = [] +default = ["std"] +std = [ + "log/std", + "parachain-template-runtime/std", + "xcm/std", +] runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "frame-benchmarking-cli/runtime-benchmarks", diff --git a/templates/solochain/README.md b/templates/solochain/README.md index c5dc5db7f3b5..6a5a7853f9c0 100644 --- a/templates/solochain/README.md +++ b/templates/solochain/README.md @@ -28,7 +28,7 @@ installation](#alternatives-installations) options. Use the following command to build the node without launching it: ```sh -cargo build --package solochain-template-node --release +cargo build --release ``` ### Embedded Docs diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml index 6eebf3694e3a..9dd1b144d229 100644 --- a/templates/solochain/node/Cargo.toml +++ b/templates/solochain/node/Cargo.toml @@ -66,7 +66,10 @@ solochain-template-runtime = { workspace = true } substrate-build-script-utils = { workspace = true, default-features = true } [features] -default = [] +default = ["std"] +std = [ + "solochain-template-runtime/std", +] # Dependencies that are only required if runtime benchmarking should be build. runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks",