diff --git a/.github/workflows/build-publish-images.yml b/.github/workflows/build-publish-images.yml index 3a9f60761863..874b5d37469c 100644 --- a/.github/workflows/build-publish-images.yml +++ b/.github/workflows/build-publish-images.yml @@ -15,7 +15,6 @@ env: COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} jobs: - # # # @@ -24,7 +23,7 @@ jobs: if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') uses: ./.github/workflows/reusable-preflight.yml -### Build ######################## + ### Build ######################## # # @@ -240,7 +239,7 @@ jobs: with: # tldr: we need to checkout the branch HEAD explicitly because of our dynamic versioning approach while building the substrate binary # see https://github.com/paritytech/ci_cd/issues/682#issuecomment-1340953589 - ref: ${{ github.head_ref || github.ref_name }} + ref: ${{ github.head_ref || github.ref_name }} - name: build run: | mkdir -p ./artifacts/substrate/ @@ -299,14 +298,14 @@ jobs: path: artifacts.tar retention-days: 1 -### Publish ######################## + ### Publish ######################## # # # build-push-image-test-parachain: needs: [preflight, build-test-parachain] - runs-on: arc-runners-polkadot-sdk + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} timeout-minutes: 60 steps: - name: Checkout @@ -330,7 +329,7 @@ jobs: # build-push-image-polkadot-debug: needs: [preflight, build-linux-stable] - runs-on: arc-runners-polkadot-sdk + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} timeout-minutes: 60 steps: - name: Checkout @@ -349,13 +348,12 @@ jobs: image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/polkadot-debug" dockerfile: "docker/dockerfiles/polkadot/polkadot_injected_debug.Dockerfile" - # # # build-push-image-colander: needs: [preflight, build-test-collators] - runs-on: arc-runners-polkadot-sdk + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} timeout-minutes: 60 steps: - name: Checkout @@ -374,13 +372,12 @@ jobs: image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/colander" dockerfile: "docker/dockerfiles/collator_injected.Dockerfile" - # # # build-push-image-malus: needs: [preflight, build-malus] - runs-on: arc-runners-polkadot-sdk + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} timeout-minutes: 60 steps: - name: Checkout @@ -399,13 +396,12 @@ jobs: image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/malus" dockerfile: "docker/dockerfiles/malus_injected.Dockerfile" - # # # build-push-image-substrate-pr: needs: [preflight, build-linux-substrate] - runs-on: arc-runners-polkadot-sdk + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} timeout-minutes: 60 steps: - name: Checkout @@ -424,15 +420,20 @@ jobs: image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/substrate" dockerfile: "docker/dockerfiles/substrate_injected.Dockerfile" - # # # # unlike other images, bridges+zombienet image is based on Zombienet image that pulls required binaries # from other fresh images (polkadot and cumulus) build-push-image-bridges-zombienet-tests: - needs: [preflight, build-linux-stable, build-linux-stable-cumulus, prepare-bridges-zombienet-artifacts] - runs-on: arc-runners-polkadot-sdk + needs: + [ + preflight, + build-linux-stable, + build-linux-stable-cumulus, + prepare-bridges-zombienet-artifacts, + ] + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} timeout-minutes: 60 steps: - name: Checkout @@ -443,24 +444,24 @@ jobs: name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: | - tar -xvf artifacts.tar - rm artifacts.tar + tar -xvf artifacts.tar + rm artifacts.tar - uses: actions/download-artifact@v4.1.8 with: name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: | - tar -xvf artifacts.tar - rm artifacts.tar + tar -xvf artifacts.tar + rm artifacts.tar - uses: actions/download-artifact@v4.1.8 with: name: prepare-bridges-zombienet-artifacts-${{ needs.preflight.outputs.SOURCE_REF_NAME }} - name: tar run: | - tar -xvf artifacts.tar - rm artifacts.tar + tar -xvf artifacts.tar + rm artifacts.tar - name: build and push image uses: ./.github/actions/build-push-image @@ -468,13 +469,12 @@ jobs: image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/bridges-zombienet-tests" dockerfile: "docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile" - # # # build-push-image-polkadot-parachain-debug: needs: [preflight, build-linux-stable-cumulus] - runs-on: arc-runners-polkadot-sdk + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} timeout-minutes: 60 steps: - name: Checkout @@ -491,4 +491,4 @@ jobs: uses: ./.github/actions/build-push-image with: image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/polkadot-parachain-debug" - dockerfile: "docker/dockerfiles/polkadot-parachain/polkadot-parachain-debug_unsigned_injected.Dockerfile" \ No newline at end of file + dockerfile: "docker/dockerfiles/polkadot-parachain/polkadot-parachain-debug_unsigned_injected.Dockerfile" diff --git a/.github/workflows/check-getting-started.yml b/.github/workflows/check-getting-started.yml index b43db33c63bf..0661fa144348 100644 --- a/.github/workflows/check-getting-started.yml +++ b/.github/workflows/check-getting-started.yml @@ -6,7 +6,7 @@ name: Check the getting-started.sh script # # There are two jobs inside. # One for systems that can run in a docker container, and one for macOS. -# +# # Each job consists of: # 1. Some necessary prerequisites for the workflow itself. # 2. A first pass of the script, which will install dependencies and clone a template. @@ -24,10 +24,10 @@ name: Check the getting-started.sh script on: pull_request: paths: - - '.github/workflows/check-getting-started.yml' - - 'scripts/getting-started.sh' + - ".github/workflows/check-getting-started.yml" + - "scripts/getting-started.sh" schedule: - - cron: '0 5 * * *' + - cron: "0 5 * * *" workflow_dispatch: concurrency: @@ -60,7 +60,7 @@ jobs: container: opensuse/tumbleweed template: solochain shell: sh - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: parity-large container: ${{ matrix.container }}:latest steps: # A minimal amount of prerequisites required before we can run the actual getting-started script, @@ -116,7 +116,7 @@ jobs: expect "start with one of the templates" { send "y\r" } - + expect -re "(.)\\) ${{ matrix.template }} template" { send "$expect_out(1,string)\r" } @@ -150,7 +150,7 @@ jobs: expect "start with one of the templates" { send "y\r" } - + expect -re "(.)\\) ${{ matrix.template }} template" { send "$expect_out(1,string)\r" expect "directory already exists" {} @@ -227,7 +227,7 @@ jobs: expect "start with one of the templates" { send "y\r" } - + expect -re "(.)\\) ${{ matrix.template }} template" { send "$expect_out(1,string)\r" } @@ -267,7 +267,7 @@ jobs: expect "start with one of the templates" { send "y\r" } - + expect -re "(.)\\) ${{ matrix.template }} template" { send "$expect_out(1,string)\r" expect "directory already exists" {} diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index 8d30e752b115..39a1c4d03486 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -50,54 +50,59 @@ jobs: package: westend-runtime wasm: westend_runtime.compact.compressed.wasm uri: "wss://try-runtime-westend.polkadot.io:443" - subcommand_extra_args: "--no-weight-warnings" + subcommand_extra_args: "--no-weight-warnings --blocktime 6000" command_extra_args: "" - network: rococo package: rococo-runtime wasm: rococo_runtime.compact.compressed.wasm uri: "wss://try-runtime-rococo.polkadot.io:443" - subcommand_extra_args: "--no-weight-warnings" + subcommand_extra_args: "--no-weight-warnings --blocktime 6000" command_extra_args: "" - network: asset-hub-westend package: asset-hub-westend-runtime wasm: asset_hub_westend_runtime.compact.compressed.wasm uri: "wss://westend-asset-hub-rpc.polkadot.io:443" - subcommand_extra_args: "" + subcommand_extra_args: " --blocktime 6000" command_extra_args: "" - network: "asset-hub-rococo" package: "asset-hub-rococo-runtime" wasm: "asset_hub_rococo_runtime.compact.compressed.wasm" uri: "wss://rococo-asset-hub-rpc.polkadot.io:443" - subcommand_extra_args: "" + subcommand_extra_args: " --blocktime 6000" command_extra_args: "" - network: "bridge-hub-westend" package: "bridge-hub-westend-runtime" wasm: "bridge_hub_westend_runtime.compact.compressed.wasm" uri: "wss://westend-bridge-hub-rpc.polkadot.io:443" + subcommand_extra_args: " --blocktime 6000" - network: "bridge-hub-rococo" package: "bridge-hub-rococo-runtime" wasm: "bridge_hub_rococo_runtime.compact.compressed.wasm" uri: "wss://rococo-bridge-hub-rpc.polkadot.io:443" + subcommand_extra_args: " --blocktime 6000" - network: "contracts-rococo" package: "contracts-rococo-runtime" wasm: "contracts_rococo_runtime.compact.compressed.wasm" uri: "wss://rococo-contracts-rpc.polkadot.io:443" + subcommand_extra_args: " --blocktime 6000" - network: "collectives-westend" package: "collectives-westend-runtime" wasm: "collectives_westend_runtime.compact.compressed.wasm" uri: "wss://westend-collectives-rpc.polkadot.io:443" command_extra_args: "--disable-spec-name-check" + subcommand_extra_args: " --blocktime 6000" - network: "coretime-rococo" package: "coretime-rococo-runtime" wasm: "coretime_rococo_runtime.compact.compressed.wasm" uri: "wss://rococo-coretime-rpc.polkadot.io:443" + subcommand_extra_args: " --blocktime 6000" steps: - name: Checkout uses: actions/checkout@v4 - name: Download CLI run: | - curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.7.0/try-runtime-x86_64-unknown-linux-musl -o try-runtime + curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.8.0/try-runtime-x86_64-unknown-linux-musl -o try-runtime chmod +x ./try-runtime echo "Using try-runtime-cli version:" ./try-runtime --version diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index e416b1202691..0c9c48a123b0 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -231,9 +231,9 @@ jobs: fi if [[ $BODY == "/cmd bench"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-benchmark" >> $GITHUB_OUTPUT + echo "RUNNER=parity-weights" >> $GITHUB_OUTPUT elif [[ $BODY == "/cmd update-ui"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + echo "RUNNER=parity-large" >> $GITHUB_OUTPUT else echo "RUNNER=ubuntu-latest" >> $GITHUB_OUTPUT fi diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml index 5b32f954d0cd..7f1d59bc0f6c 100644 --- a/.github/workflows/command-backport.yml +++ b/.github/workflows/command-backport.yml @@ -10,6 +10,7 @@ permissions: contents: write # so it can comment pull-requests: write # so it can create pull requests issues: write + actions: write # It may have to backport changes to the CI as well. jobs: backport: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 44ad9613997a..8fe3e80e628d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,7 +29,7 @@ jobs: env: SKIP_WASM_BUILD: 1 test-doc: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.preflight.outputs.RUNNER }} needs: [preflight] container: image: ${{ needs.preflight.outputs.IMAGE }} diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index ecaf3295d401..dacfcc5995b7 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -348,7 +348,7 @@ jobs: - name: Build and push id: docker_build - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 + uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 # v6.8.0 with: push: true file: docker/dockerfiles/polkadot/polkadot_injected_debian.Dockerfile diff --git a/.github/workflows/tests-misc.yml b/.github/workflows/tests-misc.yml index 45ed4ebdc6cb..4355cd0a9f87 100644 --- a/.github/workflows/tests-misc.yml +++ b/.github/workflows/tests-misc.yml @@ -157,7 +157,6 @@ jobs: node-bench-regression-guard: timeout-minutes: 20 if: always() && !cancelled() - # runs-on: arc-runners-polkadot-sdk runs-on: ubuntu-latest needs: [preflight, cargo-check-benches] steps: diff --git a/.gitlab/pipeline/zombienet.yml b/.gitlab/pipeline/zombienet.yml index b7f2397d20bd..b02d857f3f24 100644 --- a/.gitlab/pipeline/zombienet.yml +++ b/.gitlab/pipeline/zombienet.yml @@ -1,7 +1,7 @@ .zombienet-refs: extends: .build-refs variables: - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.112" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.113" PUSHGATEWAY_URL: "http://zombienet-prometheus-pushgateway.managed-monitoring:9091/metrics/job/zombie-metrics" DEBUG: "zombie,zombie::network-node,zombie::kube::client::logs" ZOMBIE_PROVIDER: "k8s" diff --git a/Cargo.lock b/Cargo.lock index 568d642879bf..119a8b3afed2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9592,6 +9592,7 @@ dependencies = [ "parity-scale-codec", "polkadot-sdk", "scale-info", + "serde_json", ] [[package]] @@ -21246,6 +21247,7 @@ dependencies = [ "sp-consensus-aura", "sp-consensus-grandpa", "sp-core 28.0.0", + "sp-genesis-builder", "sp-inherents", "sp-io 30.0.0", "sp-keyring", @@ -21277,6 +21279,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "scale-info", + "serde_json", "sp-api 26.0.0", "sp-block-builder", "sp-consensus-aura", @@ -21284,6 +21287,7 @@ dependencies = [ "sp-core 28.0.0", "sp-genesis-builder", "sp-inherents", + "sp-keyring", "sp-offchain", "sp-runtime 31.0.1", "sp-session", diff --git a/bridges/relays/lib-substrate-relay/src/messages/source.rs b/bridges/relays/lib-substrate-relay/src/messages/source.rs index b560867a235b..3e60ed7abd09 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/source.rs @@ -35,10 +35,10 @@ use bp_messages::{ storage_keys::{operating_mode_key, outbound_lane_data_key}, target_chain::FromBridgedChainMessagesProof, ChainWithMessages as _, InboundMessageDetails, MessageNonce, MessagePayload, - MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, + MessagesOperatingMode, OutboundMessageDetails, }; use bp_runtime::{BasicOperatingMode, HeaderIdProvider, RangeInclusiveExt}; -use codec::Encode; +use codec::{Decode, Encode}; use frame_support::weights::Weight; use messages_relay::{ message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, @@ -63,6 +63,18 @@ use std::ops::RangeInclusive; pub type SubstrateMessagesProof = (Weight, FromBridgedChainMessagesProof, L>); type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>; +/// Outbound lane data - for backwards compatibility with `bp_messages::OutboundLaneData` which has +/// additional `lane_state` attribute. +/// +/// TODO: remove - https://github.com/paritytech/polkadot-sdk/issues/5923 +#[derive(Decode)] +struct LegacyOutboundLaneData { + #[allow(unused)] + oldest_unpruned_nonce: MessageNonce, + latest_received_nonce: MessageNonce, + latest_generated_nonce: MessageNonce, +} + /// Substrate client as Substrate messages source. pub struct SubstrateMessagesSource { source_client: SourceClnt, @@ -98,7 +110,7 @@ impl, TargetClnt> async fn outbound_lane_data( &self, id: SourceHeaderIdOf>, - ) -> Result, SubstrateError> { + ) -> Result, SubstrateError> { self.source_client .storage_value( id.hash(), @@ -743,4 +755,38 @@ mod tests { Ok(vec![2, 4, 3]), ); } + + #[test] + fn outbound_lane_data_wrapper_is_compatible() { + let bytes_without_state = + vec![1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0]; + let bytes_with_state = { + // add state byte `bp_messages::LaneState::Opened` + let mut b = bytes_without_state.clone(); + b.push(0); + b + }; + + let full = bp_messages::OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 2, + latest_generated_nonce: 3, + state: bp_messages::LaneState::Opened, + }; + assert_eq!(full.encode(), bytes_with_state); + assert_ne!(full.encode(), bytes_without_state); + + // decode from `bytes_with_state` + let decoded: LegacyOutboundLaneData = Decode::decode(&mut &bytes_with_state[..]).unwrap(); + assert_eq!(full.oldest_unpruned_nonce, decoded.oldest_unpruned_nonce); + assert_eq!(full.latest_received_nonce, decoded.latest_received_nonce); + assert_eq!(full.latest_generated_nonce, decoded.latest_generated_nonce); + + // decode from `bytes_without_state` + let decoded: LegacyOutboundLaneData = + Decode::decode(&mut &bytes_without_state[..]).unwrap(); + assert_eq!(full.oldest_unpruned_nonce, decoded.oldest_unpruned_nonce); + assert_eq!(full.latest_received_nonce, decoded.latest_received_nonce); + assert_eq!(full.latest_generated_nonce, decoded.latest_generated_nonce); + } } diff --git a/bridges/relays/lib-substrate-relay/src/messages/target.rs b/bridges/relays/lib-substrate-relay/src/messages/target.rs index 0d1aac88a32a..214819a1c426 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/target.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/target.rs @@ -36,8 +36,9 @@ use async_std::sync::Arc; use async_trait::async_trait; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, storage_keys::inbound_lane_data_key, - ChainWithMessages as _, InboundLaneData, MessageNonce, UnrewardedRelayersState, + ChainWithMessages as _, LaneState, MessageNonce, UnrewardedRelayer, UnrewardedRelayersState, }; +use codec::Decode; use messages_relay::{ message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState}, @@ -48,12 +49,52 @@ use relay_substrate_client::{ }; use relay_utils::relay_loop::Client as RelayClient; use sp_core::Pair; -use std::{convert::TryFrom, ops::RangeInclusive}; +use std::{collections::VecDeque, convert::TryFrom, ops::RangeInclusive}; /// Message receiving proof returned by the target Substrate node. pub type SubstrateMessagesDeliveryProof = (UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof, L>); +/// Inbound lane data - for backwards compatibility with `bp_messages::InboundLaneData` which has +/// additional `lane_state` attribute. +/// +/// TODO: remove - https://github.com/paritytech/polkadot-sdk/issues/5923 +#[derive(Decode)] +struct LegacyInboundLaneData { + relayers: VecDeque>, + last_confirmed_nonce: MessageNonce, +} +impl Default for LegacyInboundLaneData { + fn default() -> Self { + let full = bp_messages::InboundLaneData::default(); + Self { relayers: full.relayers, last_confirmed_nonce: full.last_confirmed_nonce } + } +} + +impl LegacyInboundLaneData { + pub fn last_delivered_nonce(self) -> MessageNonce { + bp_messages::InboundLaneData { + relayers: self.relayers, + last_confirmed_nonce: self.last_confirmed_nonce, + // we don't care about the state here + state: LaneState::Opened, + } + .last_delivered_nonce() + } +} + +impl From> for UnrewardedRelayersState { + fn from(value: LegacyInboundLaneData) -> Self { + (&bp_messages::InboundLaneData { + relayers: value.relayers, + last_confirmed_nonce: value.last_confirmed_nonce, + // we don't care about the state here + state: LaneState::Opened, + }) + .into() + } +} + /// Substrate client as Substrate messages target. pub struct SubstrateMessagesTarget { target_client: TargetClnt, @@ -94,7 +135,7 @@ where async fn inbound_lane_data( &self, id: TargetHeaderIdOf>, - ) -> Result>>, SubstrateError> { + ) -> Result>>, SubstrateError> { self.target_client .storage_value( id.hash(), @@ -217,8 +258,8 @@ where ) -> Result<(TargetHeaderIdOf>, UnrewardedRelayersState), SubstrateError> { let inbound_lane_data = - self.inbound_lane_data(id).await?.unwrap_or(InboundLaneData::default()); - Ok((id, (&inbound_lane_data).into())) + self.inbound_lane_data(id).await?.unwrap_or(LegacyInboundLaneData::default()); + Ok((id, inbound_lane_data.into())) } async fn prove_messages_receiving( @@ -321,3 +362,49 @@ fn make_messages_delivery_call( trace_call, ) } + +#[cfg(test)] +mod tests { + use super::*; + use bp_messages::{DeliveredMessages, UnrewardedRelayer}; + use codec::Encode; + + #[test] + fn inbound_lane_data_wrapper_is_compatible() { + let bytes_without_state = + vec![4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0]; + let bytes_with_state = { + // add state byte `bp_messages::LaneState::Opened` + let mut b = bytes_without_state.clone(); + b.push(0); + b + }; + + let full = bp_messages::InboundLaneData:: { + relayers: vec![UnrewardedRelayer { + relayer: Default::default(), + messages: DeliveredMessages { begin: 2, end: 5 }, + }] + .into_iter() + .collect(), + last_confirmed_nonce: 6, + state: bp_messages::LaneState::Opened, + }; + assert_eq!(full.encode(), bytes_with_state); + assert_ne!(full.encode(), bytes_without_state); + + // decode from `bytes_with_state` + let decoded: LegacyInboundLaneData = + Decode::decode(&mut &bytes_with_state[..]).unwrap(); + assert_eq!(full.relayers, decoded.relayers); + assert_eq!(full.last_confirmed_nonce, decoded.last_confirmed_nonce); + assert_eq!(full.last_delivered_nonce(), decoded.last_delivered_nonce()); + + // decode from `bytes_without_state` + let decoded: LegacyInboundLaneData = + Decode::decode(&mut &bytes_without_state[..]).unwrap(); + assert_eq!(full.relayers, decoded.relayers); + assert_eq!(full.last_confirmed_nonce, decoded.last_confirmed_nonce); + assert_eq!(full.last_delivered_nonce(), decoded.last_delivered_nonce()); + } +} diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs index 322baaa01498..8ac43fbd116e 100644 --- a/cumulus/client/consensus/aura/src/collators/lookahead.rs +++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs @@ -36,7 +36,9 @@ use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterfa use cumulus_client_consensus_common::{self as consensus_common, ParachainBlockImportMarker}; use cumulus_client_consensus_proposer::ProposerInterface; use cumulus_primitives_aura::AuraUnincludedSegmentApi; -use cumulus_primitives_core::{ClaimQueueOffset, CollectCollationInfo, PersistedValidationData}; +use cumulus_primitives_core::{ + ClaimQueueOffset, CollectCollationInfo, PersistedValidationData, DEFAULT_CLAIM_QUEUE_OFFSET, +}; use cumulus_relay_chain_interface::RelayChainInterface; use polkadot_node_primitives::{PoV, SubmitCollationParams}; @@ -260,8 +262,7 @@ where relay_parent, params.para_id, &mut params.relay_client, - // Use depth 0, to preserve behaviour. - ClaimQueueOffset(0), + ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET), ) .await .get(0) diff --git a/cumulus/pallets/parachain-system/Cargo.toml b/cumulus/pallets/parachain-system/Cargo.toml index 66429625d5bf..3cb0394c4b95 100644 --- a/cumulus/pallets/parachain-system/Cargo.toml +++ b/cumulus/pallets/parachain-system/Cargo.toml @@ -121,3 +121,5 @@ try-runtime = [ "polkadot-runtime-parachains/try-runtime", "sp-runtime/try-runtime", ] + +experimental-ump-signals = [] diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index 21af35fe3de3..a4b505b98e54 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -193,20 +193,46 @@ pub mod ump_constants { /// Trait for selecting the next core to build the candidate for. pub trait SelectCore { - fn select_core_for_child() -> (CoreSelector, ClaimQueueOffset); + /// Core selector information for the current block. + fn selected_core() -> (CoreSelector, ClaimQueueOffset); + /// Core selector information for the next block. + fn select_next_core() -> (CoreSelector, ClaimQueueOffset); } /// The default core selection policy. pub struct DefaultCoreSelector(PhantomData); impl SelectCore for DefaultCoreSelector { - fn select_core_for_child() -> (CoreSelector, ClaimQueueOffset) { + fn selected_core() -> (CoreSelector, ClaimQueueOffset) { + let core_selector: U256 = frame_system::Pallet::::block_number().into(); + + (CoreSelector(core_selector.byte(0)), ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)) + } + + fn select_next_core() -> (CoreSelector, ClaimQueueOffset) { let core_selector: U256 = (frame_system::Pallet::::block_number() + One::one()).into(); (CoreSelector(core_selector.byte(0)), ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)) } } +/// Core selection policy that builds on claim queue offset 1. +pub struct LookaheadCoreSelector(PhantomData); + +impl SelectCore for LookaheadCoreSelector { + fn selected_core() -> (CoreSelector, ClaimQueueOffset) { + let core_selector: U256 = frame_system::Pallet::::block_number().into(); + + (CoreSelector(core_selector.byte(0)), ClaimQueueOffset(1)) + } + + fn select_next_core() -> (CoreSelector, ClaimQueueOffset) { + let core_selector: U256 = (frame_system::Pallet::::block_number() + One::one()).into(); + + (CoreSelector(core_selector.byte(0)), ClaimQueueOffset(1)) + } +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -365,6 +391,11 @@ pub mod pallet { UpwardMessages::::put(&up[..num as usize]); *up = up.split_off(num as usize); + // Send the core selector UMP signal. This is experimental until relay chain + // validators are upgraded to handle ump signals. + #[cfg(feature = "experimental-ump-signals")] + Self::send_ump_signal(); + // If the total size of the pending messages is less than the threshold, // we decrease the fee factor, since the queue is less congested. // This makes delivery of new messages cheaper. @@ -1397,9 +1428,9 @@ impl Pallet { } } - /// Returns the core selector. + /// Returns the core selector for the next block. pub fn core_selector() -> (CoreSelector, ClaimQueueOffset) { - T::SelectCore::select_core_for_child() + T::SelectCore::select_next_core() } /// Set a custom head data that should be returned as result of `validate_block`. @@ -1418,6 +1449,20 @@ impl Pallet { CustomValidationHeadData::::put(head_data); } + /// Send the ump signals + #[cfg(feature = "experimental-ump-signals")] + fn send_ump_signal() { + use cumulus_primitives_core::relay_chain::vstaging::{UMPSignal, UMP_SEPARATOR}; + + UpwardMessages::::mutate(|up| { + up.push(UMP_SEPARATOR); + + // Send the core selector signal. + let core_selector = T::SelectCore::selected_core(); + up.push(UMPSignal::SelectCore(core_selector.0, core_selector.1).encode()); + }); + } + /// Open HRMP channel for using it in benchmarks or tests. /// /// The caller assumes that the pallet will accept regular outbound message to the sibling diff --git a/cumulus/parachains/chain-specs/people-polkadot.json b/cumulus/parachains/chain-specs/people-polkadot.json index 6e30829eab49..083c0fbf44a4 100644 --- a/cumulus/parachains/chain-specs/people-polkadot.json +++ b/cumulus/parachains/chain-specs/people-polkadot.json @@ -6,7 +6,9 @@ "/dns/polkadot-people-connect-0.polkadot.io/tcp/30334/p2p/12D3KooWP7BoJ7nAF9QnsreN8Eft1yHNUhvhxFiQyKFEUePi9mu3", "/dns/polkadot-people-connect-1.polkadot.io/tcp/30334/p2p/12D3KooWSSfWY3fTGJvGkuNUNBSNVCdLLNJnwkZSNQt7GCRYXu4o", "/dns/polkadot-people-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWP7BoJ7nAF9QnsreN8Eft1yHNUhvhxFiQyKFEUePi9mu3", - "/dns/polkadot-people-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWSSfWY3fTGJvGkuNUNBSNVCdLLNJnwkZSNQt7GCRYXu4o" + "/dns/polkadot-people-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWSSfWY3fTGJvGkuNUNBSNVCdLLNJnwkZSNQt7GCRYXu4o", + "/dns/people-polkadot-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWKMYu1L28TkDf1ooMW8D8PHcztLnjV3bausH9eiVTRUYN", + "/dns/people-polkadot-boot-ng.dwellir.com/tcp/30346/p2p/12D3KooWKMYu1L28TkDf1ooMW8D8PHcztLnjV3bausH9eiVTRUYN" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 8163a445220b..367cf8a51f2c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -213,6 +213,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 75aea0eb2c2f..bb77f576e20d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -213,6 +213,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { 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 13c1ab6b625b..e3435f35f178 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 @@ -342,6 +342,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { 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 03b69a218020..b73d8e5c67fa 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 @@ -327,6 +327,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index a60d3ed66ac6..7f87d4701f96 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -223,6 +223,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 50316a9a1d04..3377802e91de 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -234,6 +234,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 74569af9ec7a..f16dae04f217 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -249,6 +249,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 5e37faf61819..e909b804587f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -249,6 +249,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index bfd31e2b484e..aa04d01cf037 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -225,6 +225,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 8258ba0564f9..c8131c94f15b 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -224,6 +224,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index a98c7b4886fd..05773846164f 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -420,6 +420,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 4d39728df83c..5abdc995c00d 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -257,6 +257,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } impl pallet_transaction_payment::Config for Runtime { diff --git a/cumulus/primitives/core/src/lib.rs b/cumulus/primitives/core/src/lib.rs index 6bad65b3ff2c..dfb574ef3301 100644 --- a/cumulus/primitives/core/src/lib.rs +++ b/cumulus/primitives/core/src/lib.rs @@ -335,7 +335,7 @@ pub mod rpsr_digest { /// The default claim queue offset to be used if it's not configured/accessible in the parachain /// runtime -pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 1; +pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 0; /// Information about a collation. /// diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 861d55c77cdc..92a6ab73a2eb 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -269,6 +269,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } impl pallet_transaction_payment::Config for Runtime { diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile index 2198da13a4b7..b1f4bffc772a 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.10 +ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.7.0 # metadata ARG VCS_REF diff --git a/docs/contributor/weight-generation.md b/docs/contributor/weight-generation.md index ebfdca59cae5..77a570c94530 100644 --- a/docs/contributor/weight-generation.md +++ b/docs/contributor/weight-generation.md @@ -3,7 +3,7 @@ To generate weights for a runtime. Weights generation is using self-hosted runner which is provided by Parity CI, the rest commands are using standard GitHub runners on `ubuntu-latest` or `ubuntu-20.04`. -Self-hosted runner for benchmarks (arc-runners-Polkadot-sdk-benchmark) is configured to meet requirements of reference +Self-hosted runner for benchmarks (`parity-weights`) is configured to meet requirements of reference hardware for running validators https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware diff --git a/polkadot/node/core/dispute-coordinator/src/tests.rs b/polkadot/node/core/dispute-coordinator/src/tests.rs index b41cdb94b4d2..48762a1d80be 100644 --- a/polkadot/node/core/dispute-coordinator/src/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/tests.rs @@ -2797,7 +2797,7 @@ fn participation_with_onchain_disabling_confirmed() { }) .await; - handle_disabled_validators_queries(&mut virtual_overseer, vec![]).await; + handle_disabled_validators_queries(&mut virtual_overseer, vec![disabled_index]).await; handle_approval_vote_request(&mut virtual_overseer, &candidate_hash, HashMap::new()) .await; assert_eq!(confirmation_rx.await, Ok(ImportStatementsResult::ValidImport)); diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 57cba85c10d9..bc687f7e2fbe 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -24,12 +24,15 @@ use super::{ HashT, HeadData, Header, Id, Id as ParaId, MultiDisputeStatementSet, ScheduledCore, UncheckedSignedAvailabilityBitfields, ValidationCodeHash, }; +use alloc::{ + collections::{BTreeMap, BTreeSet, VecDeque}, + vec, + vec::Vec, +}; use bitvec::prelude::*; -use sp_application_crypto::ByteArray; - -use alloc::{vec, vec::Vec}; use codec::{Decode, Encode}; use scale_info::TypeInfo; +use sp_application_crypto::ByteArray; use sp_core::RuntimeDebug; use sp_runtime::traits::Header as HeaderT; use sp_staking::SessionIndex; @@ -298,9 +301,9 @@ pub struct ClaimQueueOffset(pub u8); /// Signals that a parachain can send to the relay chain via the UMP queue. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] pub enum UMPSignal { - /// A message sent by a parachain to select the core the candidate is commited to. + /// A message sent by a parachain to select the core the candidate is committed to. /// Relay chain validators, in particular backers, use the `CoreSelector` and - /// `ClaimQueueOffset` to compute the index of the core the candidate has commited to. + /// `ClaimQueueOffset` to compute the index of the core the candidate has committed to. SelectCore(CoreSelector, ClaimQueueOffset), } /// Separator between `XCM` and `UMPSignal`. @@ -324,6 +327,25 @@ impl CandidateCommitments { UMPSignal::SelectCore(core_selector, cq_offset) => Some((core_selector, cq_offset)), } } + + /// Returns the core index determined by `UMPSignal::SelectCore` commitment + /// and `assigned_cores`. + /// + /// Returns `None` if there is no `UMPSignal::SelectCore` commitment or + /// assigned cores is empty. + /// + /// `assigned_cores` must be a sorted vec of all core indices assigned to a parachain. + pub fn committed_core_index(&self, assigned_cores: &[&CoreIndex]) -> Option { + if assigned_cores.is_empty() { + return None + } + + self.selected_core().and_then(|(core_selector, _cq_offset)| { + let core_index = + **assigned_cores.get(core_selector.0 as usize % assigned_cores.len())?; + Some(core_index) + }) + } } /// CandidateReceipt construction errors. @@ -337,7 +359,8 @@ pub enum CandidateReceiptError { InvalidSelectedCore, /// The parachain is not assigned to any core at specified claim queue offset. NoAssignment, - /// No core was selected. + /// No core was selected. The `SelectCore` commitment is mandatory for + /// v2 receipts if parachains has multiple cores assigned. NoCoreSelected, /// Unknown version. UnknownVersion(InternalVersion), @@ -432,33 +455,57 @@ impl CandidateDescriptorV2 { } impl CommittedCandidateReceiptV2 { - /// Checks if descriptor core index is equal to the commited core index. - /// Input `assigned_cores` must contain the sorted cores assigned to the para at - /// the committed claim queue offset. - pub fn check(&self, assigned_cores: &[CoreIndex]) -> Result<(), CandidateReceiptError> { - // Don't check v1 descriptors. - if self.descriptor.version() == CandidateDescriptorVersion::V1 { - return Ok(()) - } - - if self.descriptor.version() == CandidateDescriptorVersion::Unknown { - return Err(CandidateReceiptError::UnknownVersion(self.descriptor.version)) + /// Checks if descriptor core index is equal to the committed core index. + /// Input `cores_per_para` is a claim queue snapshot stored as a mapping + /// between `ParaId` and the cores assigned per depth. + pub fn check_core_index( + &self, + cores_per_para: &TransposedClaimQueue, + ) -> Result<(), CandidateReceiptError> { + match self.descriptor.version() { + // Don't check v1 descriptors. + CandidateDescriptorVersion::V1 => return Ok(()), + CandidateDescriptorVersion::V2 => {}, + CandidateDescriptorVersion::Unknown => + return Err(CandidateReceiptError::UnknownVersion(self.descriptor.version)), } - if assigned_cores.is_empty() { + if cores_per_para.is_empty() { return Err(CandidateReceiptError::NoAssignment) } - let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32); - - let (core_selector, _cq_offset) = - self.commitments.selected_core().ok_or(CandidateReceiptError::NoCoreSelected)?; + let (offset, core_selected) = + if let Some((_core_selector, cq_offset)) = self.commitments.selected_core() { + (cq_offset.0, true) + } else { + // If no core has been selected then we use offset 0 (top of claim queue) + (0, false) + }; + + // The cores assigned to the parachain at above computed offset. + let assigned_cores = cores_per_para + .get(&self.descriptor.para_id()) + .ok_or(CandidateReceiptError::NoAssignment)? + .get(&offset) + .ok_or(CandidateReceiptError::NoAssignment)? + .into_iter() + .collect::>(); + + let core_index = if core_selected { + self.commitments + .committed_core_index(assigned_cores.as_slice()) + .ok_or(CandidateReceiptError::NoAssignment)? + } else { + // `SelectCore` commitment is mandatory for elastic scaling parachains. + if assigned_cores.len() > 1 { + return Err(CandidateReceiptError::NoCoreSelected) + } - let core_index = assigned_cores - .get(core_selector.0 as usize % assigned_cores.len()) - .ok_or(CandidateReceiptError::InvalidCoreIndex)?; + **assigned_cores.get(0).ok_or(CandidateReceiptError::NoAssignment)? + }; - if *core_index != descriptor_core_index { + let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32); + if core_index != descriptor_core_index { return Err(CandidateReceiptError::CoreIndexMismatch) } @@ -512,6 +559,12 @@ impl BackedCandidate { &self.candidate } + /// Get a mutable reference to the committed candidate receipt of the candidate. + /// Only for testing. + #[cfg(feature = "test")] + pub fn candidate_mut(&mut self) -> &mut CommittedCandidateReceiptV2 { + &mut self.candidate + } /// Get a reference to the descriptor of the candidate. pub fn descriptor(&self) -> &CandidateDescriptorV2 { &self.candidate.descriptor @@ -697,6 +750,29 @@ impl From> for super::v8::CoreState { } } +/// The claim queue mapped by parachain id. +pub type TransposedClaimQueue = BTreeMap>>; + +/// Returns a mapping between the para id and the core indices assigned at different +/// depths in the claim queue. +pub fn transpose_claim_queue( + claim_queue: BTreeMap>, +) -> TransposedClaimQueue { + let mut per_para_claim_queue = BTreeMap::new(); + + for (core, paras) in claim_queue { + // Iterate paras assigned to this core at each depth. + for (depth, para) in paras.into_iter().enumerate() { + let depths: &mut BTreeMap> = + per_para_claim_queue.entry(para).or_insert_with(|| Default::default()); + + depths.entry(depth as u8).or_default().insert(core); + } + } + + per_para_claim_queue +} + #[cfg(test)] mod tests { use super::*; @@ -778,7 +854,7 @@ mod tests { assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown); assert_eq!( - new_ccr.check(&vec![].as_slice()), + new_ccr.check_core_index(&BTreeMap::new()), Err(CandidateReceiptError::UnknownVersion(InternalVersion(100))) ) } @@ -802,7 +878,13 @@ mod tests { .upward_messages .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); - assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); + let mut cq = BTreeMap::new(); + cq.insert( + CoreIndex(123), + vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(), + ); + + assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(())); } #[test] @@ -814,11 +896,12 @@ mod tests { new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); - // The check should fail because no `SelectCore` signal was sent. - assert_eq!( - new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), - Err(CandidateReceiptError::NoCoreSelected) - ); + let mut cq = BTreeMap::new(); + cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into()); + + // The check should not fail because no `SelectCore` signal was sent. + // The message is optional. + assert!(new_ccr.check_core_index(&transpose_claim_queue(cq)).is_ok()); // Garbage message. new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode()); @@ -826,9 +909,18 @@ mod tests { // No `SelectCore` can be decoded. assert_eq!(new_ccr.commitments.selected_core(), None); - // Failure is expected. + let mut cq = BTreeMap::new(); + cq.insert( + CoreIndex(0), + vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(), + ); + cq.insert( + CoreIndex(100), + vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(), + ); + assert_eq!( - new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), + new_ccr.check_core_index(&transpose_claim_queue(cq.clone())), Err(CandidateReceiptError::NoCoreSelected) ); @@ -847,7 +939,7 @@ mod tests { .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode()); // Duplicate doesn't override first signal. - assert_eq!(new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), Ok(())); + assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(())); } #[test] @@ -884,13 +976,57 @@ mod tests { Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123))); - assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(())); + + let mut cq = BTreeMap::new(); + cq.insert( + CoreIndex(123), + vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(), + ); + + assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(())); assert_eq!(new_ccr.hash(), v2_ccr.hash()); } + // Only check descriptor `core_index` field of v2 descriptors. If it is v1, that field + // will be garbage. #[test] - fn test_core_select_is_mandatory() { + fn test_v1_descriptors_with_ump_signal() { + let mut ccr = dummy_old_committed_candidate_receipt(); + ccr.descriptor.para_id = ParaId::new(1024); + // Adding collator signature should make it decode as v1. + ccr.descriptor.signature = dummy_collator_signature(); + ccr.descriptor.collator = dummy_collator_id(); + + ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + ccr.commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode()); + + let encoded_ccr: Vec = ccr.encode(); + + let v1_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(v1_ccr.descriptor.version(), CandidateDescriptorVersion::V1); + assert!(v1_ccr.commitments.selected_core().is_some()); + + let mut cq = BTreeMap::new(); + cq.insert(CoreIndex(0), vec![v1_ccr.descriptor.para_id()].into()); + cq.insert(CoreIndex(1), vec![v1_ccr.descriptor.para_id()].into()); + + assert!(v1_ccr.check_core_index(&transpose_claim_queue(cq)).is_ok()); + + assert_eq!( + v1_ccr.commitments.committed_core_index(&vec![&CoreIndex(10), &CoreIndex(5)]), + Some(CoreIndex(5)), + ); + + assert_eq!(v1_ccr.descriptor.core_index(), None); + } + + #[test] + fn test_core_select_is_optional() { // Testing edge case when collators provide zeroed signature and collator id. let mut old_ccr = dummy_old_committed_candidate_receipt(); old_ccr.descriptor.para_id = ParaId::new(1000); @@ -899,11 +1035,22 @@ mod tests { let new_ccr: CommittedCandidateReceiptV2 = Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + let mut cq = BTreeMap::new(); + cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into()); + // Since collator sig and id are zeroed, it means that the descriptor uses format - // version 2. - // We expect the check to fail in such case because there will be no `SelectCore` - // commitment. - assert_eq!(new_ccr.check(&vec![CoreIndex(0)]), Err(CandidateReceiptError::NoCoreSelected)); + // version 2. Should still pass checks without core selector. + assert!(new_ccr.check_core_index(&transpose_claim_queue(cq)).is_ok()); + + let mut cq = BTreeMap::new(); + cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into()); + cq.insert(CoreIndex(1), vec![new_ccr.descriptor.para_id()].into()); + + // Should fail because 2 cores are assigned, + assert_eq!( + new_ccr.check_core_index(&transpose_claim_queue(cq)), + Err(CandidateReceiptError::NoCoreSelected) + ); // Adding collator signature should make it decode as v1. old_ccr.descriptor.signature = dummy_collator_signature(); diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md b/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md index 1a3ff1c6aff0..aad77de0aded 100644 --- a/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md +++ b/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md @@ -85,7 +85,7 @@ state. Once we have all parameters, we can spin up a background task to perform the validation in a way that doesn't hold up the entire event loop. Before invoking the validation function itself, this should first do some basic checks: - * The collator signature is valid + * The collator signature is valid (only if `CandidateDescriptor` has version 1) * The PoV provided matches the `pov_hash` field of the descriptor For more details please see [PVF Host and Workers](pvf-host-and-workers.md). diff --git a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md index 5031433cf5a1..48909db07ba5 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md @@ -109,7 +109,7 @@ All failed checks should lead to an unrecoverable error making the block invalid 1. Ensure that any code upgrade scheduled by the candidate does not happen within `config.validation_upgrade_cooldown` of `Paras::last_code_upgrade(para_id, true)`, if any, comparing against the value of `Paras::FutureCodeUpgrades` for the given para ID. - 1. Check the collator's signature on the candidate data. + 1. Check the collator's signature on the candidate data (only if `CandidateDescriptor` is version 1) 1. check the backing of the candidate using the signatures and the bitfields, comparing against the validators assigned to the groups, fetched with the `group_validators` lookup, while group indices are computed by `Scheduler` according to group rotation info. diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index 665737afa6cb..1654590d109e 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -32,9 +32,9 @@ use frame_system::pallet_prelude::*; use polkadot_primitives::{ node_features::FeatureIndex, vstaging::{ - BackedCandidate, CandidateDescriptorV2, - CommittedCandidateReceiptV2 as CommittedCandidateReceipt, - InherentData as ParachainsInherentData, + BackedCandidate, CandidateDescriptorV2, ClaimQueueOffset, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreSelector, + InherentData as ParachainsInherentData, UMPSignal, UMP_SEPARATOR, }, AvailabilityBitfield, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, @@ -52,14 +52,14 @@ fn mock_validation_code() -> ValidationCode { ValidationCode(vec![1, 2, 3]) } -// Create a dummy collator id suitable to be used in a V1 candidate descriptor. -fn junk_collator() -> CollatorId { +/// Create a dummy collator id suitable to be used in a V1 candidate descriptor. +pub fn junk_collator() -> CollatorId { CollatorId::from_slice(&mut (0..32).into_iter().collect::>().as_slice()) .expect("32 bytes; qed") } -// Creates a dummy collator signature suitable to be used in a V1 candidate descriptor. -fn junk_collator_signature() -> CollatorSignature { +/// Creates a dummy collator signature suitable to be used in a V1 candidate descriptor. +pub fn junk_collator_signature() -> CollatorSignature { CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) .expect("64 bytes; qed") } @@ -144,9 +144,14 @@ pub(crate) struct BenchBuilder { unavailable_cores: Vec, /// Use v2 candidate descriptor. candidate_descriptor_v2: bool, + /// Apply custom changes to generated candidates + candidate_modifier: Option>, _phantom: core::marker::PhantomData, } +pub type CandidateModifier = + fn(CommittedCandidateReceipt) -> CommittedCandidateReceipt; + /// Paras inherent `enter` benchmark scenario. #[cfg(any(feature = "runtime-benchmarks", test))] pub(crate) struct Bench { @@ -176,6 +181,7 @@ impl BenchBuilder { fill_claimqueue: true, unavailable_cores: vec![], candidate_descriptor_v2: false, + candidate_modifier: None, _phantom: core::marker::PhantomData::, } } @@ -290,6 +296,15 @@ impl BenchBuilder { self } + /// Set the candidate modifier. + pub(crate) fn set_candidate_modifier( + mut self, + modifier: Option>, + ) -> Self { + self.candidate_modifier = modifier; + self + } + /// Get the maximum number of validators per core. fn max_validators_per_core(&self) -> u32 { self.max_validators_per_core.unwrap_or(Self::fallback_max_validators_per_core()) @@ -325,18 +340,33 @@ impl BenchBuilder { HeadData(vec![0xFF; max_head_size as usize]) } - fn candidate_descriptor_mock() -> CandidateDescriptorV2 { - // Use a v1 descriptor. - CandidateDescriptor:: { - para_id: 0.into(), - relay_parent: Default::default(), - collator: junk_collator(), - persisted_validation_data_hash: Default::default(), - pov_hash: Default::default(), - erasure_root: Default::default(), - signature: junk_collator_signature(), - para_head: Default::default(), - validation_code_hash: mock_validation_code().hash(), + fn candidate_descriptor_mock(candidate_descriptor_v2: bool) -> CandidateDescriptorV2 { + if candidate_descriptor_v2 { + CandidateDescriptorV2::new( + 0.into(), + Default::default(), + CoreIndex(200), + 2, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + mock_validation_code().hash(), + ) + } else { + // Convert v1 to v2. + CandidateDescriptor:: { + para_id: 0.into(), + relay_parent: Default::default(), + collator: junk_collator(), + persisted_validation_data_hash: Default::default(), + pov_hash: Default::default(), + erasure_root: Default::default(), + signature: junk_collator_signature(), + para_head: Default::default(), + validation_code_hash: mock_validation_code().hash(), + } + .into() } .into() } @@ -348,17 +378,19 @@ impl BenchBuilder { candidate_hash: CandidateHash, availability_votes: BitVec, commitments: CandidateCommitments, + candidate_descriptor_v2: bool, ) -> inclusion::CandidatePendingAvailability> { inclusion::CandidatePendingAvailability::>::new( - core_idx, // core - candidate_hash, // hash - Self::candidate_descriptor_mock(), // candidate descriptor - commitments, // commitments - availability_votes, // availability votes - Default::default(), // backers - Zero::zero(), // relay parent - One::one(), // relay chain block this was backed in - group_idx, // backing group + core_idx, // core + candidate_hash, // hash + Self::candidate_descriptor_mock(candidate_descriptor_v2), // candidate descriptor + commitments, // commitments + availability_votes, // availability votes + Default::default(), // backers + Zero::zero(), // relay parent + One::one(), /* relay chain block this + * was backed in */ + group_idx, // backing group ) } @@ -373,6 +405,7 @@ impl BenchBuilder { group_idx: GroupIndex, availability_votes: BitVec, candidate_hash: CandidateHash, + candidate_descriptor_v2: bool, ) { let commitments = CandidateCommitments:: { upward_messages: Default::default(), @@ -388,6 +421,7 @@ impl BenchBuilder { candidate_hash, availability_votes, commitments, + candidate_descriptor_v2, ); inclusion::PendingAvailability::::mutate(para_id, |maybe_candidates| { if let Some(candidates) = maybe_candidates { @@ -547,6 +581,7 @@ impl BenchBuilder { // No validators have made this candidate available yet. bitvec::bitvec![u8, bitvec::order::Lsb0; 0; validators.len()], CandidateHash(H256::from(byte32_slice_from(current_core_idx))), + self.candidate_descriptor_v2, ); if !self.unavailable_cores.contains(¤t_core_idx) { concluding_cores.insert(current_core_idx); @@ -654,7 +689,7 @@ impl BenchBuilder { para_id, relay_parent, core_idx, - 1, + self.target_session, persisted_validation_data_hash, pov_hash, Default::default(), @@ -676,7 +711,7 @@ impl BenchBuilder { .into() }; - let candidate = CommittedCandidateReceipt:: { + let mut candidate = CommittedCandidateReceipt:: { descriptor, commitments: CandidateCommitments:: { upward_messages: Default::default(), @@ -689,6 +724,27 @@ impl BenchBuilder { }, }; + if self.candidate_descriptor_v2 { + // `UMPSignal` separator. + candidate.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // `SelectCore` commitment. + // Claim queue offset must be `0` so this candidate is for the very + // next block. + candidate.commitments.upward_messages.force_push( + UMPSignal::SelectCore( + CoreSelector(chain_idx as u8), + ClaimQueueOffset(0), + ) + .encode(), + ); + } + + // Maybe apply the candidate modifier + if let Some(modifier) = self.candidate_modifier { + candidate = modifier(candidate); + } + let candidate_hash = candidate.hash(); let validity_votes: Vec<_> = group_validators @@ -708,12 +764,15 @@ impl BenchBuilder { }) .collect(); - // Check if the elastic scaling bit is set, if so we need to supply the core - // index in the generated candidate. - let core_idx = configuration::ActiveConfig::::get() - .node_features - .get(FeatureIndex::ElasticScalingMVP as usize) - .map(|_the_bit| core_idx); + // Don't inject core when it is available in descriptor. + let core_idx = if candidate.descriptor.core_index().is_some() { + None + } else { + configuration::ActiveConfig::::get() + .node_features + .get(FeatureIndex::ElasticScalingMVP as usize) + .and_then(|the_bit| if *the_bit { Some(core_idx) } else { None }) + }; BackedCandidate::::new( candidate, @@ -766,6 +825,7 @@ impl BenchBuilder { group_idx, Self::validator_availability_votes_yes(validators.len()), candidate_hash, + self.candidate_descriptor_v2, ); let statements_len = diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index cc333e3b20f8..36f874b8db1e 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -440,6 +440,11 @@ pub(crate) enum UmpAcceptanceCheckErr { TotalSizeExceeded { total_size: u64, limit: u64 }, /// A para-chain cannot send UMP messages while it is offboarding. IsOffboarding, + /// The allowed number of `UMPSignal` messages in the queue was exceeded. + /// Currenly only one such message is allowed. + TooManyUMPSignals { count: u32 }, + /// The UMP queue contains an invalid `UMPSignal` + NoUmpSignal, } impl fmt::Debug for UmpAcceptanceCheckErr { @@ -468,6 +473,12 @@ impl fmt::Debug for UmpAcceptanceCheckErr { UmpAcceptanceCheckErr::IsOffboarding => { write!(fmt, "upward message rejected because the para is off-boarding") }, + UmpAcceptanceCheckErr::TooManyUMPSignals { count } => { + write!(fmt, "the ump queue has too many `UMPSignal` messages ({} > 1 )", count) + }, + UmpAcceptanceCheckErr::NoUmpSignal => { + write!(fmt, "Required UMP signal not found") + }, } } } @@ -935,6 +946,27 @@ impl Pallet { para: ParaId, upward_messages: &[UpwardMessage], ) -> Result<(), UmpAcceptanceCheckErr> { + // Filter any pending UMP signals and the separator. + let upward_messages = if let Some(separator_index) = + upward_messages.iter().position(|message| message.is_empty()) + { + let (upward_messages, ump_signals) = upward_messages.split_at(separator_index); + + if ump_signals.len() > 2 { + return Err(UmpAcceptanceCheckErr::TooManyUMPSignals { + count: ump_signals.len() as u32, + }) + } + + if ump_signals.len() == 1 { + return Err(UmpAcceptanceCheckErr::NoUmpSignal) + } + + upward_messages + } else { + upward_messages + }; + // Cannot send UMP messages while off-boarding. if paras::Pallet::::is_offboarding(para) { ensure!(upward_messages.is_empty(), UmpAcceptanceCheckErr::IsOffboarding); @@ -989,6 +1021,8 @@ impl Pallet { pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: &[Vec]) { let bounded = upward_messages .iter() + // Stop once we hit the `UMPSignal` separator. + .take_while(|message| !message.is_empty()) .filter_map(|d| { BoundedSlice::try_from(&d[..]) .inspect_err(|_| { @@ -1257,17 +1291,17 @@ impl CandidateCheckContext { let relay_parent = backed_candidate_receipt.descriptor.relay_parent(); // Check that the relay-parent is one of the allowed relay-parents. - let (relay_parent_storage_root, relay_parent_number) = { + let (state_root, relay_parent_number) = { match allowed_relay_parents.acquire_info(relay_parent, self.prev_context) { None => return Err(Error::::DisallowedRelayParent), - Some(info) => info, + Some((info, relay_parent_number)) => (info.state_root, relay_parent_number), } }; { let persisted_validation_data = make_persisted_validation_data_with_parent::( relay_parent_number, - relay_parent_storage_root, + state_root, parent_head_data, ); diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 59114e28be16..87d21e209a49 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -26,8 +26,13 @@ use crate::{ shared::AllowedRelayParentsTracker, }; use polkadot_primitives::{ - effective_minimum_backing_votes, AvailabilityBitfield, CandidateDescriptor, - SignedAvailabilityBitfields, UncheckedSignedAvailabilityBitfields, + effective_minimum_backing_votes, + vstaging::{ + CandidateDescriptorV2, CandidateDescriptorVersion, ClaimQueueOffset, CoreSelector, + UMPSignal, UMP_SEPARATOR, + }, + AvailabilityBitfield, CandidateDescriptor, SignedAvailabilityBitfields, + UncheckedSignedAvailabilityBitfields, }; use assert_matches::assert_matches; @@ -83,7 +88,7 @@ fn default_allowed_relay_parent_tracker() -> AllowedRelayParentsTracker, pub(crate) validation_code: ValidationCode, pub(crate) hrmp_watermark: BlockNumber, + /// Creates a v2 descriptor if set. + pub(crate) core_index: Option, + /// The core selector to use. + pub(crate) core_selector: Option, } impl std::default::Default for TestCandidateBuilder { @@ -277,14 +286,28 @@ impl std::default::Default for TestCandidateBuilder { new_validation_code: None, validation_code: dummy_validation_code(), hrmp_watermark: 0u32.into(), + core_index: None, + core_selector: None, } } } impl TestCandidateBuilder { pub(crate) fn build(self) -> CommittedCandidateReceipt { - CommittedCandidateReceipt { - descriptor: CandidateDescriptor { + let descriptor = if let Some(core_index) = self.core_index { + CandidateDescriptorV2::new( + self.para_id, + self.relay_parent, + core_index, + 0, + self.persisted_validation_data_hash, + self.pov_hash, + Default::default(), + self.para_head_hash.unwrap_or_else(|| self.head_data.hash()), + self.validation_code.hash(), + ) + } else { + CandidateDescriptor { para_id: self.para_id, pov_hash: self.pov_hash, relay_parent: self.relay_parent, @@ -301,14 +324,31 @@ impl TestCandidateBuilder { ) .expect("32 bytes; qed"), } - .into(), + .into() + }; + let mut ccr = CommittedCandidateReceipt { + descriptor, commitments: CandidateCommitments { head_data: self.head_data, new_validation_code: self.new_validation_code, hrmp_watermark: self.hrmp_watermark, ..Default::default() }, + }; + + if ccr.descriptor.version() == CandidateDescriptorVersion::V2 { + ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + ccr.commitments.upward_messages.force_push( + UMPSignal::SelectCore( + CoreSelector(self.core_selector.unwrap_or_default()), + ClaimQueueOffset(0), + ) + .encode(), + ); } + + ccr } } @@ -2497,18 +2537,21 @@ fn check_allowed_relay_parents() { allowed_relay_parents.update( relay_parent_a.1, Hash::zero(), + Default::default(), relay_parent_a.0, max_ancestry_len, ); allowed_relay_parents.update( relay_parent_b.1, Hash::zero(), + Default::default(), relay_parent_b.0, max_ancestry_len, ); allowed_relay_parents.update( relay_parent_c.1, Hash::zero(), + Default::default(), relay_parent_c.0, max_ancestry_len, ); diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index 70c4ba72d58e..2aca0f2c728a 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -45,6 +45,7 @@ use frame_support::{ pallet_prelude::*, traits::Randomness, }; + use frame_system::pallet_prelude::*; use pallet_babe::{self, ParentBlockRandomness}; use polkadot_primitives::{ @@ -332,22 +333,6 @@ impl Pallet { let now = frame_system::Pallet::::block_number(); let config = configuration::ActiveConfig::::get(); - // Before anything else, update the allowed relay-parents. - { - let parent_number = now - One::one(); - let parent_storage_root = *parent_header.state_root(); - - shared::AllowedRelayParents::::mutate(|tracker| { - tracker.update( - parent_hash, - parent_storage_root, - parent_number, - config.async_backing_params.allowed_ancestry_len, - ); - }); - } - let allowed_relay_parents = shared::AllowedRelayParents::::get(); - let candidates_weight = backed_candidates_weight::(&backed_candidates); let bitfields_weight = signed_bitfields_weight::(&bitfields); let disputes_weight = multi_dispute_statement_sets_weight::(&disputes); @@ -593,6 +578,29 @@ impl Pallet { METRICS.on_candidates_processed_total(backed_candidates.len() as u64); + // After freeing cores and filling claims, but before processing backed candidates + // we update the allowed relay-parents. + { + let parent_number = now - One::one(); + let parent_storage_root = *parent_header.state_root(); + + shared::AllowedRelayParents::::mutate(|tracker| { + tracker.update( + parent_hash, + parent_storage_root, + scheduler::ClaimQueue::::get() + .into_iter() + .map(|(core_index, paras)| { + (core_index, paras.into_iter().map(|e| e.para_id()).collect()) + }) + .collect(), + parent_number, + config.async_backing_params.allowed_ancestry_len, + ); + }); + } + let allowed_relay_parents = shared::AllowedRelayParents::::get(); + let core_index_enabled = configuration::ActiveConfig::::get() .node_features .get(FeatureIndex::ElasticScalingMVP as usize) @@ -972,6 +980,86 @@ pub(crate) fn sanitize_bitfields( bitfields } +/// Perform required checks for given candidate receipt. +/// +/// Returns `true` if candidate descriptor is version 1. +/// +/// Otherwise returns `false` if: +/// - version 2 descriptors are not allowed +/// - the core index in descriptor doesn't match the one computed from the commitments +/// - the `SelectCore` signal does not refer to a core at the top of claim queue +fn sanitize_backed_candidate_v2( + candidate: &BackedCandidate, + allowed_relay_parents: &AllowedRelayParentsTracker>, + allow_v2_receipts: bool, +) -> bool { + if candidate.descriptor().version() == CandidateDescriptorVersion::V1 { + return true + } + + // It is mandatory to filter these before calling `filter_unchained_candidates` to ensure + // any v1 descendants of v2 candidates are dropped. + if !allow_v2_receipts { + log::debug!( + target: LOG_TARGET, + "V2 candidate descriptors not allowed. Dropping candidate {:?} for paraid {:?}.", + candidate.candidate().hash(), + candidate.descriptor().para_id() + ); + return false + } + + let Some(session_index) = candidate.descriptor().session_index() else { + log::debug!( + target: LOG_TARGET, + "Invalid V2 candidate receipt {:?} for paraid {:?}, missing session index.", + candidate.candidate().hash(), + candidate.descriptor().para_id(), + ); + return false + }; + + // Check if session index is equal to current session index. + if session_index != shared::CurrentSessionIndex::::get() { + log::debug!( + target: LOG_TARGET, + "Dropping V2 candidate receipt {:?} for paraid {:?}, invalid session index {}, current session {}", + candidate.candidate().hash(), + candidate.descriptor().para_id(), + session_index, + shared::CurrentSessionIndex::::get() + ); + return false + } + + // Get the claim queue snapshot at the candidate relay parent. + let Some((rp_info, _)) = + allowed_relay_parents.acquire_info(candidate.descriptor().relay_parent(), None) + else { + log::debug!( + target: LOG_TARGET, + "Relay parent {:?} for candidate {:?} is not in the allowed relay parents.", + candidate.descriptor().relay_parent(), + candidate.candidate().hash(), + ); + return false + }; + + // Check validity of `core_index`. + if let Err(err) = candidate.candidate().check_core_index(&rp_info.claim_queue) { + log::debug!( + target: LOG_TARGET, + "Dropping candidate {:?} for paraid {:?}, {:?}", + candidate.candidate().hash(), + candidate.descriptor().para_id(), + err, + ); + + return false + } + true +} + /// Performs various filtering on the backed candidates inherent data. /// Must maintain the invariant that the returned candidate collection contains the candidates /// sorted in dependency order for each para. When doing any filtering, we must therefore drop any @@ -1001,18 +1089,10 @@ fn sanitize_backed_candidates( // Map the candidates to the right paraids, while making sure that the order between candidates // of the same para is preserved. let mut candidates_per_para: BTreeMap> = BTreeMap::new(); + for candidate in backed_candidates { - // Drop any v2 candidate receipts if nodes are not allowed to use them. - // It is mandatory to filter these before calling `filter_unchained_candidates` to ensure - // any v1 descendants of v2 candidates are dropped. - if !allow_v2_receipts && candidate.descriptor().version() == CandidateDescriptorVersion::V2 + if !sanitize_backed_candidate_v2::(&candidate, allowed_relay_parents, allow_v2_receipts) { - log::debug!( - target: LOG_TARGET, - "V2 candidate descriptors not allowed. Dropping candidate {:?} for paraid {:?}.", - candidate.candidate().hash(), - candidate.descriptor().para_id() - ); continue } @@ -1210,6 +1290,7 @@ fn filter_backed_statements_from_disabled_validators< // 1. Core index assigned to the parachain which has produced the candidate // 2. The relay chain block number of the candidate retain_candidates::(backed_candidates_with_core, |para_id, (bc, core_idx)| { + // `CoreIndex` not used, we just need a copy to write it back later. let (validator_indices, maybe_core_index) = bc.validator_indices_and_core_index(core_index_enabled); let mut validator_indices = BitVec::<_>::from(validator_indices); @@ -1368,8 +1449,8 @@ fn filter_unchained_candidates= 1 && core_index_enabled { - // We must preserve the dependency order given in the input. - let mut temp_backed_candidates = Vec::with_capacity(scheduled_cores.len()); - - for candidate in backed_candidates { - if scheduled_cores.len() == 0 { - // We've got candidates for all of this para's assigned cores. Move on to - // the next para. - log::debug!( - target: LOG_TARGET, - "Found enough candidates for paraid: {:?}.", - candidate.descriptor().para_id() - ); - break; - } - let maybe_injected_core_index: Option = - get_injected_core_index::(allowed_relay_parents, &candidate); - - if let Some(core_index) = maybe_injected_core_index { - if scheduled_cores.remove(&core_index) { - temp_backed_candidates.push((candidate, core_index)); - } else { - // if we got a candidate for a core index which is not scheduled, stop - // the work for this para. the already processed candidate chain in - // temp_backed_candidates is still fine though. - log::debug!( - target: LOG_TARGET, - "Found a backed candidate {:?} with injected core index {}, which is not scheduled for paraid {:?}.", - candidate.candidate().hash(), - core_index.0, - candidate.descriptor().para_id() - ); - - break; - } - } else { - // if we got a candidate which does not contain its core index, stop the - // work for this para. the already processed candidate chain in - // temp_backed_candidates is still fine though. - - log::debug!( - target: LOG_TARGET, - "Found a backed candidate {:?} with no injected core index, for paraid {:?} which has multiple scheduled cores.", - candidate.candidate().hash(), - candidate.descriptor().para_id() - ); - - break; - } - } - - if !temp_backed_candidates.is_empty() { - backed_candidates_with_core - .entry(para_id) - .or_insert_with(|| vec![]) - .extend(temp_backed_candidates); + if let Some(core_index) = + get_core_index::(core_index_enabled, allowed_relay_parents, &candidate) + { + if scheduled_cores.remove(&core_index) { + temp_backed_candidates.push((candidate, core_index)); + } else { + // if we got a candidate for a core index which is not scheduled, stop + // the work for this para. the already processed candidate chain in + // temp_backed_candidates is still fine though. + log::debug!( + target: LOG_TARGET, + "Found a backed candidate {:?} with core index {}, which is not scheduled for paraid {:?}.", + candidate.candidate().hash(), + core_index.0, + candidate.descriptor().para_id() + ); + + break; } } else { - log::warn!( + // No core index is fine, if para has just 1 core assigned. + if scheduled_cores.len() == 1 { + temp_backed_candidates + .push((candidate, scheduled_cores.pop_first().expect("Length is 1"))); + break; + } + + // if we got a candidate which does not contain its core index, stop the + // work for this para. the already processed candidate chain in + // temp_backed_candidates is still fine though. + + log::debug!( target: LOG_TARGET, - "Found a paraid {:?} which has multiple scheduled cores but ElasticScalingMVP feature is not enabled: {:?}", - para_id, - scheduled_cores + "Found a backed candidate {:?} without core index information, but paraid {:?} has multiple scheduled cores.", + candidate.candidate().hash(), + candidate.descriptor().para_id() ); + + break; } - } else { - log::debug!( - target: LOG_TARGET, - "Paraid: {:?} has no entry in scheduled cores but {} candidates were supplied.", - para_id, - backed_candidates.len() - ); + } + + if !temp_backed_candidates.is_empty() { + backed_candidates_with_core + .entry(para_id) + .or_insert_with(|| vec![]) + .extend(temp_backed_candidates); } } backed_candidates_with_core } +// Must be called only for candidates that have been sanitized already. +fn get_core_index( + core_index_enabled: bool, + allowed_relay_parents: &AllowedRelayParentsTracker>, + candidate: &BackedCandidate, +) -> Option { + candidate.candidate().descriptor.core_index().or_else(|| { + get_injected_core_index::(core_index_enabled, allowed_relay_parents, &candidate) + }) +} + fn get_injected_core_index( + core_index_enabled: bool, allowed_relay_parents: &AllowedRelayParentsTracker>, candidate: &BackedCandidate, ) -> Option { // After stripping the 8 bit extensions, the `validator_indices` field length is expected // to be equal to backing group size. If these don't match, the `CoreIndex` is badly encoded, // or not supported. - let (validator_indices, maybe_core_idx) = candidate.validator_indices_and_core_index(true); + let (validator_indices, maybe_core_idx) = + candidate.validator_indices_and_core_index(core_index_enabled); let Some(core_idx) = maybe_core_idx else { return None }; diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index ac42ac1611df..f5c3d5077764 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -44,8 +44,11 @@ fn default_config() -> MockGenesisConfig { #[cfg(not(feature = "runtime-benchmarks"))] mod enter { use super::{inclusion::tests::TestCandidateBuilder, *}; + use polkadot_primitives::vstaging::{ClaimQueueOffset, CoreSelector, UMPSignal, UMP_SEPARATOR}; + use rstest::rstest; + use crate::{ - builder::{Bench, BenchBuilder}, + builder::{junk_collator, junk_collator_signature, Bench, BenchBuilder, CandidateModifier}, mock::{mock_assigner, new_test_ext, BlockLength, BlockWeights, RuntimeOrigin, Test}, scheduler::{ common::{Assignment, AssignmentProvider}, @@ -59,7 +62,8 @@ mod enter { use frame_support::assert_ok; use frame_system::limits; use polkadot_primitives::{ - vstaging::InternalVersion, AvailabilityBitfield, SchedulerParams, UncheckedSigned, + vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2, InternalVersion}, + AvailabilityBitfield, CandidateDescriptor, UncheckedSigned, }; use sp_runtime::Perbill; @@ -73,6 +77,7 @@ mod enter { elastic_paras: BTreeMap, unavailable_cores: Vec, v2_descriptor: bool, + candidate_modifier: Option::Hash>>, } fn make_inherent_data( @@ -86,6 +91,7 @@ mod enter { elastic_paras, unavailable_cores, v2_descriptor, + candidate_modifier, }: TestConfig, ) -> Bench { let extra_cores = elastic_paras @@ -104,7 +110,8 @@ mod enter { .set_dispute_sessions(&dispute_sessions[..]) .set_fill_claimqueue(fill_claimqueue) .set_unavailable_cores(unavailable_cores) - .set_candidate_descriptor_v2(v2_descriptor); + .set_candidate_descriptor_v2(v2_descriptor) + .set_candidate_modifier(candidate_modifier); // Setup some assignments as needed: mock_assigner::Pallet::::set_core_count(builder.max_cores()); @@ -126,15 +133,25 @@ mod enter { } } - #[test] + #[rstest] + #[case(true)] + #[case(false)] // Validate that if we create 2 backed candidates which are assigned to 2 cores that will be // freed via becoming fully available, the backed candidates will not be filtered out in // `create_inherent` and will not cause `enter` to early. - fn include_backed_candidates() { + fn include_backed_candidates(#[case] v2_descriptor: bool) { let config = MockGenesisConfig::default(); assert!(config.configuration.config.scheduler_params.lookahead > 0); new_test_ext(config).execute_with(|| { + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + v2_descriptor, + ) + .unwrap(); + let dispute_statements = BTreeMap::new(); let mut backed_and_concluding = BTreeMap::new(); @@ -147,10 +164,11 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: false, + fill_claimqueue: true, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], - v2_descriptor: false, + v2_descriptor, + candidate_modifier: None, }); // We expect the scenario to have cores 0 & 1 with pending availability. The backed @@ -171,9 +189,6 @@ mod enter { .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); - // The current schedule is empty prior to calling `create_inherent_enter`. - assert!(scheduler::Pallet::::claim_queue_is_empty()); - // Nothing is filtered out (including the backed candidates.) assert_eq!( Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(), @@ -212,8 +227,14 @@ mod enter { }); } - #[test] - fn include_backed_candidates_elastic_scaling() { + #[rstest] + #[case(true, false)] + #[case(true, true)] + #[case(false, true)] + fn include_backed_candidates_elastic_scaling( + #[case] v2_descriptor: bool, + #[case] injected_core: bool, + ) { // ParaId 0 has one pending candidate on core 0. // ParaId 1 has one pending candidate on core 1. // ParaId 2 has three pending candidates on cores 2, 3 and 4. @@ -226,7 +247,15 @@ mod enter { configuration::Pallet::::set_node_feature( RuntimeOrigin::root(), FeatureIndex::ElasticScalingMVP as u8, - true, + injected_core, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + v2_descriptor, ) .unwrap(); @@ -243,10 +272,11 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: false, + fill_claimqueue: true, elastic_paras: [(2, 3)].into_iter().collect(), unavailable_cores: vec![], - v2_descriptor: false, + v2_descriptor, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -263,9 +293,6 @@ mod enter { .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); - // The current schedule is empty prior to calling `create_inherent_enter`. - assert!(scheduler::Pallet::::claim_queue_is_empty()); - assert!(pallet::OnChainVotes::::get().is_none()); // Nothing is filtered out (including the backed candidates.) @@ -352,6 +379,7 @@ mod enter { elastic_paras: [(2, 4)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), v2_descriptor: false, + candidate_modifier: None, }); let mut expected_para_inherent_data = scenario.data.clone(); @@ -609,6 +637,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -683,6 +712,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -755,6 +785,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -843,6 +874,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -931,6 +963,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -991,6 +1024,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1078,6 +1112,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1186,6 +1221,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1255,6 +1291,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1322,6 +1359,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1388,6 +1426,15 @@ mod enter { ccr.commitments.processed_downward_messages = idx as u32; let core_index = start_core_index + idx; + // `UMPSignal` separator. + ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // `SelectCore` commitment. + // Claim queue offset must be `0`` so this candidate is for the very next block. + ccr.commitments.upward_messages.force_push( + UMPSignal::SelectCore(CoreSelector(idx as u8), ClaimQueueOffset(0)).encode(), + ); + BackedCandidate::new( ccr.into(), Default::default(), @@ -1400,8 +1447,10 @@ mod enter { // Ensure that overweight parachain inherents are always rejected by the runtime. // Runtime should panic and return `InherentOverweight` error. - #[test] - fn test_backed_candidates_apply_weight_works_for_elastic_scaling() { + #[rstest] + #[case(true)] + #[case(false)] + fn test_backed_candidates_apply_weight_works_for_elastic_scaling(#[case] v2_descriptor: bool) { new_test_ext(MockGenesisConfig::default()).execute_with(|| { let seed = [ 1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3, @@ -1412,6 +1461,14 @@ mod enter { // Create an overweight inherent and oversized block let mut backed_and_concluding = BTreeMap::new(); + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + v2_descriptor, + ) + .unwrap(); + for i in 0..30 { backed_and_concluding.insert(i, i); } @@ -1425,7 +1482,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], - v2_descriptor: false, + v2_descriptor, + candidate_modifier: None, }); let mut para_inherent_data = scenario.data.clone(); @@ -1516,6 +1574,7 @@ mod enter { elastic_paras: BTreeMap::new(), unavailable_cores: vec![], v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1572,10 +1631,10 @@ mod enter { num_validators_per_core: 5, code_upgrade: None, fill_claimqueue: true, - // 8 cores ! elastic_paras: [(2, 8)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), v2_descriptor: true, + candidate_modifier: None, }); let mut unfiltered_para_inherent_data = scenario.data.clone(); @@ -1614,6 +1673,432 @@ mod enter { assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); }); } + + #[test] + fn too_many_ump_signals() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 8)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: Some(|mut candidate: CommittedCandidateReceiptV2| { + if candidate.descriptor.para_id() == 2.into() { + // Add an extra message so `verify_backed_candidates` fails. + candidate.commitments.upward_messages.force_push( + UMPSignal::SelectCore(CoreSelector(123 as u8), ClaimQueueOffset(2)) + .encode(), + ); + } + candidate + }), + }); + + let unfiltered_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 10 v2 candidate descriptors. + assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data) + .unwrap(); + + let dispatch_error = Pallet::::enter( + frame_system::RawOrigin::None.into(), + unfiltered_para_inherent_data, + ) + .unwrap_err() + .error; + + // We expect `enter` to fail because the inherent data contains backed candidates with + // v2 descriptors. + assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + }); + } + + #[test] + fn invalid_ump_signals() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 8)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: Some(|mut candidate: CommittedCandidateReceiptV2| { + if candidate.descriptor.para_id() == 1.into() { + // Drop the core selector to make it invalid + candidate + .commitments + .upward_messages + .truncate(candidate.commitments.upward_messages.len() - 1); + } + candidate + }), + }); + + let unfiltered_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 10 v2 candidate descriptors. + assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data) + .unwrap(); + + let dispatch_error = Pallet::::enter( + frame_system::RawOrigin::None.into(), + unfiltered_para_inherent_data, + ) + .unwrap_err() + .error; + + // We expect `enter` to fail because the inherent data contains backed candidates with + // v2 descriptors. + assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + }); + } + #[test] + fn v2_descriptors_are_accepted() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 3)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: None, + }); + + let inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (2 validators per core, 5 backed candidates) + assert_eq!(inherent_data.bitfields.len(), 5); + // * 5 v2 candidate descriptors. + assert_eq!(inherent_data.backed_candidates.len(), 5); + + Pallet::::enter(frame_system::RawOrigin::None.into(), inherent_data).unwrap(); + }); + } + + // Test when parachain runtime is upgraded to support the new commitments + // but some collators are not and provide v1 descriptors. + #[test] + fn elastic_scaling_mixed_v1_v2_descriptors() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 3)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: None, + }); + + let mut inherent_data = scenario.data.clone(); + let candidate_count = inherent_data.backed_candidates.len(); + + // Make last 2 candidates v1 + for index in candidate_count - 2..candidate_count { + let encoded = inherent_data.backed_candidates[index].descriptor().encode(); + + let mut decoded: CandidateDescriptor = + Decode::decode(&mut encoded.as_slice()).unwrap(); + decoded.collator = junk_collator(); + decoded.signature = junk_collator_signature(); + + *inherent_data.backed_candidates[index].descriptor_mut() = + Decode::decode(&mut encoded.as_slice()).unwrap(); + } + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (2 validators per core, 5 backed candidates) + assert_eq!(inherent_data.bitfields.len(), 5); + // * 5 v2 candidate descriptors. + assert_eq!(inherent_data.backed_candidates.len(), 5); + + Pallet::::enter(frame_system::RawOrigin::None.into(), inherent_data).unwrap(); + }); + } + + // Mixed test with v1, v2 with/without `UMPSignal::SelectCore` + #[test] + fn mixed_v1_and_v2_optional_commitments() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + backed_and_concluding.insert(3, 1); + backed_and_concluding.insert(4, 1); + + let unavailable_cores = vec![]; + + let candidate_modifier = |mut candidate: CommittedCandidateReceiptV2| { + // first candidate has v2 descriptor with no commitments + if candidate.descriptor.para_id() == ParaId::from(0) { + candidate.commitments.upward_messages.clear(); + } + + if candidate.descriptor.para_id() > ParaId::from(2) { + let mut v1: CandidateDescriptor = candidate.descriptor.into(); + + v1.collator = junk_collator(); + v1.signature = junk_collator_signature(); + + candidate.descriptor = v1.into(); + } + candidate + }; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: Default::default(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: Some(candidate_modifier), + }); + + let inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (2 validators per core, 5 backed candidates) + assert_eq!(inherent_data.bitfields.len(), 5); + // * 5 v2 candidate descriptors. + assert_eq!(inherent_data.backed_candidates.len(), 5); + + Pallet::::enter(frame_system::RawOrigin::None.into(), inherent_data).unwrap(); + }); + } + + // A test to ensure that the `paras_inherent` filters out candidates with invalid + // session index in the descriptor. + #[test] + fn invalid_session_index() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 3)].into_iter().collect(), + unavailable_cores, + v2_descriptor: true, + candidate_modifier: None, + }); + + let mut inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (2 validators per core, 5 backed candidates) + assert_eq!(inherent_data.bitfields.len(), 5); + // * 5 v2 candidate descriptors passed, 1 is invalid + assert_eq!(inherent_data.backed_candidates.len(), 5); + + let index = inherent_data.backed_candidates.len() - 1; + + // Put invalid session index in last candidate + let backed_candidate = inherent_data.backed_candidates[index].clone(); + + let candidate = CommittedCandidateReceiptV2 { + descriptor: CandidateDescriptorV2::new( + backed_candidate.descriptor().para_id(), + backed_candidate.descriptor().relay_parent(), + backed_candidate.descriptor().core_index().unwrap(), + 100, + backed_candidate.descriptor().persisted_validation_data_hash(), + backed_candidate.descriptor().pov_hash(), + backed_candidate.descriptor().erasure_root(), + backed_candidate.descriptor().para_head(), + backed_candidate.descriptor().validation_code_hash(), + ), + commitments: backed_candidate.candidate().commitments.clone(), + }; + + inherent_data.backed_candidates[index] = BackedCandidate::new( + candidate, + backed_candidate.validity_votes().to_vec(), + backed_candidate.validator_indices_and_core_index(false).0.into(), + None, + ); + + let mut expected_inherent_data = inherent_data.clone(); + expected_inherent_data.backed_candidates.truncate(index); + + let mut create_inherent_data = InherentData::new(); + create_inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &inherent_data) + .unwrap(); + + // 1 candidate with invalid session is filtered out + assert_eq!( + Pallet::::create_inherent_inner(&create_inherent_data).unwrap(), + expected_inherent_data + ); + + Pallet::::enter(frame_system::RawOrigin::None.into(), inherent_data).unwrap_err(); + }); + } } fn default_header() -> polkadot_primitives::Header { @@ -1913,6 +2398,7 @@ mod sanitizers { shared::Pallet::::add_allowed_relay_parent( default_header().hash(), Default::default(), + Default::default(), RELAY_PARENT_NUM, 1, ); @@ -2101,18 +2587,12 @@ mod sanitizers { // Para 6 is not scheduled. One candidate supplied. // Para 7 is scheduled on core 7 and 8, but the candidate contains the wrong core index. // Para 8 is scheduled on core 9, but the candidate contains the wrong core index. - fn get_test_data_multiple_cores_per_para(core_index_enabled: bool) -> TestData { + fn get_test_data_multiple_cores_per_para( + core_index_enabled: bool, + v2_descriptor: bool, + ) -> TestData { const RELAY_PARENT_NUM: u32 = 3; - // Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing - // votes) won't behave correctly - shared::Pallet::::add_allowed_relay_parent( - default_header().hash(), - Default::default(), - RELAY_PARENT_NUM, - 1, - ); - let header = default_header(); let relay_parent = header.hash(); let session_index = SessionIndex::from(0_u32); @@ -2231,6 +2711,21 @@ mod sanitizers { ), ])); + // Add the relay parent to `shared` pallet. Otherwise some code (e.g. filtering backing + // votes) won't behave correctly + shared::Pallet::::add_allowed_relay_parent( + relay_parent, + Default::default(), + scheduler::ClaimQueue::::get() + .into_iter() + .map(|(core_index, paras)| { + (core_index, paras.into_iter().map(|e| e.para_id()).collect()) + }) + .collect(), + RELAY_PARENT_NUM, + 1, + ); + // Set the on-chain included head data and current code hash. for id in 1..=8u32 { paras::Pallet::::set_current_head(ParaId::from(id), HeadData(vec![id as u8])); @@ -2260,6 +2755,14 @@ mod sanitizers { let mut backed_candidates = vec![]; let mut expected_backed_candidates_with_core = BTreeMap::new(); + let maybe_core_index = |core_index: CoreIndex| -> Option { + if !v2_descriptor { + None + } else { + Some(core_index) + } + }; + // Para 1 { let candidate = TestCandidateBuilder { @@ -2276,6 +2779,7 @@ mod sanitizers { hrmp_watermark: RELAY_PARENT_NUM, head_data: HeadData(vec![1, 1]), validation_code: ValidationCode(vec![1]), + core_index: maybe_core_index(CoreIndex(0)), ..Default::default() } .build(); @@ -2291,7 +2795,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(0 as u32)), ); backed_candidates.push(backed.clone()); - if core_index_enabled { + if core_index_enabled || v2_descriptor { expected_backed_candidates_with_core .entry(ParaId::from(1)) .or_insert(vec![]) @@ -2312,6 +2816,8 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![1]), + core_index: maybe_core_index(CoreIndex(1)), + core_selector: Some(1), ..Default::default() } .build(); @@ -2326,7 +2832,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(1 as u32)), ); backed_candidates.push(backed.clone()); - if core_index_enabled { + if core_index_enabled || v2_descriptor { expected_backed_candidates_with_core .entry(ParaId::from(1)) .or_insert(vec![]) @@ -2349,6 +2855,7 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![2]), + core_index: maybe_core_index(CoreIndex(2)), ..Default::default() } .build(); @@ -2363,7 +2870,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(2 as u32)), ); backed_candidates.push(backed.clone()); - if core_index_enabled { + if core_index_enabled || v2_descriptor { expected_backed_candidates_with_core .entry(ParaId::from(2)) .or_insert(vec![]) @@ -2386,6 +2893,7 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![3]), + core_index: maybe_core_index(CoreIndex(4)), ..Default::default() } .build(); @@ -2421,6 +2929,7 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![4]), + core_index: maybe_core_index(CoreIndex(5)), ..Default::default() } .build(); @@ -2455,6 +2964,7 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![4]), + core_index: maybe_core_index(CoreIndex(5)), ..Default::default() } .build(); @@ -2488,6 +2998,7 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![6]), + core_index: maybe_core_index(CoreIndex(6)), ..Default::default() } .build(); @@ -2519,6 +3030,7 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![7]), + core_index: maybe_core_index(CoreIndex(6)), ..Default::default() } .build(); @@ -2550,6 +3062,7 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![8]), + core_index: maybe_core_index(CoreIndex(7)), ..Default::default() } .build(); @@ -2564,7 +3077,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(7 as u32)), ); backed_candidates.push(backed.clone()); - if !core_index_enabled { + if !core_index_enabled && !v2_descriptor { expected_backed_candidates_with_core .entry(ParaId::from(8)) .or_insert(vec![]) @@ -2625,13 +3138,6 @@ mod sanitizers { let header = default_header(); let relay_parent = header.hash(); - shared::Pallet::::add_allowed_relay_parent( - relay_parent, - Default::default(), - RELAY_PARENT_NUM, - 1, - ); - let session_index = SessionIndex::from(0_u32); let keystore = LocalKeystore::in_memory(); @@ -2743,6 +3249,19 @@ mod sanitizers { ), ])); + shared::Pallet::::add_allowed_relay_parent( + relay_parent, + Default::default(), + scheduler::ClaimQueue::::get() + .into_iter() + .map(|(core_index, paras)| { + (core_index, paras.into_iter().map(|e| e.para_id()).collect()) + }) + .collect(), + RELAY_PARENT_NUM, + 1, + ); + // Set the on-chain included head data and current code hash. for id in 1..=4u32 { paras::Pallet::::set_current_head(ParaId::from(id), HeadData(vec![id as u8])); @@ -3128,6 +3647,7 @@ mod sanitizers { shared::Pallet::::add_allowed_relay_parent( prev_relay_parent, Default::default(), + Default::default(), RELAY_PARENT_NUM - 1, 2, ); @@ -3135,6 +3655,7 @@ mod sanitizers { shared::Pallet::::add_allowed_relay_parent( relay_parent, Default::default(), + Default::default(), RELAY_PARENT_NUM, 2, ); @@ -3142,6 +3663,7 @@ mod sanitizers { shared::Pallet::::add_allowed_relay_parent( next_relay_parent, Default::default(), + Default::default(), RELAY_PARENT_NUM + 1, 2, ); @@ -3534,15 +4056,20 @@ mod sanitizers { } #[rstest] - #[case(false)] - #[case(true)] - fn test_with_multiple_cores_per_para(#[case] core_index_enabled: bool) { + #[case(false, false)] + #[case(true, false)] + #[case(false, true)] + #[case(true, true)] + fn test_with_multiple_cores_per_para( + #[case] core_index_enabled: bool, + #[case] v2_descriptor: bool, + ) { new_test_ext(default_config()).execute_with(|| { let TestData { backed_candidates, expected_backed_candidates_with_core, scheduled_paras: scheduled, - } = get_test_data_multiple_cores_per_para(core_index_enabled); + } = get_test_data_multiple_cores_per_para(core_index_enabled, v2_descriptor); assert_eq!( sanitize_backed_candidates::( @@ -3551,7 +4078,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, - false, + v2_descriptor, ), expected_backed_candidates_with_core, ); @@ -3738,17 +4265,22 @@ mod sanitizers { // nothing is scheduled, so no paraids match, thus all backed candidates are skipped #[rstest] - #[case(false, false)] - #[case(true, true)] - #[case(false, true)] - #[case(true, false)] + #[case(false, false, true)] + #[case(true, true, true)] + #[case(false, true, true)] + #[case(true, false, true)] + #[case(false, false, false)] + #[case(true, true, false)] + #[case(false, true, false)] + #[case(true, false, false)] fn nothing_scheduled( #[case] core_index_enabled: bool, #[case] multiple_cores_per_para: bool, + #[case] v2_descriptor: bool, ) { new_test_ext(default_config()).execute_with(|| { let TestData { backed_candidates, .. } = if multiple_cores_per_para { - get_test_data_multiple_cores_per_para(core_index_enabled) + get_test_data_multiple_cores_per_para(core_index_enabled, v2_descriptor) } else { get_test_data_one_core_per_para(core_index_enabled) }; @@ -3805,8 +4337,14 @@ mod sanitizers { } // candidates that have concluded as invalid are filtered out, as well as their descendants. - #[test] - fn concluded_invalid_are_filtered_out_multiple_cores_per_para() { + #[rstest] + #[case(false, true)] + #[case(true, false)] + #[case(true, true)] + fn concluded_invalid_are_filtered_out_multiple_cores_per_para( + #[case] core_index_enabled: bool, + #[case] v2_descriptor: bool, + ) { // Mark the first candidate of paraid 1 as invalid. Its descendant should also // be dropped. Also mark the candidate of paraid 3 as invalid. new_test_ext(default_config()).execute_with(|| { @@ -3815,7 +4353,7 @@ mod sanitizers { scheduled_paras: scheduled, mut expected_backed_candidates_with_core, .. - } = get_test_data_multiple_cores_per_para(true); + } = get_test_data_multiple_cores_per_para(core_index_enabled, v2_descriptor); let mut invalid_set = std::collections::BTreeSet::new(); @@ -3834,8 +4372,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), invalid_set, scheduled, - true, - false, + core_index_enabled, + v2_descriptor, ); // We'll be left with candidates from paraid 2 and 4. @@ -3854,7 +4392,7 @@ mod sanitizers { scheduled_paras: scheduled, mut expected_backed_candidates_with_core, .. - } = get_test_data_multiple_cores_per_para(true); + } = get_test_data_multiple_cores_per_para(core_index_enabled, v2_descriptor); let mut invalid_set = std::collections::BTreeSet::new(); @@ -3871,8 +4409,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), invalid_set, scheduled, - true, - false, + core_index_enabled, + v2_descriptor, ); // Only the second candidate of paraid 1 should be removed. @@ -4083,7 +4621,7 @@ mod sanitizers { // Disable Bob, only the second candidate of paraid 1 should be removed. new_test_ext(default_config()).execute_with(|| { let TestData { mut expected_backed_candidates_with_core, .. } = - get_test_data_multiple_cores_per_para(true); + get_test_data_multiple_cores_per_para(true, false); set_disabled_validators(vec![1]); @@ -4105,7 +4643,7 @@ mod sanitizers { for disabled in [vec![0], vec![0, 1]] { new_test_ext(default_config()).execute_with(|| { let TestData { mut expected_backed_candidates_with_core, .. } = - get_test_data_multiple_cores_per_para(true); + get_test_data_multiple_cores_per_para(true, false); set_disabled_validators(disabled); diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs index 51e33001609c..a0996d5df0ee 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs @@ -455,8 +455,22 @@ pub fn backing_state( // // Thus, minimum relay parent is ensured to have asynchronous backing enabled. let now = frame_system::Pallet::::block_number(); - let min_relay_parent_number = shared::AllowedRelayParents::::get() - .hypothetical_earliest_block_number(now, config.async_backing_params.allowed_ancestry_len); + + // Use the right storage depending on version to ensure #64 doesn't cause issues with this + // migration. + let min_relay_parent_number = if shared::Pallet::::on_chain_storage_version() == + StorageVersion::new(0) + { + shared::migration::v0::AllowedRelayParents::::get().hypothetical_earliest_block_number( + now, + config.async_backing_params.allowed_ancestry_len, + ) + } else { + shared::AllowedRelayParents::::get().hypothetical_earliest_block_number( + now, + config.async_backing_params.allowed_ancestry_len, + ) + }; let required_parent = paras::Heads::::get(para_id)?; let validation_code_hash = paras::CurrentCodeHash::::get(para_id)?; diff --git a/polkadot/runtime/parachains/src/shared.rs b/polkadot/runtime/parachains/src/shared.rs index 154b7cfefc3a..f582bf0d90b5 100644 --- a/polkadot/runtime/parachains/src/shared.rs +++ b/polkadot/runtime/parachains/src/shared.rs @@ -20,12 +20,14 @@ //! dependent on any of the other pallets. use alloc::{ - collections::{btree_map::BTreeMap, vec_deque::VecDeque}, + collections::{btree_map::BTreeMap, btree_set::BTreeSet, vec_deque::VecDeque}, vec::Vec, }; use frame_support::{pallet_prelude::*, traits::DisabledValidators}; use frame_system::pallet_prelude::BlockNumberFor; -use polkadot_primitives::{SessionIndex, ValidatorId, ValidatorIndex}; +use polkadot_primitives::{ + vstaging::transpose_claim_queue, CoreIndex, Id, SessionIndex, ValidatorId, ValidatorIndex, +}; use sp_runtime::traits::AtLeast32BitUnsigned; use rand::{seq::SliceRandom, SeedableRng}; @@ -43,16 +45,28 @@ pub(crate) const SESSION_DELAY: SessionIndex = 2; #[cfg(test)] mod tests; -/// Information about past relay-parents. +pub mod migration; + +/// Information about a relay parent. +#[derive(Encode, Decode, Default, TypeInfo, Debug)] +pub struct RelayParentInfo { + // Relay parent hash + pub relay_parent: Hash, + // The state root at this block + pub state_root: Hash, + // Claim queue snapshot, optimized for accessing the assignments by `ParaId`. + // For each para we store the cores assigned per depth. + pub claim_queue: BTreeMap>>, +} + +/// Keeps tracks of information about all viable relay parents. #[derive(Encode, Decode, Default, TypeInfo)] pub struct AllowedRelayParentsTracker { - // The past relay parents, paired with state roots, that are viable to build upon. + // Information about past relay parents that are viable to build upon. // // They are in ascending chronologic order, so the newest relay parents are at // the back of the deque. - // - // (relay_parent, state_root) - buffer: VecDeque<(Hash, Hash)>, + buffer: VecDeque>, // The number of the most recent relay-parent, if any. // If the buffer is empty, this value has no meaning and may @@ -70,13 +84,17 @@ impl &mut self, relay_parent: Hash, state_root: Hash, + claim_queue: BTreeMap>, number: BlockNumber, max_ancestry_len: u32, ) { + let claim_queue = transpose_claim_queue(claim_queue); + // + 1 for the most recent block, which is always allowed. let buffer_size_limit = max_ancestry_len as usize + 1; - self.buffer.push_back((relay_parent, state_root)); + self.buffer.push_back(RelayParentInfo { relay_parent, state_root, claim_queue }); + self.latest_number = number; while self.buffer.len() > buffer_size_limit { let _ = self.buffer.pop_front(); @@ -96,8 +114,8 @@ impl &self, relay_parent: Hash, prev: Option, - ) -> Option<(Hash, BlockNumber)> { - let pos = self.buffer.iter().position(|(rp, _)| rp == &relay_parent)?; + ) -> Option<(&RelayParentInfo, BlockNumber)> { + let pos = self.buffer.iter().position(|info| info.relay_parent == relay_parent)?; let age = (self.buffer.len() - 1) - pos; let number = self.latest_number - BlockNumber::from(age as u32); @@ -107,7 +125,7 @@ impl } } - Some((self.buffer[pos].1, number)) + Some((&self.buffer[pos], number)) } /// Returns block number of the earliest block the buffer would contain if @@ -127,8 +145,11 @@ impl pub mod pallet { use super::*; + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] #[pallet::without_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] @@ -263,11 +284,12 @@ impl Pallet { pub(crate) fn add_allowed_relay_parent( relay_parent: T::Hash, state_root: T::Hash, + claim_queue: BTreeMap>, number: BlockNumberFor, max_ancestry_len: u32, ) { AllowedRelayParents::::mutate(|tracker| { - tracker.update(relay_parent, state_root, number, max_ancestry_len) + tracker.update(relay_parent, state_root, claim_queue, number, max_ancestry_len) }) } } diff --git a/polkadot/runtime/parachains/src/shared/migration.rs b/polkadot/runtime/parachains/src/shared/migration.rs new file mode 100644 index 000000000000..ae0412c6e26c --- /dev/null +++ b/polkadot/runtime/parachains/src/shared/migration.rs @@ -0,0 +1,196 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +use super::*; +use codec::{Decode, Encode}; +use frame_support::{ + pallet_prelude::ValueQuery, traits::UncheckedOnRuntimeUpgrade, weights::Weight, +}; + +#[cfg(feature = "try-runtime")] +const LOG_TARGET: &str = "runtime::shared"; + +pub mod v0 { + use super::*; + use alloc::collections::vec_deque::VecDeque; + + use frame_support::storage_alias; + + /// All allowed relay-parents storage at version 0. + #[storage_alias] + pub(crate) type AllowedRelayParents = StorageValue< + Pallet, + super::v0::AllowedRelayParentsTracker<::Hash, BlockNumberFor>, + ValueQuery, + >; + + #[derive(Encode, Decode, Default, TypeInfo)] + pub struct AllowedRelayParentsTracker { + // The past relay parents, paired with state roots, that are viable to build upon. + // + // They are in ascending chronologic order, so the newest relay parents are at + // the back of the deque. + // + // (relay_parent, state_root) + pub buffer: VecDeque<(Hash, Hash)>, + + // The number of the most recent relay-parent, if any. + // If the buffer is empty, this value has no meaning and may + // be nonsensical. + pub latest_number: BlockNumber, + } + + // Required to workaround #64. + impl + AllowedRelayParentsTracker + { + /// Returns block number of the earliest block the buffer would contain if + /// `now` is pushed into it. + pub(crate) fn hypothetical_earliest_block_number( + &self, + now: BlockNumber, + max_ancestry_len: u32, + ) -> BlockNumber { + let allowed_ancestry_len = max_ancestry_len.min(self.buffer.len() as u32); + + now - allowed_ancestry_len.into() + } + } + + impl From> + for super::AllowedRelayParentsTracker + { + fn from(value: AllowedRelayParentsTracker) -> Self { + Self { + latest_number: value.latest_number, + buffer: value + .buffer + .into_iter() + .map(|(relay_parent, state_root)| super::RelayParentInfo { + relay_parent, + state_root, + claim_queue: Default::default(), + }) + .collect(), + } + } + } +} + +mod v1 { + use super::*; + + #[cfg(feature = "try-runtime")] + use frame_support::{ + ensure, + traits::{GetStorageVersion, StorageVersion}, + }; + + pub struct VersionUncheckedMigrateToV1(core::marker::PhantomData); + + impl UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV1 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + log::trace!(target: LOG_TARGET, "Running pre_upgrade() for shared MigrateToV1"); + let bytes = u32::to_ne_bytes(v0::AllowedRelayParents::::get().buffer.len() as u32); + + Ok(bytes.to_vec()) + } + + fn on_runtime_upgrade() -> Weight { + let mut weight: Weight = Weight::zero(); + + // Read old storage. + let old_rp_tracker = v0::AllowedRelayParents::::take(); + + super::AllowedRelayParents::::set(old_rp_tracker.into()); + + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + log::trace!(target: LOG_TARGET, "Running post_upgrade() for shared MigrateToV1"); + ensure!( + Pallet::::on_chain_storage_version() >= StorageVersion::new(1), + "Storage version should be >= 1 after the migration" + ); + + let relay_parent_count = u32::from_ne_bytes( + state + .try_into() + .expect("u32::from_ne_bytes(to_ne_bytes(u32)) always works; qed"), + ); + + let rp_tracker = AllowedRelayParents::::get(); + + ensure!( + relay_parent_count as usize == rp_tracker.buffer.len(), + "Number of allowed relay parents should be the same as the one before the upgrade." + ); + + Ok(()) + } + } +} + +/// Migrate shared module storage to v1. +pub type MigrateToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + v1::VersionUncheckedMigrateToV1, + Pallet, + ::DbWeight, +>; + +#[cfg(test)] +mod tests { + use super::{v1::VersionUncheckedMigrateToV1, *}; + use crate::mock::{new_test_ext, MockGenesisConfig, Test}; + use frame_support::traits::UncheckedOnRuntimeUpgrade; + use polkadot_primitives::Hash; + + #[test] + fn migrate_to_v1() { + new_test_ext(MockGenesisConfig::default()).execute_with(|| { + let rp_tracker = v0::AllowedRelayParentsTracker { + latest_number: 9, + buffer: (0..10u64) + .into_iter() + .map(|idx| (Hash::from_low_u64_ne(idx), Hash::from_low_u64_ne(2 * idx))) + .collect::>(), + }; + + v0::AllowedRelayParents::::put(rp_tracker); + + as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade(); + + let rp_tracker = AllowedRelayParents::::get(); + + assert_eq!(rp_tracker.buffer.len(), 10); + + for idx in 0..10u64 { + let relay_parent = Hash::from_low_u64_ne(idx); + let state_root = Hash::from_low_u64_ne(2 * idx); + let (info, block_num) = rp_tracker.acquire_info(relay_parent, None).unwrap(); + + assert!(info.claim_queue.is_empty()); + assert_eq!(info.relay_parent, relay_parent); + assert_eq!(info.state_root, state_root); + assert_eq!(block_num as u64, idx); + } + }); + } +} diff --git a/polkadot/runtime/parachains/src/shared/tests.rs b/polkadot/runtime/parachains/src/shared/tests.rs index e47d1fd9cfe0..6da84e254f05 100644 --- a/polkadot/runtime/parachains/src/shared/tests.rs +++ b/polkadot/runtime/parachains/src/shared/tests.rs @@ -36,22 +36,71 @@ fn tracker_earliest_block_number() { // Push a single block into the tracker, suppose max capacity is 1. let max_ancestry_len = 0; - tracker.update(Hash::zero(), Hash::zero(), 0, max_ancestry_len); + tracker.update(Hash::zero(), Hash::zero(), Default::default(), 0, max_ancestry_len); assert_eq!(tracker.hypothetical_earliest_block_number(now, max_ancestry_len), now); // Test a greater capacity. let max_ancestry_len = 4; let now = 4; for i in 1..now { - tracker.update(Hash::zero(), Hash::zero(), i, max_ancestry_len); + tracker.update(Hash::zero(), Hash::zero(), Default::default(), i, max_ancestry_len); assert_eq!(tracker.hypothetical_earliest_block_number(i + 1, max_ancestry_len), 0); } // Capacity exceeded. - tracker.update(Hash::zero(), Hash::zero(), now, max_ancestry_len); + tracker.update(Hash::zero(), Hash::zero(), Default::default(), now, max_ancestry_len); assert_eq!(tracker.hypothetical_earliest_block_number(now + 1, max_ancestry_len), 1); } +#[test] +fn tracker_claim_queue_remap() { + let mut tracker = AllowedRelayParentsTracker::::default(); + + let mut claim_queue = BTreeMap::new(); + claim_queue.insert(CoreIndex(0), vec![Id::from(0), Id::from(1), Id::from(2)].into()); + claim_queue.insert(CoreIndex(1), vec![Id::from(0), Id::from(0), Id::from(100)].into()); + claim_queue.insert(CoreIndex(2), vec![Id::from(1), Id::from(2), Id::from(100)].into()); + + tracker.update(Hash::zero(), Hash::zero(), claim_queue, 1u32, 3u32); + + let (info, _block_num) = tracker.acquire_info(Hash::zero(), None).unwrap(); + assert_eq!( + info.claim_queue.get(&Id::from(0)).unwrap()[&0], + vec![CoreIndex(0), CoreIndex(1)].into_iter().collect::>() + ); + assert_eq!( + info.claim_queue.get(&Id::from(1)).unwrap()[&0], + vec![CoreIndex(2)].into_iter().collect::>() + ); + assert_eq!(info.claim_queue.get(&Id::from(2)).unwrap().get(&0), None); + assert_eq!(info.claim_queue.get(&Id::from(100)).unwrap().get(&0), None); + + assert_eq!( + info.claim_queue.get(&Id::from(0)).unwrap()[&1], + vec![CoreIndex(1)].into_iter().collect::>() + ); + assert_eq!( + info.claim_queue.get(&Id::from(1)).unwrap()[&1], + vec![CoreIndex(0)].into_iter().collect::>() + ); + assert_eq!( + info.claim_queue.get(&Id::from(2)).unwrap()[&1], + vec![CoreIndex(2)].into_iter().collect::>() + ); + assert_eq!(info.claim_queue.get(&Id::from(100)).unwrap().get(&1), None); + + assert_eq!(info.claim_queue.get(&Id::from(0)).unwrap().get(&2), None); + assert_eq!(info.claim_queue.get(&Id::from(1)).unwrap().get(&2), None); + assert_eq!( + info.claim_queue.get(&Id::from(2)).unwrap()[&2], + vec![CoreIndex(0)].into_iter().collect::>() + ); + assert_eq!( + info.claim_queue.get(&Id::from(100)).unwrap()[&2], + vec![CoreIndex(1), CoreIndex(2)].into_iter().collect::>() + ); +} + #[test] fn tracker_acquire_info() { let mut tracker = AllowedRelayParentsTracker::::default(); @@ -65,20 +114,20 @@ fn tracker_acquire_info() { ]; let (relay_parent, state_root) = blocks[0]; - tracker.update(relay_parent, state_root, 0, max_ancestry_len); + tracker.update(relay_parent, state_root, Default::default(), 0, max_ancestry_len); assert_matches!( tracker.acquire_info(relay_parent, None), - Some((s, b)) if s == state_root && b == 0 + Some((s, b)) if s.state_root == state_root && b == 0 ); let (relay_parent, state_root) = blocks[1]; - tracker.update(relay_parent, state_root, 1u32, max_ancestry_len); + tracker.update(relay_parent, state_root, Default::default(), 1u32, max_ancestry_len); let (relay_parent, state_root) = blocks[2]; - tracker.update(relay_parent, state_root, 2u32, max_ancestry_len); + tracker.update(relay_parent, state_root, Default::default(), 2u32, max_ancestry_len); for (block_num, (rp, state_root)) in blocks.iter().enumerate().take(2) { assert_matches!( tracker.acquire_info(*rp, None), - Some((s, b)) if &s == state_root && b == block_num as u32 + Some((s, b)) if &s.state_root == state_root && b == block_num as u32 ); assert!(tracker.acquire_info(*rp, Some(2)).is_none()); @@ -87,7 +136,7 @@ fn tracker_acquire_info() { for (block_num, (rp, state_root)) in blocks.iter().enumerate().skip(1) { assert_matches!( tracker.acquire_info(*rp, Some(block_num as u32 - 1)), - Some((s, b)) if &s == state_root && b == block_num as u32 + Some((s, b)) if &s.state_root == state_root && b == block_num as u32 ); } } diff --git a/polkadot/runtime/parachains/src/ump_tests.rs b/polkadot/runtime/parachains/src/ump_tests.rs index 91571859ecf0..cd7951ac9aa9 100644 --- a/polkadot/runtime/parachains/src/ump_tests.rs +++ b/polkadot/runtime/parachains/src/ump_tests.rs @@ -31,7 +31,10 @@ use frame_support::{ traits::{EnqueueMessage, ExecuteOverweightError, ServiceQueues}, weights::Weight, }; -use polkadot_primitives::{well_known_keys, Id as ParaId, UpwardMessage}; +use polkadot_primitives::{ + vstaging::{ClaimQueueOffset, CoreSelector, UMPSignal, UMP_SEPARATOR}, + well_known_keys, Id as ParaId, UpwardMessage, +}; use sp_crypto_hashing::{blake2_256, twox_64}; use sp_runtime::traits::Bounded; @@ -141,12 +144,12 @@ mod check_upward_messages { configuration::ActiveConfig::::get().max_upward_message_num_per_candidate; for sent in 0..permitted + 1 { - check(P_0, vec![msg(""); sent as usize], None); + check(P_0, vec![msg("a"); sent as usize], None); } for sent in permitted + 1..permitted + 10 { check( P_0, - vec![msg(""); sent as usize], + vec![msg("a"); sent as usize], Some(UmpAcceptanceCheckErr::MoreMessagesThanPermitted { sent, permitted }), ); } @@ -161,7 +164,7 @@ mod check_upward_messages { let max_per_candidate = configuration::ActiveConfig::::get().max_upward_message_num_per_candidate; - for msg_size in 0..=max_size { + for msg_size in 1..=max_size { check(P_0, vec![vec![0; msg_size as usize]], None); } for msg_size in max_size + 1..max_size + 10 { @@ -185,18 +188,18 @@ mod check_upward_messages { let limit = configuration::ActiveConfig::::get().max_upward_queue_count as u64; for _ in 0..limit { - check(P_0, vec![msg("")], None); - queue(P_0, vec![msg("")]); + check(P_0, vec![msg("a")], None); + queue(P_0, vec![msg("a")]); } check( P_0, - vec![msg("")], + vec![msg("a")], Some(UmpAcceptanceCheckErr::CapacityExceeded { count: limit + 1, limit }), ); check( P_0, - vec![msg(""); 2], + vec![msg("a"); 2], Some(UmpAcceptanceCheckErr::CapacityExceeded { count: limit + 2, limit }), ); }); @@ -642,6 +645,42 @@ fn cannot_offboard_while_ump_dispatch_queued() { }); } +/// Test UMP signals are filtered out and don't consume `max_upward_message_num_per_candidate`. +#[test] +fn enqueue_ump_signals() { + let para = 100.into(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + register_parachain(para); + run_to_block(5, vec![4, 5]); + + let config = configuration::ActiveConfig::::get(); + let mut messages = (0..config.max_upward_message_num_per_candidate) + .into_iter() + .map(|_| "msg".encode()) + .collect::>(); + let expected_messages = messages.iter().cloned().map(|msg| (para, msg)).collect::>(); + + // `UMPSignals` and separator do not count as XCM messages. The below check must pass. + messages.append(&mut vec![ + UMP_SEPARATOR, + UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(0)).encode(), + ]); + + ParaInclusion::check_upward_messages( + &configuration::ActiveConfig::::get(), + para, + &messages, + ) + .unwrap(); + + // We expect that all messages except UMP signal and separator are processed + ParaInclusion::receive_upward_messages(para, &messages); + MessageQueue::service_queues(Weight::max_value()); + assert_eq!(Processed::take(), expected_messages); + }); +} + /// A para-chain cannot send an UMP to the relay chain while it is offboarding. #[test] fn cannot_enqueue_ump_while_offboarding() { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index e1826e4100b6..ab6563525ff7 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -400,6 +400,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type MaxFreezes = ConstU32<1>; + type DoneSlashHandler = (); } parameter_types! { @@ -1234,6 +1235,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<1>; + type DoneSlashHandler = (); } parameter_types! { @@ -1677,6 +1679,7 @@ pub mod migrations { // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, parachains_inclusion::migration::MigrateToV1, + parachains_shared::migration::MigrateToV1, ); } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 30ef2de31651..6ba8e6ad3182 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -234,6 +234,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 7324485256a1..fe1777bc94ee 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -399,6 +399,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf; + type DoneSlashHandler = (); } parameter_types! { @@ -1784,6 +1785,7 @@ pub mod migrations { Runtime, MaxAgentsToMigrate, >, + parachains_shared::migration::MigrateToV1, ); } diff --git a/polkadot/xcm/xcm-builder/src/barriers.rs b/polkadot/xcm/xcm-builder/src/barriers.rs index 5d95005eb663..c995361ea8a3 100644 --- a/polkadot/xcm/xcm-builder/src/barriers.rs +++ b/polkadot/xcm/xcm-builder/src/barriers.rs @@ -57,8 +57,9 @@ const MAX_ASSETS_FOR_BUY_EXECUTION: usize = 2; /// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking /// payments into account. /// -/// Only allows for `TeleportAsset`, `WithdrawAsset`, `ClaimAsset` and `ReserveAssetDeposit` XCMs -/// because they are the only ones that place assets in the Holding Register to pay for execution. +/// Only allows for `WithdrawAsset`, `ReceiveTeleportedAsset`, `ReserveAssetDeposited` and +/// `ClaimAsset` XCMs because they are the only ones that place assets in the Holding Register to +/// pay for execution. pub struct AllowTopLevelPaidExecutionFrom(PhantomData); impl> ShouldExecute for AllowTopLevelPaidExecutionFrom { fn should_execute( @@ -81,9 +82,9 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFrom instructions[..end] .matcher() .match_next_inst(|inst| match inst { + WithdrawAsset(ref assets) | ReceiveTeleportedAsset(ref assets) | ReserveAssetDeposited(ref assets) | - WithdrawAsset(ref assets) | ClaimAsset { ref assets, .. } => if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION { Ok(()) @@ -92,7 +93,10 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFrom }, _ => Err(ProcessMessageError::BadFormat), })? - .skip_inst_while(|inst| matches!(inst, ClearOrigin))? + .skip_inst_while(|inst| { + matches!(inst, ClearOrigin | AliasOrigin(..)) || + matches!(inst, DescendOrigin(child) if child != &Here) + })? .match_next_inst(|inst| match inst { BuyExecution { weight_limit: Limited(ref mut weight), .. } if weight.all_gte(max_weight) => diff --git a/polkadot/xcm/xcm-builder/src/tests/barriers.rs b/polkadot/xcm/xcm-builder/src/tests/barriers.rs index 665b5febc61f..cd2b6db66efc 100644 --- a/polkadot/xcm/xcm-builder/src/tests/barriers.rs +++ b/polkadot/xcm/xcm-builder/src/tests/barriers.rs @@ -283,6 +283,56 @@ fn allow_paid_should_work() { assert_eq!(r, Ok(())) } +#[test] +fn allow_paid_should_deprivilege_origin() { + AllowPaidFrom::set(vec![Parent.into()]); + let fees = (Parent, 1).into(); + + let mut paying_message_clears_origin = Xcm::<()>(vec![ + ReserveAssetDeposited((Parent, 100).into()), + ClearOrigin, + BuyExecution { fees, weight_limit: Limited(Weight::from_parts(30, 30)) }, + DepositAsset { assets: AllCounted(1).into(), beneficiary: Here.into() }, + ]); + let r = AllowTopLevelPaidExecutionFrom::>::should_execute( + &Parent.into(), + paying_message_clears_origin.inner_mut(), + Weight::from_parts(30, 30), + &mut props(Weight::zero()), + ); + assert_eq!(r, Ok(())); + + let mut paying_message_aliases_origin = paying_message_clears_origin.clone(); + paying_message_aliases_origin.0[1] = AliasOrigin(Parachain(1).into()); + let r = AllowTopLevelPaidExecutionFrom::>::should_execute( + &Parent.into(), + paying_message_aliases_origin.inner_mut(), + Weight::from_parts(30, 30), + &mut props(Weight::zero()), + ); + assert_eq!(r, Ok(())); + + let mut paying_message_descends_origin = paying_message_clears_origin.clone(); + paying_message_descends_origin.0[1] = DescendOrigin(Parachain(1).into()); + let r = AllowTopLevelPaidExecutionFrom::>::should_execute( + &Parent.into(), + paying_message_descends_origin.inner_mut(), + Weight::from_parts(30, 30), + &mut props(Weight::zero()), + ); + assert_eq!(r, Ok(())); + + let mut paying_message_fake_descends_origin = paying_message_clears_origin.clone(); + paying_message_fake_descends_origin.0[1] = DescendOrigin(Here.into()); + let r = AllowTopLevelPaidExecutionFrom::>::should_execute( + &Parent.into(), + paying_message_fake_descends_origin.inner_mut(), + Weight::from_parts(30, 30), + &mut props(Weight::zero()), + ); + assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(30, 30)))); +} + #[test] fn suspension_should_work() { TestSuspender::set_suspended(true); diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index 18bde3aab485..9f2146fa30e8 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -77,6 +77,7 @@ impl pallet_balances::Config for Test { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/prdoc/pr_5423.prdoc b/prdoc/pr_5423.prdoc new file mode 100644 index 000000000000..dbd685d73dc3 --- /dev/null +++ b/prdoc/pr_5423.prdoc @@ -0,0 +1,20 @@ +title: Runtime support for candidate receipt v2 (RFC103) + +doc: + - audience: [Runtime Dev, Node Dev] + description: | + Implementation of [RFC103](https://github.com/polkadot-fellows/RFCs/pull/103) in the relay chain runtime. + The runtime will accept and validate the new receipts only if the `FeatureIndex::CandidateReceiptV2` + feature bit is enabled. + +crates: + - name: polkadot-primitives + bump: major + - name: polkadot-runtime-parachains + bump: patch + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: patch + - name: polkadot + bump: patch diff --git a/prdoc/pr_5623.prdoc b/prdoc/pr_5623.prdoc new file mode 100644 index 000000000000..c0701e0e1b51 --- /dev/null +++ b/prdoc/pr_5623.prdoc @@ -0,0 +1,89 @@ +# 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: Generic slashing side-effects + +doc: + - audience: Runtime Dev + description: | + What? + Make it possible for other pallets to implement their own logic when a slash on a balance occurs. + + How? + First we abstract the done_slash function of holds::Balanced to it's own trait that any pallet can implement. + Then we add a config type in pallet-balances that accepts a callback tuple of all the pallets that implement this trait. + Finally implement done_slash for pallet-balances such that it calls the config type. + Integration + The default implementation of done_slash is still an empty function, and the new config type of pallet-balances can be set to an empty tuple, so nothing changes by default. + +crates: + - name: frame-support + bump: major + + - name: pallet-balances + bump: major + + - name: pallet-broker + bump: minor + + - name: rococo-runtime + bump: minor + + - name: pallet-nis + bump: minor + + - name: westend-runtime + bump: minor + + - name: pallet-assets-freezer + bump: minor + + - name: pallet-contracts-mock-network + bump: minor + + - name: pallet-revive-mock-network + bump: minor + + - name: asset-hub-rococo-runtime + bump: minor + + - name: asset-hub-westend-runtime + bump: minor + + - name: bridge-hub-rococo-runtime + bump: minor + + - name: bridge-hub-westend-runtime + bump: minor + + - name: collectives-westend-runtime + bump: minor + + - name: coretime-rococo-runtime + bump: minor + + - name: coretime-westend-runtime + bump: minor + + - name: people-rococo-runtime + bump: minor + + - name: people-westend-runtime + bump: minor + + - name: penpal-runtime + bump: minor + + - name: contracts-rococo-runtime + bump: minor + + - name: rococo-parachain-runtime + bump: minor + + - name: staging-xcm-builder + bump: minor + + - name: polkadot-sdk + bump: minor + + diff --git a/prdoc/pr_5856.prdoc b/prdoc/pr_5856.prdoc new file mode 100644 index 000000000000..383e95e3da88 --- /dev/null +++ b/prdoc/pr_5856.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Extend state tracking of chainHead to capture notification gaps + +doc: + - audience: Node Dev + description: | + This PR extends the state tracking of the RPC-v2 chainHead methods. + ChainHead tracks the reported blocks to detect notification gaps. + This state tracking ensures we can detect `NewBlock` events for + which we did not report previously the parent hash. + +crates: + - name: sc-rpc-spec-v2 + bump: minor + diff --git a/prdoc/pr_5859.prdoc b/prdoc/pr_5859.prdoc new file mode 100644 index 000000000000..edb3008238b3 --- /dev/null +++ b/prdoc/pr_5859.prdoc @@ -0,0 +1,11 @@ +title: Add number of live peers available for requests + +doc: + - audience: [Node Operator, Node Dev] + description: | + This PR adds a new metric for the number of live peers available for beefy requests. + The metric is exposed under the name `substrate_beefy_on_demand_live_peers`. + +crates: + - name: sc-consensus-beefy + bump: minor diff --git a/prdoc/pr_5861.prdoc b/prdoc/pr_5861.prdoc new file mode 100644 index 000000000000..e2187dc1bdde --- /dev/null +++ b/prdoc/pr_5861.prdoc @@ -0,0 +1,37 @@ +title: "[pallet-revive] immutable data storage" + +doc: + - audience: Runtime Dev + description: | + This PR introduces the concept of immutable storage data, used for + [Solidity immutable variables](https://docs.soliditylang.org/en/latest/contracts.html#immutable). + + This is a minimal implementation. Immutable data is attached to a contract; to + `ContractInfo` fixed in size, we only store the length there, and store the immutable + data in a dedicated storage map instead. Which comes at the cost of requiring an + storage read (costly) for contracts using this feature. + + We discussed more optimal solutions not requiring any additional storage accesses + internally, but they turned out to be non-trivial to implement. Another optimization + benefiting multiple calls to the same contract in a single call stack would be to cache + the immutable data in `Stack`. However, this potential creates a DOS vulnerability (the + attack vector is to call into as many contracts in a single stack as possible, where + they all have maximum immutable data to fill the cache as efficiently as possible). So + this either has to be guaranteed to be a non-issue by limits, or, more likely, to have + some logic to bound the cache. Eventually, we should think about introducing the concept + of warm and cold storage reads (akin to EVM). Since immutable variables are commonly + used in contracts, this change is blocking our initial launch and we should only + optimize it properly in follow-ups. + + This PR also disables the `set_code_hash` API (which isn't usable for Solidity contracts + without pre-compiles anyways). With immutable storage attached to contracts, we now want + to run the constructor of the new code hash to collect the immutable data during + `set_code_hash`. This will be implemented in a follow up PR. + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: patch + - name: pallet-revive-uapi + bump: minor diff --git a/prdoc/pr_5888.prdoc b/prdoc/pr_5888.prdoc new file mode 100644 index 000000000000..9552eada6915 --- /dev/null +++ b/prdoc/pr_5888.prdoc @@ -0,0 +1,16 @@ +title: 'parachain-system: send core selector ump signal' + +doc: + - audience: Runtime Dev + description: | + Send the core selector ump signal in cumulus. Guarded by a compile time feature called `experimental-ump-signals` + until nodes are upgraded to a version that includes https://github.com/paritytech/polkadot-sdk/pull/5423 for + gracefully handling ump signals. + +crates: + - name: cumulus-client-consensus-aura + bump: minor + - name: cumulus-pallet-parachain-system + bump: major + - name: cumulus-primitives-core + bump: minor diff --git a/prdoc/pr_5901.prdoc b/prdoc/pr_5901.prdoc new file mode 100644 index 000000000000..4d3bce7f45a2 --- /dev/null +++ b/prdoc/pr_5901.prdoc @@ -0,0 +1,3 @@ +crates: + - name: polkadot-node-core-dispute-coordinator + bump: none diff --git a/prdoc/pr_5917.prdoc b/prdoc/pr_5917.prdoc new file mode 100644 index 000000000000..54b2e42ed9c3 --- /dev/null +++ b/prdoc/pr_5917.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "XCM paid execution barrier supports more origin altering instructions" + +doc: + - audience: Runtime Dev + description: | + Updates the `AllowTopLevelPaidExecutionFrom` barrier to also support messages that + use `DescendOrigin` or `AliasOrigin` for altering the computed origin during execution. + +crates: + - name: staging-xcm-builder + bump: patch diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 784d74973c32..c81921f844bd 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -546,6 +546,7 @@ impl pallet_balances::Config for Runtime { type WeightInfo = pallet_balances::weights::SubstrateWeight; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf; + type DoneSlashHandler = (); } parameter_types! { diff --git a/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs b/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs index 95ecf35557a5..5408d95acf2d 100644 --- a/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs +++ b/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs @@ -38,7 +38,7 @@ use crate::{ request_response::{Error, JustificationRequest, BEEFY_SYNC_LOG_TARGET}, }, justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof}, - metric_inc, + metric_inc, metric_set, metrics::{register_metrics, OnDemandOutgoingRequestsMetrics}, KnownPeers, }; @@ -242,6 +242,8 @@ impl OnDemandJustificationsEngine { diff --git a/substrate/client/consensus/beefy/src/metrics.rs b/substrate/client/consensus/beefy/src/metrics.rs index 30180fe43ec4..15f2f9f90334 100644 --- a/substrate/client/consensus/beefy/src/metrics.rs +++ b/substrate/client/consensus/beefy/src/metrics.rs @@ -236,6 +236,8 @@ pub struct OnDemandOutgoingRequestsMetrics { pub beefy_on_demand_justification_invalid_proof: Counter, /// Number of on-demand justification good proof pub beefy_on_demand_justification_good_proof: Counter, + /// Number of live beefy peers available for requests. + pub beefy_on_demand_live_peers: Gauge, } impl PrometheusRegister for OnDemandOutgoingRequestsMetrics { @@ -277,6 +279,13 @@ impl PrometheusRegister for OnDemandOutgoingRequestsMetrics { )?, registry, )?, + beefy_on_demand_live_peers: register( + Gauge::new( + "substrate_beefy_on_demand_live_peers", + "Number of live beefy peers available for requests.", + )?, + registry, + )?, }) } } diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs index ebb72ed3d156..f2326f015677 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_follow.rs @@ -24,7 +24,7 @@ use crate::chain_head::{ BestBlockChanged, Finalized, FollowEvent, Initialized, NewBlock, RuntimeEvent, RuntimeVersionEvent, }, - subscription::{SubscriptionManagement, SubscriptionManagementError}, + subscription::{InsertedSubscriptionData, SubscriptionManagement, SubscriptionManagementError}, }; use futures::{ channel::oneshot, @@ -53,8 +53,6 @@ use std::{ /// `Initialized` event. const MAX_FINALIZED_BLOCKS: usize = 16; -use super::subscription::InsertedSubscriptionData; - /// Generates the events of the `chainHead_follow` method. pub struct ChainHeadFollower, Block: BlockT, Client> { /// Substrate client. @@ -71,11 +69,76 @@ pub struct ChainHeadFollower, Block: BlockT, Client> { current_best_block: Option, /// LRU cache of pruned blocks. pruned_blocks: LruMap, + /// LRU cache of announced blocks. + announced_blocks: AnnouncedBlocks, /// Stop all subscriptions if the distance between the leaves and the current finalized /// block is larger than this value. max_lagging_distance: usize, } +struct AnnouncedBlocks { + /// Unfinalized blocks. + blocks: LruMap, + /// Finalized blocks. + finalized: MostRecentFinalizedBlocks, +} + +/// Wrapper over LRU to efficiently lookup hashes and remove elements as FIFO queue. +/// +/// For the finalized blocks we use `peek` to avoid moving the block counter to the front. +/// This effectively means that the LRU acts as a FIFO queue. Otherwise, we might +/// end up with scenarios where the "finalized block" in the end of LRU is overwritten which +/// may not necessarily be the oldest finalized block i.e, possible that "get" promotes an +/// older finalized block because it was accessed more recently. +struct MostRecentFinalizedBlocks(LruMap); + +impl MostRecentFinalizedBlocks { + /// Insert the finalized block hash into the LRU cache. + fn insert(&mut self, block: Block::Hash) { + self.0.insert(block, ()); + } + + /// Check if the block is contained in the LRU cache. + fn contains(&mut self, block: &Block::Hash) -> Option<&()> { + self.0.peek(block) + } +} + +impl AnnouncedBlocks { + /// Creates a new `AnnouncedBlocks`. + fn new() -> Self { + Self { + // The total number of pinned blocks is `MAX_PINNED_BLOCKS`, ensure we don't + // exceed the limit. + blocks: LruMap::new(ByLength::new((MAX_PINNED_BLOCKS - MAX_FINALIZED_BLOCKS) as u32)), + // We are keeping a smaller number of announced finalized blocks in memory. + // This is because the `Finalized` event might be triggered before the `NewBlock` event. + finalized: MostRecentFinalizedBlocks(LruMap::new(ByLength::new( + MAX_FINALIZED_BLOCKS as u32, + ))), + } + } + + /// Insert the block into the announced blocks. + fn insert(&mut self, block: Block::Hash, finalized: bool) { + if finalized { + // When a block is declared as finalized, it is removed from the unfinalized blocks. + // + // Given that the finalized blocks are bounded to `MAX_FINALIZED_BLOCKS`, + // this ensures we keep the minimum number of blocks in memory. + self.blocks.remove(&block); + self.finalized.insert(block); + } else { + self.blocks.insert(block, ()); + } + } + + /// Check if the block was previously announced. + fn was_announced(&mut self, block: &Block::Hash) -> bool { + self.blocks.get(block).is_some() || self.finalized.contains(block).is_some() + } +} + impl, Block: BlockT, Client> ChainHeadFollower { /// Create a new [`ChainHeadFollower`]. pub fn new( @@ -96,6 +159,7 @@ impl, Block: BlockT, Client> ChainHeadFollower, startup_point: &StartupPoint, ) -> Result>, SubscriptionManagementError> { - // The block was already pinned by the initial block events or by the finalized event. - if !self.sub_handle.pin_block(&self.sub_id, notification.hash)? { - return Ok(Default::default()) - } + let block_hash = notification.hash; // Ensure we are only reporting blocks after the starting point. if *notification.header.number() < startup_point.finalized_number { return Ok(Default::default()) } - Ok(self.generate_import_events( - notification.hash, - *notification.header.parent_hash(), - notification.is_new_best, - )) + // Ensure the block can be pinned before generating the events. + if !self.sub_handle.pin_block(&self.sub_id, block_hash)? { + // The block is already pinned, this is similar to the check above. + // + // The `SubscriptionManagement` ensures the block is tracked until (short lived): + // - 2 calls to `pin_block` are made (from `Finalized` and `NewBlock` branches). + // - the block is unpinned by the user + // + // This is rather a sanity checks for edge-cases (in theory), where + // [`MAX_FINALIZED_BLOCKS` + 1] finalized events are triggered before the `NewBlock` + // event of the first `Finalized` event. + return Ok(Default::default()) + } + + if self.announced_blocks.was_announced(&block_hash) { + // Block was already reported by the finalized branch. + return Ok(Default::default()) + } + + // Double check the parent hash. If the parent hash is not reported, we have a gap. + let parent_block_hash = *notification.header.parent_hash(); + if !self.announced_blocks.was_announced(&parent_block_hash) { + // The parent block was not reported, we have a gap. + return Err(SubscriptionManagementError::Custom("Parent block was not reported".into())) + } + + self.announced_blocks.insert(block_hash, false); + Ok(self.generate_import_events(block_hash, parent_block_hash, notification.is_new_best)) } /// Generates new block events from the given finalized hashes. @@ -448,12 +548,21 @@ where return Err(SubscriptionManagementError::BlockHeaderAbsent) }; + if !self.announced_blocks.was_announced(first_header.parent_hash()) { + return Err(SubscriptionManagementError::Custom( + "Parent block was not reported for a finalized block".into(), + )); + } + let parents = std::iter::once(first_header.parent_hash()).chain(finalized_block_hashes.iter()); for (i, (hash, parent)) in finalized_block_hashes.iter().zip(parents).enumerate() { - // Check if the block was already reported and thus, is already pinned. - if !self.sub_handle.pin_block(&self.sub_id, *hash)? { - continue + // Ensure the block is pinned before generating the events. + self.sub_handle.pin_block(&self.sub_id, *hash)?; + + // Check if the block was already reported. + if self.announced_blocks.was_announced(hash) { + continue; } // Generate `NewBlock` events for all blocks beside the last block in the list @@ -461,6 +570,7 @@ where if !is_last { // Generate only the `NewBlock` event for this block. events.extend(self.generate_import_events(*hash, *parent, false)); + self.announced_blocks.insert(*hash, true); continue; } @@ -483,7 +593,8 @@ where } // Let's generate the `NewBlock` and `NewBestBlock` events for the block. - events.extend(self.generate_import_events(*hash, *parent, true)) + events.extend(self.generate_import_events(*hash, *parent, true)); + self.announced_blocks.insert(*hash, true); } Ok(events) @@ -545,6 +656,10 @@ where let pruned_block_hashes = self.get_pruned_hashes(¬ification.stale_heads, last_finalized)?; + for finalized in &finalized_block_hashes { + self.announced_blocks.insert(*finalized, true); + } + let finalized_event = FollowEvent::Finalized(Finalized { finalized_block_hashes, pruned_block_hashes: pruned_block_hashes.clone(), diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index 44a2849d9153..0c2486157bd0 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -3965,3 +3965,71 @@ async fn follow_report_best_block_of_a_known_block() { }); assert_eq!(event, expected); } + +#[tokio::test] +async fn follow_event_with_unknown_parent() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + + let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); + + let api = ChainHead::new( + client_mock.clone(), + backend, + Arc::new(TokioTestExecutor::default()), + ChainHeadConfig { + global_max_pinned_blocks: MAX_PINNED_BLOCKS, + subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), + subscription_max_ongoing_operations: MAX_OPERATIONS, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + max_lagging_distance: MAX_LAGGING_DISTANCE, + }, + ) + .into_rpc(); + + let finalized_hash = client.info().finalized_hash; + let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap(); + // Initialized must always be reported first. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Initialized(Initialized { + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], + finalized_block_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + + // Block tree: + // + // finalized -> (gap: block 1) -> block 2 + // + // Block 1 is not announced yet. ChainHead should report the stop + // event when encountering an unknown parent of block 2. + + // Note: `client` is used just for constructing the blocks. + // The blocks are imported to chainHead using the `client_mock`. + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_1_hash = block_1.hash(); + client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); + + let block_2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); + + run_with_timeout(client_mock.trigger_import_stream(block_2.header)).await; + // When importing the block 2, chainHead detects a gap in our blocks and stops. + assert_matches!(get_next_event::>(&mut sub).await, FollowEvent::Stop); +} diff --git a/substrate/frame/assets-freezer/src/mock.rs b/substrate/frame/assets-freezer/src/mock.rs index 5e04dfe8e2b9..bc903a018f7b 100644 --- a/substrate/frame/assets-freezer/src/mock.rs +++ b/substrate/frame/assets-freezer/src/mock.rs @@ -87,6 +87,7 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); + type DoneSlashHandler = (); } impl pallet_assets::Config for Test { diff --git a/substrate/frame/balances/src/impl_fungible.rs b/substrate/frame/balances/src/impl_fungible.rs index 0f4e51f35012..cf95a97d191f 100644 --- a/substrate/frame/balances/src/impl_fungible.rs +++ b/substrate/frame/balances/src/impl_fungible.rs @@ -363,6 +363,14 @@ impl, I: 'static> fungible::Balanced for Pallet impl, I: 'static> fungible::BalancedHold for Pallet {} +impl, I: 'static> + fungible::hold::DoneSlash for Pallet +{ + fn done_slash(reason: &T::RuntimeHoldReason, who: &T::AccountId, amount: T::Balance) { + T::DoneSlashHandler::done_slash(reason, who, amount); + } +} + impl, I: 'static> AccountTouch<(), T::AccountId> for Pallet { type Balance = T::Balance; fn deposit_required(_: ()) -> Self::Balance { diff --git a/substrate/frame/balances/src/lib.rs b/substrate/frame/balances/src/lib.rs index f6858347f24e..65e594a904f9 100644 --- a/substrate/frame/balances/src/lib.rs +++ b/substrate/frame/balances/src/lib.rs @@ -234,6 +234,7 @@ pub mod pallet { type MaxFreezes = VariantCountOf; type WeightInfo = (); + type DoneSlashHandler = (); } } @@ -312,6 +313,14 @@ pub mod pallet { /// The maximum number of individual freeze locks that can exist on an account at any time. #[pallet::constant] type MaxFreezes: Get; + + /// Allows callbacks to other pallets so they can update their bookkeeping when a slash + /// occurs. + type DoneSlashHandler: fungible::hold::DoneSlash< + Self::RuntimeHoldReason, + Self::AccountId, + Self::Balance, + >; } /// The in-code storage version. diff --git a/substrate/frame/broker/src/test_fungibles.rs b/substrate/frame/broker/src/test_fungibles.rs index b0a06fc1a326..1015e1fac252 100644 --- a/substrate/frame/broker/src/test_fungibles.rs +++ b/substrate/frame/broker/src/test_fungibles.rs @@ -269,6 +269,20 @@ where { } +impl< + Instance: Get, + AccountId: Encode, + AssetId: tokens::AssetId + Copy, + MinimumBalance: TypedGet, + HoldReason: Encode + Decode + TypeInfo + 'static, + Balance: tokens::Balance, + > fungibles::hold::DoneSlash + for TestFungibles +where + MinimumBalance::Type: tokens::Balance, +{ +} + impl< Instance: Get, AccountId: Encode, diff --git a/substrate/frame/contracts/mock-network/src/parachain.rs b/substrate/frame/contracts/mock-network/src/parachain.rs index 5a06cc6748b8..6edbfb0e7e86 100644 --- a/substrate/frame/contracts/mock-network/src/parachain.rs +++ b/substrate/frame/contracts/mock-network/src/parachain.rs @@ -94,6 +94,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type WeightInfo = (); + type DoneSlashHandler = (); } parameter_types! { diff --git a/substrate/frame/contracts/mock-network/src/relay_chain.rs b/substrate/frame/contracts/mock-network/src/relay_chain.rs index 705578cde1d9..5fed061f80b4 100644 --- a/substrate/frame/contracts/mock-network/src/relay_chain.rs +++ b/substrate/frame/contracts/mock-network/src/relay_chain.rs @@ -89,6 +89,7 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<0>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; + type DoneSlashHandler = (); } impl shared::Config for Runtime { diff --git a/substrate/frame/nis/src/mock.rs b/substrate/frame/nis/src/mock.rs index f3320a306df7..2b008f8ec2a4 100644 --- a/substrate/frame/nis/src/mock.rs +++ b/substrate/frame/nis/src/mock.rs @@ -64,6 +64,7 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; + type DoneSlashHandler = (); } impl pallet_balances::Config for Test { @@ -84,6 +85,7 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); + type DoneSlashHandler = (); } parameter_types! { diff --git a/substrate/frame/revive/fixtures/contracts/immutable_data.rs b/substrate/frame/revive/fixtures/contracts/immutable_data.rs new file mode 100644 index 000000000000..ac50e61a400b --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/immutable_data.rs @@ -0,0 +1,43 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests that the `get_immutable_data` and `set_immutable_data` APIs work. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + input!(data: &[u8; 8],); + + api::set_immutable_data(data); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(data: &[u8; 8],); + + let mut buf = [0; 8]; + api::get_immutable_data(&mut &mut buf[..]); + + assert_eq!(data, &buf); +} diff --git a/substrate/frame/revive/mock-network/src/parachain.rs b/substrate/frame/revive/mock-network/src/parachain.rs index 0fd2248db572..26a8fdcada27 100644 --- a/substrate/frame/revive/mock-network/src/parachain.rs +++ b/substrate/frame/revive/mock-network/src/parachain.rs @@ -94,6 +94,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type WeightInfo = (); + type DoneSlashHandler = (); } parameter_types! { diff --git a/substrate/frame/revive/mock-network/src/relay_chain.rs b/substrate/frame/revive/mock-network/src/relay_chain.rs index 705578cde1d9..5fed061f80b4 100644 --- a/substrate/frame/revive/mock-network/src/relay_chain.rs +++ b/substrate/frame/revive/mock-network/src/relay_chain.rs @@ -89,6 +89,7 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<0>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; + type DoneSlashHandler = (); } impl shared::Config for Runtime { diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index acca876f376a..ebafb6c7054a 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -628,6 +628,53 @@ mod benchmarks { assert_eq!(U256::from_little_endian(&memory[..len]), runtime.ext().balance_of(&address)); } + #[benchmark(pov_mode = Measured)] + fn seal_get_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) { + let len = n as usize; + let immutable_data = vec![1u8; len]; + + build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]); + + >::insert::<_, BoundedVec<_, _>>( + contract.address(), + immutable_data.clone().try_into().unwrap(), + ); + + let result; + #[block] + { + result = runtime.bench_get_immutable_data(memory.as_mut_slice(), 4, 0 as u32); + } + + assert_ok!(result); + assert_eq!(&memory[0..4], (len as u32).encode()); + assert_eq!(&memory[4..len + 4], &immutable_data); + } + + #[benchmark(pov_mode = Measured)] + fn seal_set_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) { + let len = n as usize; + let mut memory = vec![1u8; len]; + let mut setup = CallSetup::::default(); + let input = setup.data(); + let (mut ext, _) = setup.ext(); + ext.override_export(crate::debug::ExportedFunction::Constructor); + + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, input); + + let result; + #[block] + { + result = runtime.bench_set_immutable_data(memory.as_mut_slice(), 0, n); + } + + assert_ok!(result); + assert_eq!( + &memory[..], + &>::get(setup.contract().address()).unwrap()[..] + ); + } + #[benchmark(pov_mode = Measured)] fn seal_value_transferred() { build_runtime!(runtime, memory: [[0u8;32], ]); diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 2e48bab29255..c6d2f205ae22 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -25,7 +25,7 @@ use crate::{ storage::{self, meter::Diff, WriteOutcome}, transient_storage::TransientStorage, BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBuffer, Error, - Event, Pallet as Contracts, LOG_TARGET, + Event, ImmutableData, ImmutableDataOf, Pallet as Contracts, LOG_TARGET, }; use alloc::vec::Vec; use core::{fmt::Debug, marker::PhantomData, mem}; @@ -296,6 +296,18 @@ pub trait Ext: sealing::Sealed { ::AddressMapper::to_address(self.account_id()) } + /// Returns the immutable data of the current contract. + /// + /// Returns `Err(InvalidImmutableAccess)` if called from a constructor. + fn get_immutable_data(&mut self) -> Result; + + /// Set the the immutable data of the current contract. + /// + /// Returns `Err(InvalidImmutableAccess)` if not called from a constructor. + /// + /// Note: Requires &mut self to access the contract info. + fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError>; + /// Returns the balance of the current contract. /// /// The `value_transferred` is already added. @@ -373,7 +385,7 @@ pub trait Ext: sealing::Sealed { #[cfg(feature = "runtime-benchmarks")] fn transient_storage(&mut self) -> &mut TransientStorage; - /// Sets new code hash for existing contract. + /// Sets new code hash and immutable data for an existing contract. fn set_code_hash(&mut self, hash: H256) -> DispatchResult; /// Returns the number of times the specified contract exists on the call stack. Delegated calls @@ -1286,6 +1298,13 @@ where fn account_balance(&self, who: &T::AccountId) -> U256 { T::Currency::reducible_balance(who, Preservation::Preserve, Fortitude::Polite).into() } + + /// Certain APIs, e.g. `{set,get}_immutable_data` behave differently depending + /// on the configured entry point. Thus, we allow setting the export manually. + #[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] + pub(crate) fn override_export(&mut self, export: ExportedFunction) { + self.top_frame_mut().entry_point = export; + } } impl<'a, T, E> Ext for Stack<'a, T, E> @@ -1418,6 +1437,7 @@ where info.queue_trie_for_deletion(); let account_address = T::AddressMapper::to_address(&frame.account_id); ContractInfoOf::::remove(&account_address); + ImmutableDataOf::::remove(&account_address); Self::decrement_refcount(info.code_hash); for (code_hash, deposit) in info.delegate_dependencies() { @@ -1521,6 +1541,30 @@ where self.caller_is_origin() && self.origin == Origin::Root } + fn get_immutable_data(&mut self) -> Result { + if self.top_frame().entry_point == ExportedFunction::Constructor { + return Err(Error::::InvalidImmutableAccess.into()); + } + + let address = T::AddressMapper::to_address(self.account_id()); + Ok(>::get(address).ok_or_else(|| Error::::InvalidImmutableAccess)?) + } + + fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError> { + if self.top_frame().entry_point == ExportedFunction::Call { + return Err(Error::::InvalidImmutableAccess.into()); + } + + let account_id = self.account_id().clone(); + let len = data.len() as u32; + let amount = self.top_frame_mut().contract_info().set_immutable_data_len(len)?; + self.top_frame_mut().nested_storage.charge_deposit(account_id.clone(), amount); + + >::insert(T::AddressMapper::to_address(&account_id), &data); + + Ok(()) + } + fn balance(&self) -> U256 { self.account_balance(&self.top_frame().account_id) } @@ -1627,6 +1671,21 @@ where &mut self.transient_storage } + /// TODO: This should be changed to run the constructor of the supplied `hash`. + /// + /// Because the immutable data is attached to a contract and not a code, + /// we need to update the immutable data too. + /// + /// Otherwise we open a massive footgun: + /// If the immutables changed in the new code, the contract will brick. + /// + /// A possible implementation strategy is to add a flag to `FrameArgs::Instantiate`, + /// so that `fn run()` will roll back any changes if this flag is set. + /// + /// After running the constructor, the new immutable data is already stored in + /// `self.immutable_data` at the address of the (reverted) contract instantiation. + /// + /// The `set_code_hash` contract API stays disabled until this change is implemented. fn set_code_hash(&mut self, hash: H256) -> DispatchResult { let frame = top_frame_mut!(self); @@ -4280,4 +4339,216 @@ mod tests { assert_matches!(result, Ok(_)); }); } + + #[test] + fn immutable_data_access_checks_work() { + let dummy_ch = MockLoader::insert(Constructor, move |ctx, _| { + // Calls can not store immutable data + assert_eq!( + ctx.ext.get_immutable_data(), + Err(Error::::InvalidImmutableAccess.into()) + ); + exec_success() + }); + let instantiator_ch = MockLoader::insert(Call, { + move |ctx, _| { + let value = ::Currency::minimum_balance().into(); + + assert_eq!( + ctx.ext.set_immutable_data(vec![0, 1, 2, 3].try_into().unwrap()), + Err(Error::::InvalidImmutableAccess.into()) + ); + + // Constructors can not access the immutable data + ctx.ext + .instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None) + .unwrap(); + + exec_success() + } + }); + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + set_balance(&ALICE, 1000); + set_balance(&BOB_CONTRACT_ID, 100); + place_contract(&BOB, instantiator_ch); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); + + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap() + }); + } + + #[test] + fn correct_immutable_data_in_delegate_call() { + let charlie_ch = MockLoader::insert(Call, |ctx, _| { + Ok(ExecReturnValue { + flags: ReturnFlags::empty(), + data: ctx.ext.get_immutable_data()?.to_vec(), + }) + }); + let bob_ch = MockLoader::insert(Call, move |ctx, _| { + // In a regular call, we should witness the callee immutable data + assert_eq!( + ctx.ext + .call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false, + ) + .map(|_| ctx.ext.last_frame_output().data.clone()), + Ok(vec![2]), + ); + + // In a delegate call, we should witness the caller immutable data + assert_eq!( + ctx.ext.delegate_call(charlie_ch, Vec::new()).map(|_| ctx + .ext + .last_frame_output() + .data + .clone()), + Ok(vec![1]) + ); + + exec_success() + }); + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + place_contract(&BOB, bob_ch); + place_contract(&CHARLIE, charlie_ch); + + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); + + // Place unique immutable data for each contract + >::insert::<_, ImmutableData>( + BOB_ADDR, + vec![1].try_into().unwrap(), + ); + >::insert::<_, ImmutableData>( + CHARLIE_ADDR, + vec![2].try_into().unwrap(), + ); + + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap() + }); + } + + #[test] + fn immutable_data_set_works_only_once() { + let dummy_ch = MockLoader::insert(Constructor, move |ctx, _| { + // Calling `set_immutable_data` the first time should work + assert_ok!(ctx.ext.set_immutable_data(vec![0, 1, 2, 3].try_into().unwrap())); + // Calling `set_immutable_data` the second time should error out + assert_eq!( + ctx.ext.set_immutable_data(vec![0, 1, 2, 3].try_into().unwrap()), + Err(Error::::InvalidImmutableAccess.into()) + ); + exec_success() + }); + let instantiator_ch = MockLoader::insert(Call, { + move |ctx, _| { + let value = ::Currency::minimum_balance().into(); + ctx.ext + .instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None) + .unwrap(); + + exec_success() + } + }); + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + set_balance(&ALICE, 1000); + set_balance(&BOB_CONTRACT_ID, 100); + place_contract(&BOB, instantiator_ch); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); + + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap() + }); + } + + #[test] + fn immutable_data_set_errors_with_empty_data() { + let dummy_ch = MockLoader::insert(Constructor, move |ctx, _| { + // Calling `set_immutable_data` with empty data should error out + assert_eq!( + ctx.ext.set_immutable_data(Default::default()), + Err(Error::::InvalidImmutableAccess.into()) + ); + exec_success() + }); + let instantiator_ch = MockLoader::insert(Call, { + move |ctx, _| { + let value = ::Currency::minimum_balance().into(); + ctx.ext + .instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None) + .unwrap(); + + exec_success() + } + }); + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + set_balance(&ALICE, 1000); + set_balance(&BOB_CONTRACT_ID, 100); + place_contract(&BOB, instantiator_ch); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); + + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap() + }); + } } diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 91c5fa5563a5..6d87fd904c3f 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -94,6 +94,7 @@ type CodeVec = BoundedVec>; type EventRecordOf = EventRecord<::RuntimeEvent, ::Hash>; type DebugBuffer = BoundedVec>; +type ImmutableData = BoundedVec>; /// Used as a sentinel value when reading and writing contract memory. /// @@ -550,6 +551,9 @@ pub mod pallet { ExecutionFailed, /// Failed to convert a U256 to a Balance. BalanceConversionFailed, + /// Immutable data can only be set during deploys and only be read during calls. + /// Additionally, it is only valid to set the data once and it must not be empty. + InvalidImmutableAccess, } /// A reason for the pallet contracts placing a hold on funds. @@ -573,6 +577,10 @@ pub mod pallet { #[pallet::storage] pub(crate) type ContractInfoOf = StorageMap<_, Identity, H160, ContractInfo>; + /// The immutable data associated with a given account. + #[pallet::storage] + pub(crate) type ImmutableDataOf = StorageMap<_, Identity, H160, ImmutableData>; + /// Evicted contracts that await child trie deletion. /// /// Child trie deletion is a heavy operation depending on the amount of storage items @@ -665,6 +673,16 @@ pub mod pallet { .hash() .len() as u32; + let max_immutable_key_size = T::AccountId::max_encoded_len() as u32; + let max_immutable_size: u32 = ((max_block_ref_time / + (>::weight(&RuntimeCosts::SetImmutableData( + limits::IMMUTABLE_BYTES, + )) + .ref_time())) + .saturating_mul(limits::IMMUTABLE_BYTES.saturating_add(max_immutable_key_size) as u64)) + .try_into() + .expect("Immutable data size too big"); + // We can use storage to store items using the available block ref_time with the // `set_storage` host function. let max_storage_size: u32 = ((max_block_ref_time / @@ -674,6 +692,7 @@ pub mod pallet { }) .ref_time())) .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64)) + .saturating_add(max_immutable_size.into()) .try_into() .expect("Storage size too big"); diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index f712493d3bc8..50cdba6d0c91 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -65,6 +65,12 @@ pub const DEBUG_BUFFER_BYTES: u32 = 2 * 1024 * 1024; /// The page size in which PolkaVM should allocate memory chunks. pub const PAGE_SIZE: u32 = 4 * 1024; +/// The maximum amount of immutable bytes a single contract can store. +/// +/// The current limit of 4kb allows storing up 16 U256 immutable variables. +/// Which should always be enough because Solidity allows for 16 local (stack) variables. +pub const IMMUTABLE_BYTES: u32 = 4 * 1024; + /// Limits that are only enforced on code upload. /// /// # Note diff --git a/substrate/frame/revive/src/storage.rs b/substrate/frame/revive/src/storage.rs index ef7ce2db32cf..db4db3e8eac3 100644 --- a/substrate/frame/revive/src/storage.rs +++ b/substrate/frame/revive/src/storage.rs @@ -26,7 +26,7 @@ use crate::{ storage::meter::Diff, weights::WeightInfo, BalanceOf, CodeInfo, Config, ContractInfoOf, DeletionQueue, DeletionQueueCounter, Error, - TrieId, SENTINEL, + StorageDeposit, TrieId, SENTINEL, }; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; @@ -36,6 +36,7 @@ use frame_support::{ weights::{Weight, WeightMeter}, CloneNoBound, DefaultNoBound, }; +use meter::DepositOf; use scale_info::TypeInfo; use sp_core::{ConstU32, Get, H160}; use sp_io::KillStorageResult; @@ -75,6 +76,8 @@ pub struct ContractInfo { /// to the map can not be removed from the chain state and can be safely used for delegate /// calls. delegate_dependencies: DelegateDependencyMap, + /// The size of the immutable data of this contract. + immutable_data_len: u32, } impl ContractInfo { @@ -88,7 +91,7 @@ impl ContractInfo { code_hash: sp_core::H256, ) -> Result { if >::contains_key(address) { - return Err(Error::::DuplicateContract.into()) + return Err(Error::::DuplicateContract.into()); } let trie_id = { @@ -108,6 +111,7 @@ impl ContractInfo { storage_item_deposit: Zero::zero(), storage_base_deposit: Zero::zero(), delegate_dependencies: Default::default(), + immutable_data_len: 0, }; Ok(contract) @@ -356,6 +360,35 @@ impl ContractInfo { pub fn load_code_hash(account: &AccountIdOf) -> Option { >::get(&T::AddressMapper::to_address(account)).map(|i| i.code_hash) } + + /// Returns the amount of immutable bytes of this contract. + pub fn immutable_data_len(&self) -> u32 { + self.immutable_data_len + } + + /// Set the number of immutable bytes of this contract. + /// + /// On success, returns the storage deposit to be charged. + /// + /// Returns `Err(InvalidImmutableAccess)` if: + /// - The immutable bytes of this contract are not 0. This indicates that the immutable data + /// have already been set; it is only valid to set the immutable data exactly once. + /// - The provided `immutable_data_len` value was 0; it is invalid to set empty immutable data. + pub fn set_immutable_data_len( + &mut self, + immutable_data_len: u32, + ) -> Result, DispatchError> { + if self.immutable_data_len != 0 || immutable_data_len == 0 { + return Err(Error::::InvalidImmutableAccess.into()); + } + + self.immutable_data_len = immutable_data_len; + + let amount = T::DepositPerByte::get() + .saturating_mul(immutable_data_len.into()) + .saturating_add(T::DepositPerItem::get()); + Ok(StorageDeposit::Charge(amount)) + } } /// Information about what happened to the pre-existing value when calling [`ContractInfo::write`]. diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs index a2ece03f9aaf..712010bc8257 100644 --- a/substrate/frame/revive/src/storage/meter.rs +++ b/substrate/frame/revive/src/storage/meter.rs @@ -674,6 +674,7 @@ mod tests { items: u32, bytes_deposit: BalanceOf, items_deposit: BalanceOf, + immutable_data_len: u32, } fn new_info(info: StorageInfo) -> ContractInfo { @@ -686,6 +687,7 @@ mod tests { storage_item_deposit: info.items_deposit, storage_base_deposit: Default::default(), delegate_dependencies: Default::default(), + immutable_data_len: info.immutable_data_len, } } @@ -773,6 +775,7 @@ mod tests { items: 5, bytes_deposit: 100, items_deposit: 10, + immutable_data_len: 0, }); let mut nested0 = meter.nested(BalanceOf::::zero()); nested0.charge(&Diff { @@ -788,6 +791,7 @@ mod tests { items: 10, bytes_deposit: 100, items_deposit: 20, + immutable_data_len: 0, }); let mut nested1 = nested0.nested(BalanceOf::::zero()); nested1.charge(&Diff { items_removed: 5, ..Default::default() }); @@ -798,6 +802,7 @@ mod tests { items: 7, bytes_deposit: 100, items_deposit: 20, + immutable_data_len: 0, }); let mut nested2 = nested0.nested(BalanceOf::::zero()); nested2.charge(&Diff { items_removed: 7, ..Default::default() }); @@ -867,6 +872,7 @@ mod tests { items: 10, bytes_deposit: 100, items_deposit: 20, + immutable_data_len: 0, }); let mut nested1 = nested0.nested(BalanceOf::::zero()); nested1.charge(&Diff { items_removed: 5, ..Default::default() }); diff --git a/substrate/frame/revive/src/test_utils.rs b/substrate/frame/revive/src/test_utils.rs index 671efebdf4bd..92c21297a3ec 100644 --- a/substrate/frame/revive/src/test_utils.rs +++ b/substrate/frame/revive/src/test_utils.rs @@ -54,6 +54,7 @@ pub const BOB_CONTRACT_ID: AccountId32 = ee_suffix(BOB_ADDR); pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); pub const CHARLIE_ADDR: H160 = H160([3u8; 20]); +pub const CHARLIE_CONTRACT_ID: AccountId32 = ee_suffix(CHARLIE_ADDR); pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); pub const DJANGO_ADDR: H160 = H160([4u8; 20]); diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 5c5d144f24a2..c7d1a8b2cf0d 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -140,9 +140,18 @@ pub mod test_utils { pub fn contract_info_storage_deposit(addr: &H160) -> BalanceOf { let contract_info = self::get_contract(&addr); let info_size = contract_info.encoded_size() as u64; - DepositPerByte::get() + let info_deposit = DepositPerByte::get() .saturating_mul(info_size) - .saturating_add(DepositPerItem::get()) + .saturating_add(DepositPerItem::get()); + let immutable_size = contract_info.immutable_data_len() as u64; + if immutable_size > 0 { + let immutable_deposit = DepositPerByte::get() + .saturating_mul(immutable_size) + .saturating_add(DepositPerItem::get()); + info_deposit.saturating_add(immutable_deposit) + } else { + info_deposit + } } pub fn expected_deposit(code_len: usize) -> u64 { // For code_info, the deposit for max_encoded_len is taken. @@ -3530,8 +3539,11 @@ mod run_tests { // Set enough deposit limit for the child instantiate. This should succeed. let result = builder::bare_call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) - .storage_deposit_limit(callee_info_len + 2 + ED + 4) - .data((1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 3)).encode()) + .storage_deposit_limit(callee_info_len + 2 + ED + 4 + 2) + .data( + (1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 3 + 2)) + .encode(), + ) .build(); let returned = result.result.unwrap(); @@ -3548,6 +3560,7 @@ mod run_tests { // - callee instantiation deposit = (callee_info_len + 2) // - callee account ED // - for writing an item of 1 byte to storage = 3 Balance + // - Immutable data storage item deposit assert_eq!( ::Currency::free_balance(&BOB), 1_000_000 - (callee_info_len + 2 + ED + 3) @@ -4354,4 +4367,33 @@ mod run_tests { .build()); }); } + + #[test] + fn immutable_data_works() { + let (code, _) = compile_module("immutable_data").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let data = [0xfe; 8]; + + // Create fixture: Constructor sets the immtuable data + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .data(data.to_vec()) + .build_and_unwrap_contract(); + + // Storing immmutable data charges storage deposit; verify it explicitly. + assert_eq!( + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &::AddressMapper::to_account_id(&addr) + ), + test_utils::contract_info_storage_deposit(&addr) + ); + assert_eq!(test_utils::get_contract(&addr).immutable_data_len(), data.len() as u32); + + // Call the contract: Asserts the input to equal the immutable data + assert_ok!(builder::call(addr).data(data.to_vec()).build()); + }); + } } diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 56e3b685dcf6..485abbfda225 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -390,6 +390,10 @@ pub enum RuntimeCosts { LockDelegateDependency, /// Weight of calling `unlock_delegate_dependency` UnlockDelegateDependency, + /// Weight of calling `get_immutable_dependency` + GetImmutableData(u32), + /// Weight of calling `set_immutable_dependency` + SetImmutableData(u32), } /// For functions that modify storage, benchmarks are performed with one item in the @@ -507,6 +511,8 @@ impl Token for RuntimeCosts { EcdsaToEthAddress => T::WeightInfo::seal_ecdsa_to_eth_address(), LockDelegateDependency => T::WeightInfo::lock_delegate_dependency(), UnlockDelegateDependency => T::WeightInfo::unlock_delegate_dependency(), + GetImmutableData(len) => T::WeightInfo::seal_get_immutable_data(len), + SetImmutableData(len) => T::WeightInfo::seal_set_immutable_data(len), } } } @@ -1513,6 +1519,36 @@ pub mod env { )?) } + /// Stores the immutable data into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::get_immutable_data`]. + #[api_version(0)] + fn get_immutable_data( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { + let charged = self.charge_gas(RuntimeCosts::GetImmutableData(limits::IMMUTABLE_BYTES))?; + let data = self.ext.get_immutable_data()?; + self.adjust_gas(charged, RuntimeCosts::GetImmutableData(data.len() as u32)); + self.write_sandbox_output(memory, out_ptr, out_len_ptr, &data, false, already_charged)?; + Ok(()) + } + + /// Attaches the supplied immutable data to the currently executing contract. + /// See [`pallet_revive_uapi::HostFn::set_immutable_data`]. + #[api_version(0)] + fn set_immutable_data(&mut self, memory: &mut M, ptr: u32, len: u32) -> Result<(), TrapReason> { + if len > limits::IMMUTABLE_BYTES { + return Err(Error::::OutOfBounds.into()); + } + self.charge_gas(RuntimeCosts::SetImmutableData(len))?; + let buf = memory.read(ptr, len)?; + let data = buf.try_into().expect("bailed out earlier; qed"); + self.ext.set_immutable_data(data)?; + Ok(()) + } + /// Stores the *free* balance of the current account into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::balance`]. #[api_version(0)] @@ -1930,7 +1966,9 @@ pub mod env { /// Replace the contract code at the specified address with new code. /// See [`pallet_revive_uapi::HostFn::set_code_hash`]. - #[api_version(0)] + /// + /// Disabled until the internal implementation takes care of collecting + /// the immutable data of the new code hash. #[mutating] fn set_code_hash( &mut self, diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 6586cd1fc9ce..5e549de3a9fb 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_revive` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-10-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-jniz7bxx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` @@ -69,6 +69,8 @@ pub trait WeightInfo { fn seal_weight_left() -> Weight; fn seal_balance() -> Weight; fn seal_balance_of() -> Weight; + fn seal_get_immutable_data(n: u32, ) -> Weight; + fn seal_set_immutable_data(n: u32, ) -> Weight; fn seal_value_transferred() -> Weight; fn seal_minimum_balance() -> Weight; fn seal_block_number() -> Weight; @@ -124,8 +126,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_783_000 picoseconds. - Weight::from_parts(2_883_000, 1594) + // Minimum execution time: 2_700_000 picoseconds. + Weight::from_parts(2_882_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -135,10 +137,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `392 + k * (69 ±0)` // Estimated: `382 + k * (70 ±0)` - // Minimum execution time: 13_394_000 picoseconds. - Weight::from_parts(305_292, 382) - // Standard Error: 1_217 - .saturating_add(Weight::from_parts(1_233_492, 0).saturating_mul(k.into())) + // Minimum execution time: 13_819_000 picoseconds. + Weight::from_parts(14_021_000, 382) + // Standard Error: 843 + .saturating_add(Weight::from_parts(1_222_715, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -146,7 +148,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) @@ -158,10 +160,10 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 262144]`. fn call_with_code_per_byte(_c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `834` - // Estimated: `4299` - // Minimum execution time: 78_977_000 picoseconds. - Weight::from_parts(81_687_641, 4299) + // Measured: `838` + // Estimated: `4303` + // Minimum execution time: 79_748_000 picoseconds. + Weight::from_parts(82_727_773, 4303) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -170,7 +172,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Balances::Holds` (r:2 w:2) /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) @@ -183,12 +185,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `303` // Estimated: `6232` - // Minimum execution time: 179_690_000 picoseconds. - Weight::from_parts(149_042_544, 6232) + // Minimum execution time: 179_270_000 picoseconds. + Weight::from_parts(149_933_578, 6232) // Standard Error: 11 - .saturating_add(Weight::from_parts(33, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(16, 0).saturating_mul(c.into())) // Standard Error: 11 - .saturating_add(Weight::from_parts(4_644, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(4_675, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -197,7 +199,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Revive::PristineCode` (r:1 w:0) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) @@ -209,15 +211,15 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4111` - // Minimum execution time: 156_389_000 picoseconds. - Weight::from_parts(130_603_882, 4111) + // Minimum execution time: 156_112_000 picoseconds. + Weight::from_parts(132_715_336, 4111) // Standard Error: 16 - .saturating_add(Weight::from_parts(4_594, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(4_599, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) @@ -228,10 +230,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `834` - // Estimated: `4299` - // Minimum execution time: 80_108_000 picoseconds. - Weight::from_parts(81_555_000, 4299) + // Measured: `838` + // Estimated: `4303` + // Minimum execution time: 82_111_000 picoseconds. + Weight::from_parts(83_659_000, 4303) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -246,8 +248,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 49_297_000 picoseconds. - Weight::from_parts(50_873_587, 3574) + // Minimum execution time: 49_154_000 picoseconds. + Weight::from_parts(50_856_127, 3574) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -261,21 +263,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `285` // Estimated: `3750` - // Minimum execution time: 42_556_000 picoseconds. - Weight::from_parts(43_708_000, 3750) + // Minimum execution time: 41_347_000 picoseconds. + Weight::from_parts(42_272_000, 3750) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:2 w:2) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `491` - // Estimated: `6431` - // Minimum execution time: 24_623_000 picoseconds. - Weight::from_parts(26_390_000, 6431) + // Measured: `495` + // Estimated: `6435` + // Minimum execution time: 25_234_000 picoseconds. + Weight::from_parts(26_339_000, 6435) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -284,79 +286,79 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_590_000 picoseconds. - Weight::from_parts(8_005_868, 0) - // Standard Error: 396 - .saturating_add(Weight::from_parts(203_612, 0).saturating_mul(r.into())) + // Minimum execution time: 7_509_000 picoseconds. + Weight::from_parts(9_047_452, 0) + // Standard Error: 165 + .saturating_add(Weight::from_parts(197_103, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 285_000 picoseconds. - Weight::from_parts(305_000, 0) + // Minimum execution time: 257_000 picoseconds. + Weight::from_parts(300_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_is_contract() -> Weight { // Proof Size summary in bytes: // Measured: `272` // Estimated: `3737` - // Minimum execution time: 6_711_000 picoseconds. - Weight::from_parts(7_103_000, 3737) + // Minimum execution time: 6_537_000 picoseconds. + Weight::from_parts(7_024_000, 3737) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `365` - // Estimated: `3830` - // Minimum execution time: 7_572_000 picoseconds. - Weight::from_parts(7_888_000, 3830) + // Measured: `369` + // Estimated: `3834` + // Minimum execution time: 7_571_000 picoseconds. + Weight::from_parts(8_060_000, 3834) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 240_000 picoseconds. - Weight::from_parts(264_000, 0) + // Minimum execution time: 254_000 picoseconds. + Weight::from_parts(281_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 324_000 picoseconds. - Weight::from_parts(356_000, 0) + // Minimum execution time: 279_000 picoseconds. + Weight::from_parts(338_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 273_000 picoseconds. + // Minimum execution time: 240_000 picoseconds. Weight::from_parts(286_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(296_000, 0) + // Minimum execution time: 198_000 picoseconds. + Weight::from_parts(262_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 610_000 picoseconds. - Weight::from_parts(701_000, 0) + // Minimum execution time: 593_000 picoseconds. + Weight::from_parts(665_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `0` - // Minimum execution time: 5_207_000 picoseconds. - Weight::from_parts(5_403_000, 0) + // Minimum execution time: 5_393_000 picoseconds. + Weight::from_parts(5_688_000, 0) } /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) @@ -364,37 +366,64 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3517` - // Minimum execution time: 3_829_000 picoseconds. - Weight::from_parts(3_977_000, 3517) + // Minimum execution time: 3_907_000 picoseconds. + Weight::from_parts(4_154_000, 3517) .saturating_add(T::DbWeight::get().reads(1_u64)) } + /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) + /// The range of component `n` is `[1, 4096]`. + fn seal_get_immutable_data(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `205 + n * (1 ±0)` + // Estimated: `3670 + n * (1 ±0)` + // Minimum execution time: 5_700_000 picoseconds. + Weight::from_parts(6_362_270, 3670) + // Standard Error: 4 + .saturating_add(Weight::from_parts(749, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Revive::ImmutableDataOf` (r:0 w:1) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) + /// The range of component `n` is `[1, 4096]`. + fn seal_set_immutable_data(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_041_000 picoseconds. + Weight::from_parts(2_216_694, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(620, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 245_000 picoseconds. - Weight::from_parts(266_000, 0) + // Minimum execution time: 229_000 picoseconds. + Weight::from_parts(261_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 256_000 picoseconds. - Weight::from_parts(288_000, 0) + // Minimum execution time: 226_000 picoseconds. + Weight::from_parts(270_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 265_000 picoseconds. - Weight::from_parts(278_000, 0) + // Minimum execution time: 215_000 picoseconds. + Weight::from_parts(273_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 243_000 picoseconds. - Weight::from_parts(272_000, 0) + // Minimum execution time: 242_000 picoseconds. + Weight::from_parts(271_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -402,8 +431,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 5_467_000 picoseconds. - Weight::from_parts(5_607_000, 1552) + // Minimum execution time: 5_438_000 picoseconds. + Weight::from_parts(5_695_000, 1552) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 262140]`. @@ -411,8 +440,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 438_000 picoseconds. - Weight::from_parts(532_907, 0) + // Minimum execution time: 405_000 picoseconds. + Weight::from_parts(539_737, 0) // Standard Error: 0 .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) } @@ -421,8 +450,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 259_000 picoseconds. - Weight::from_parts(629_625, 0) + // Minimum execution time: 222_000 picoseconds. + Weight::from_parts(539_301, 0) // Standard Error: 0 .saturating_add(Weight::from_parts(294, 0).saturating_mul(n.into())) } @@ -432,18 +461,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::DeletionQueue` (r:0 w:1) /// Proof: `Revive::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// Storage: `Revive::ImmutableDataOf` (r:0 w:1) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) /// The range of component `n` is `[0, 32]`. fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `272 + n * (88 ±0)` // Estimated: `3738 + n * (2563 ±0)` - // Minimum execution time: 14_997_000 picoseconds. - Weight::from_parts(17_752_993, 3738) - // Standard Error: 9_865 - .saturating_add(Weight::from_parts(4_159_693, 0).saturating_mul(n.into())) + // Minimum execution time: 15_971_000 picoseconds. + Weight::from_parts(18_759_349, 3738) + // Standard Error: 10_298 + .saturating_add(Weight::from_parts(4_194_635, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2563).saturating_mul(n.into())) } @@ -453,20 +484,20 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_277_000 picoseconds. - Weight::from_parts(4_023_910, 0) - // Standard Error: 2_210 - .saturating_add(Weight::from_parts(202_823, 0).saturating_mul(t.into())) - // Standard Error: 19 - .saturating_add(Weight::from_parts(1_141, 0).saturating_mul(n.into())) + // Minimum execution time: 4_341_000 picoseconds. + Weight::from_parts(4_292_048, 0) + // Standard Error: 2_476 + .saturating_add(Weight::from_parts(193_480, 0).saturating_mul(t.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(959, 0).saturating_mul(n.into())) } /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 309_000 picoseconds. - Weight::from_parts(834_030, 0) + // Minimum execution time: 287_000 picoseconds. + Weight::from_parts(758_020, 0) // Standard Error: 1 .saturating_add(Weight::from_parts(814, 0).saturating_mul(i.into())) } @@ -476,8 +507,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 7_705_000 picoseconds. - Weight::from_parts(7_923_000, 744) + // Minimum execution time: 7_944_000 picoseconds. + Weight::from_parts(8_358_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -486,8 +517,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 44_510_000 picoseconds. - Weight::from_parts(45_840_000, 10754) + // Minimum execution time: 44_950_000 picoseconds. + Weight::from_parts(45_575_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -496,8 +527,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 8_842_000 picoseconds. - Weight::from_parts(9_363_000, 744) + // Minimum execution time: 9_095_000 picoseconds. + Weight::from_parts(9_484_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -507,8 +538,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 46_172_000 picoseconds. - Weight::from_parts(47_586_000, 10754) + // Minimum execution time: 46_814_000 picoseconds. + Weight::from_parts(47_710_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -520,12 +551,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 9_158_000 picoseconds. - Weight::from_parts(9_708_320, 247) - // Standard Error: 36 - .saturating_add(Weight::from_parts(499, 0).saturating_mul(n.into())) - // Standard Error: 36 - .saturating_add(Weight::from_parts(672, 0).saturating_mul(o.into())) + // Minimum execution time: 9_496_000 picoseconds. + Weight::from_parts(9_875_533, 247) + // Standard Error: 34 + .saturating_add(Weight::from_parts(712, 0).saturating_mul(n.into())) + // Standard Error: 34 + .saturating_add(Weight::from_parts(777, 0).saturating_mul(o.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -537,10 +568,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_885_000 picoseconds. - Weight::from_parts(9_597_656, 247) - // Standard Error: 48 - .saturating_add(Weight::from_parts(649, 0).saturating_mul(n.into())) + // Minimum execution time: 9_185_000 picoseconds. + Weight::from_parts(9_912_715, 247) + // Standard Error: 59 + .saturating_add(Weight::from_parts(567, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -552,10 +583,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_473_000 picoseconds. - Weight::from_parts(9_246_006, 247) - // Standard Error: 47 - .saturating_add(Weight::from_parts(1_468, 0).saturating_mul(n.into())) + // Minimum execution time: 8_586_000 picoseconds. + Weight::from_parts(9_536_041, 247) + // Standard Error: 54 + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -566,10 +597,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_996_000 picoseconds. - Weight::from_parts(8_784_165, 247) + // Minimum execution time: 8_162_000 picoseconds. + Weight::from_parts(8_813_128, 247) // Standard Error: 43 - .saturating_add(Weight::from_parts(591, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(880, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -580,10 +611,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 9_246_000 picoseconds. - Weight::from_parts(10_239_803, 247) - // Standard Error: 57 - .saturating_add(Weight::from_parts(1_305, 0).saturating_mul(n.into())) + // Minimum execution time: 9_717_000 picoseconds. + Weight::from_parts(10_635_621, 247) + // Standard Error: 58 + .saturating_add(Weight::from_parts(1_430, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -592,36 +623,36 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_422_000 picoseconds. - Weight::from_parts(1_531_000, 0) + // Minimum execution time: 1_503_000 picoseconds. + Weight::from_parts(1_592_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_858_000 picoseconds. - Weight::from_parts(1_944_000, 0) + // Minimum execution time: 1_903_000 picoseconds. + Weight::from_parts(1_996_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_443_000 picoseconds. - Weight::from_parts(1_506_000, 0) + // Minimum execution time: 1_432_000 picoseconds. + Weight::from_parts(1_534_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_592_000 picoseconds. - Weight::from_parts(1_651_000, 0) + // Minimum execution time: 1_606_000 picoseconds. + Weight::from_parts(1_669_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 960_000 picoseconds. - Weight::from_parts(1_060_000, 0) + Weight::from_parts(1_089_000, 0) } /// The range of component `n` is `[0, 512]`. /// The range of component `o` is `[0, 512]`. @@ -629,62 +660,62 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_151_000 picoseconds. - Weight::from_parts(2_294_801, 0) + // Minimum execution time: 2_317_000 picoseconds. + Weight::from_parts(2_449_933, 0) // Standard Error: 11 - .saturating_add(Weight::from_parts(375, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(315, 0).saturating_mul(n.into())) // Standard Error: 11 - .saturating_add(Weight::from_parts(415, 0).saturating_mul(o.into())) + .saturating_add(Weight::from_parts(387, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_880_000 picoseconds. - Weight::from_parts(2_259_773, 0) + // Minimum execution time: 1_980_000 picoseconds. + Weight::from_parts(2_352_243, 0) // Standard Error: 17 - .saturating_add(Weight::from_parts(356, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(372, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_755_000 picoseconds. - Weight::from_parts(1_968_235, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(387, 0).saturating_mul(n.into())) + // Minimum execution time: 1_857_000 picoseconds. + Weight::from_parts(2_082_050, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(373, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_618_000 picoseconds. - Weight::from_parts(1_811_972, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(174, 0).saturating_mul(n.into())) + // Minimum execution time: 1_693_000 picoseconds. + Weight::from_parts(1_892_600, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(193, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_take_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_482_000 picoseconds. - Weight::from_parts(2_682_484, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(2, 0).saturating_mul(n.into())) + // Minimum execution time: 2_620_000 picoseconds. + Weight::from_parts(2_818_388, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(39, 0).saturating_mul(n.into())) } fn seal_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `0` - // Minimum execution time: 9_899_000 picoseconds. - Weight::from_parts(10_342_000, 0) + // Minimum execution time: 10_207_000 picoseconds. + Weight::from_parts(10_627_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) @@ -693,12 +724,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `626 + t * (140 ±0)` - // Estimated: `4091 + t * (140 ±0)` - // Minimum execution time: 33_645_000 picoseconds. - Weight::from_parts(34_407_662, 4091) - // Standard Error: 36_930 - .saturating_add(Weight::from_parts(2_062_425, 0).saturating_mul(t.into())) + // Measured: `630 + t * (140 ±0)` + // Estimated: `4095 + t * (140 ±0)` + // Minimum execution time: 34_452_000 picoseconds. + Weight::from_parts(34_837_900, 4095) + // Standard Error: 30_385 + .saturating_add(Weight::from_parts(1_981_565, 0).saturating_mul(t.into())) // Standard Error: 0 .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) @@ -713,8 +744,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `457` // Estimated: `3922` - // Minimum execution time: 26_924_000 picoseconds. - Weight::from_parts(27_753_000, 3922) + // Minimum execution time: 27_287_000 picoseconds. + Weight::from_parts(28_450_000, 3922) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -722,7 +753,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Revive::PristineCode` (r:1 w:0) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `i` is `[0, 262144]`. @@ -730,10 +761,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `703` // Estimated: `4160` - // Minimum execution time: 117_979_000 picoseconds. - Weight::from_parts(105_415_117, 4160) + // Minimum execution time: 118_660_000 picoseconds. + Weight::from_parts(104_638_796, 4160) // Standard Error: 11 - .saturating_add(Weight::from_parts(4_293, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(4_296, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -742,64 +773,64 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 620_000 picoseconds. - Weight::from_parts(3_414_286, 0) + // Minimum execution time: 683_000 picoseconds. + Weight::from_parts(3_836_563, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(1_463, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_043_000 picoseconds. - Weight::from_parts(3_402_639, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_667, 0).saturating_mul(n.into())) + // Minimum execution time: 1_082_000 picoseconds. + Weight::from_parts(5_027_764, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_644, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 642_000 picoseconds. - Weight::from_parts(3_359_294, 0) + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(3_585_640, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(1_590, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_586, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 606_000 picoseconds. - Weight::from_parts(3_789_868, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_575, 0).saturating_mul(n.into())) + // Minimum execution time: 638_000 picoseconds. + Weight::from_parts(3_763_242, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_582, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 46_195_000 picoseconds. - Weight::from_parts(31_420_941, 0) + // Minimum execution time: 42_962_000 picoseconds. + Weight::from_parts(27_938_396, 0) // Standard Error: 13 - .saturating_add(Weight::from_parts(5_165, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(5_269, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 46_933_000 picoseconds. - Weight::from_parts(48_054_000, 0) + // Minimum execution time: 47_133_000 picoseconds. + Weight::from_parts(48_458_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_531_000 picoseconds. - Weight::from_parts(12_690_000, 0) + // Minimum execution time: 13_249_000 picoseconds. + Weight::from_parts(13_518_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -807,8 +838,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `266` // Estimated: `3731` - // Minimum execution time: 14_694_000 picoseconds. - Weight::from_parts(15_032_000, 3731) + // Minimum execution time: 14_696_000 picoseconds. + Weight::from_parts(15_106_000, 3731) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -818,8 +849,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `304` // Estimated: `3769` - // Minimum execution time: 10_205_000 picoseconds. - Weight::from_parts(10_707_000, 3769) + // Minimum execution time: 10_292_000 picoseconds. + Weight::from_parts(10_670_000, 3769) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -829,8 +860,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `304` // Estimated: `3561` - // Minimum execution time: 9_025_000 picoseconds. - Weight::from_parts(9_517_000, 3561) + // Minimum execution time: 9_056_000 picoseconds. + Weight::from_parts(9_350_000, 3561) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -839,10 +870,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_451_000 picoseconds. - Weight::from_parts(10_620_260, 0) - // Standard Error: 77 - .saturating_add(Weight::from_parts(84_885, 0).saturating_mul(r.into())) + // Minimum execution time: 9_145_000 picoseconds. + Weight::from_parts(10_744_073, 0) + // Standard Error: 72 + .saturating_add(Weight::from_parts(84_813, 0).saturating_mul(r.into())) } } @@ -854,8 +885,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 2_783_000 picoseconds. - Weight::from_parts(2_883_000, 1594) + // Minimum execution time: 2_700_000 picoseconds. + Weight::from_parts(2_882_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -865,10 +896,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `392 + k * (69 ±0)` // Estimated: `382 + k * (70 ±0)` - // Minimum execution time: 13_394_000 picoseconds. - Weight::from_parts(305_292, 382) - // Standard Error: 1_217 - .saturating_add(Weight::from_parts(1_233_492, 0).saturating_mul(k.into())) + // Minimum execution time: 13_819_000 picoseconds. + Weight::from_parts(14_021_000, 382) + // Standard Error: 843 + .saturating_add(Weight::from_parts(1_222_715, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -876,7 +907,7 @@ impl WeightInfo for () { .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) @@ -888,10 +919,10 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 262144]`. fn call_with_code_per_byte(_c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `834` - // Estimated: `4299` - // Minimum execution time: 78_977_000 picoseconds. - Weight::from_parts(81_687_641, 4299) + // Measured: `838` + // Estimated: `4303` + // Minimum execution time: 79_748_000 picoseconds. + Weight::from_parts(82_727_773, 4303) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -900,7 +931,7 @@ impl WeightInfo for () { /// Storage: `Balances::Holds` (r:2 w:2) /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) @@ -913,12 +944,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `303` // Estimated: `6232` - // Minimum execution time: 179_690_000 picoseconds. - Weight::from_parts(149_042_544, 6232) + // Minimum execution time: 179_270_000 picoseconds. + Weight::from_parts(149_933_578, 6232) // Standard Error: 11 - .saturating_add(Weight::from_parts(33, 0).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(16, 0).saturating_mul(c.into())) // Standard Error: 11 - .saturating_add(Weight::from_parts(4_644, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(4_675, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -927,7 +958,7 @@ impl WeightInfo for () { /// Storage: `Revive::PristineCode` (r:1 w:0) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) @@ -939,15 +970,15 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `657` // Estimated: `4111` - // Minimum execution time: 156_389_000 picoseconds. - Weight::from_parts(130_603_882, 4111) + // Minimum execution time: 156_112_000 picoseconds. + Weight::from_parts(132_715_336, 4111) // Standard Error: 16 - .saturating_add(Weight::from_parts(4_594, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(4_599, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) @@ -958,10 +989,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `834` - // Estimated: `4299` - // Minimum execution time: 80_108_000 picoseconds. - Weight::from_parts(81_555_000, 4299) + // Measured: `838` + // Estimated: `4303` + // Minimum execution time: 82_111_000 picoseconds. + Weight::from_parts(83_659_000, 4303) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -976,8 +1007,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 49_297_000 picoseconds. - Weight::from_parts(50_873_587, 3574) + // Minimum execution time: 49_154_000 picoseconds. + Weight::from_parts(50_856_127, 3574) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -991,21 +1022,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `285` // Estimated: `3750` - // Minimum execution time: 42_556_000 picoseconds. - Weight::from_parts(43_708_000, 3750) + // Minimum execution time: 41_347_000 picoseconds. + Weight::from_parts(42_272_000, 3750) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:2 w:2) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `491` - // Estimated: `6431` - // Minimum execution time: 24_623_000 picoseconds. - Weight::from_parts(26_390_000, 6431) + // Measured: `495` + // Estimated: `6435` + // Minimum execution time: 25_234_000 picoseconds. + Weight::from_parts(26_339_000, 6435) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1014,79 +1045,79 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_590_000 picoseconds. - Weight::from_parts(8_005_868, 0) - // Standard Error: 396 - .saturating_add(Weight::from_parts(203_612, 0).saturating_mul(r.into())) + // Minimum execution time: 7_509_000 picoseconds. + Weight::from_parts(9_047_452, 0) + // Standard Error: 165 + .saturating_add(Weight::from_parts(197_103, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 285_000 picoseconds. - Weight::from_parts(305_000, 0) + // Minimum execution time: 257_000 picoseconds. + Weight::from_parts(300_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_is_contract() -> Weight { // Proof Size summary in bytes: // Measured: `272` // Estimated: `3737` - // Minimum execution time: 6_711_000 picoseconds. - Weight::from_parts(7_103_000, 3737) + // Minimum execution time: 6_537_000 picoseconds. + Weight::from_parts(7_024_000, 3737) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `365` - // Estimated: `3830` - // Minimum execution time: 7_572_000 picoseconds. - Weight::from_parts(7_888_000, 3830) + // Measured: `369` + // Estimated: `3834` + // Minimum execution time: 7_571_000 picoseconds. + Weight::from_parts(8_060_000, 3834) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 240_000 picoseconds. - Weight::from_parts(264_000, 0) + // Minimum execution time: 254_000 picoseconds. + Weight::from_parts(281_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 324_000 picoseconds. - Weight::from_parts(356_000, 0) + // Minimum execution time: 279_000 picoseconds. + Weight::from_parts(338_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 273_000 picoseconds. + // Minimum execution time: 240_000 picoseconds. Weight::from_parts(286_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(296_000, 0) + // Minimum execution time: 198_000 picoseconds. + Weight::from_parts(262_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 610_000 picoseconds. - Weight::from_parts(701_000, 0) + // Minimum execution time: 593_000 picoseconds. + Weight::from_parts(665_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `0` - // Minimum execution time: 5_207_000 picoseconds. - Weight::from_parts(5_403_000, 0) + // Minimum execution time: 5_393_000 picoseconds. + Weight::from_parts(5_688_000, 0) } /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) @@ -1094,37 +1125,64 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `52` // Estimated: `3517` - // Minimum execution time: 3_829_000 picoseconds. - Weight::from_parts(3_977_000, 3517) + // Minimum execution time: 3_907_000 picoseconds. + Weight::from_parts(4_154_000, 3517) .saturating_add(RocksDbWeight::get().reads(1_u64)) } + /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) + /// The range of component `n` is `[1, 4096]`. + fn seal_get_immutable_data(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `205 + n * (1 ±0)` + // Estimated: `3670 + n * (1 ±0)` + // Minimum execution time: 5_700_000 picoseconds. + Weight::from_parts(6_362_270, 3670) + // Standard Error: 4 + .saturating_add(Weight::from_parts(749, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Revive::ImmutableDataOf` (r:0 w:1) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) + /// The range of component `n` is `[1, 4096]`. + fn seal_set_immutable_data(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 2_041_000 picoseconds. + Weight::from_parts(2_216_694, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(620, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 245_000 picoseconds. - Weight::from_parts(266_000, 0) + // Minimum execution time: 229_000 picoseconds. + Weight::from_parts(261_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 256_000 picoseconds. - Weight::from_parts(288_000, 0) + // Minimum execution time: 226_000 picoseconds. + Weight::from_parts(270_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 265_000 picoseconds. - Weight::from_parts(278_000, 0) + // Minimum execution time: 215_000 picoseconds. + Weight::from_parts(273_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 243_000 picoseconds. - Weight::from_parts(272_000, 0) + // Minimum execution time: 242_000 picoseconds. + Weight::from_parts(271_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -1132,8 +1190,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 5_467_000 picoseconds. - Weight::from_parts(5_607_000, 1552) + // Minimum execution time: 5_438_000 picoseconds. + Weight::from_parts(5_695_000, 1552) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// The range of component `n` is `[0, 262140]`. @@ -1141,8 +1199,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 438_000 picoseconds. - Weight::from_parts(532_907, 0) + // Minimum execution time: 405_000 picoseconds. + Weight::from_parts(539_737, 0) // Standard Error: 0 .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) } @@ -1151,8 +1209,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 259_000 picoseconds. - Weight::from_parts(629_625, 0) + // Minimum execution time: 222_000 picoseconds. + Weight::from_parts(539_301, 0) // Standard Error: 0 .saturating_add(Weight::from_parts(294, 0).saturating_mul(n.into())) } @@ -1162,18 +1220,20 @@ impl WeightInfo for () { /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::DeletionQueue` (r:0 w:1) /// Proof: `Revive::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// Storage: `Revive::ImmutableDataOf` (r:0 w:1) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) /// The range of component `n` is `[0, 32]`. fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `272 + n * (88 ±0)` // Estimated: `3738 + n * (2563 ±0)` - // Minimum execution time: 14_997_000 picoseconds. - Weight::from_parts(17_752_993, 3738) - // Standard Error: 9_865 - .saturating_add(Weight::from_parts(4_159_693, 0).saturating_mul(n.into())) + // Minimum execution time: 15_971_000 picoseconds. + Weight::from_parts(18_759_349, 3738) + // Standard Error: 10_298 + .saturating_add(Weight::from_parts(4_194_635, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2563).saturating_mul(n.into())) } @@ -1183,20 +1243,20 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_277_000 picoseconds. - Weight::from_parts(4_023_910, 0) - // Standard Error: 2_210 - .saturating_add(Weight::from_parts(202_823, 0).saturating_mul(t.into())) - // Standard Error: 19 - .saturating_add(Weight::from_parts(1_141, 0).saturating_mul(n.into())) + // Minimum execution time: 4_341_000 picoseconds. + Weight::from_parts(4_292_048, 0) + // Standard Error: 2_476 + .saturating_add(Weight::from_parts(193_480, 0).saturating_mul(t.into())) + // Standard Error: 22 + .saturating_add(Weight::from_parts(959, 0).saturating_mul(n.into())) } /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 309_000 picoseconds. - Weight::from_parts(834_030, 0) + // Minimum execution time: 287_000 picoseconds. + Weight::from_parts(758_020, 0) // Standard Error: 1 .saturating_add(Weight::from_parts(814, 0).saturating_mul(i.into())) } @@ -1206,8 +1266,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 7_705_000 picoseconds. - Weight::from_parts(7_923_000, 744) + // Minimum execution time: 7_944_000 picoseconds. + Weight::from_parts(8_358_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1216,8 +1276,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 44_510_000 picoseconds. - Weight::from_parts(45_840_000, 10754) + // Minimum execution time: 44_950_000 picoseconds. + Weight::from_parts(45_575_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1226,8 +1286,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `744` // Estimated: `744` - // Minimum execution time: 8_842_000 picoseconds. - Weight::from_parts(9_363_000, 744) + // Minimum execution time: 9_095_000 picoseconds. + Weight::from_parts(9_484_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1237,8 +1297,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10754` // Estimated: `10754` - // Minimum execution time: 46_172_000 picoseconds. - Weight::from_parts(47_586_000, 10754) + // Minimum execution time: 46_814_000 picoseconds. + Weight::from_parts(47_710_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1250,12 +1310,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 9_158_000 picoseconds. - Weight::from_parts(9_708_320, 247) - // Standard Error: 36 - .saturating_add(Weight::from_parts(499, 0).saturating_mul(n.into())) - // Standard Error: 36 - .saturating_add(Weight::from_parts(672, 0).saturating_mul(o.into())) + // Minimum execution time: 9_496_000 picoseconds. + Weight::from_parts(9_875_533, 247) + // Standard Error: 34 + .saturating_add(Weight::from_parts(712, 0).saturating_mul(n.into())) + // Standard Error: 34 + .saturating_add(Weight::from_parts(777, 0).saturating_mul(o.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -1267,10 +1327,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_885_000 picoseconds. - Weight::from_parts(9_597_656, 247) - // Standard Error: 48 - .saturating_add(Weight::from_parts(649, 0).saturating_mul(n.into())) + // Minimum execution time: 9_185_000 picoseconds. + Weight::from_parts(9_912_715, 247) + // Standard Error: 59 + .saturating_add(Weight::from_parts(567, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1282,10 +1342,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_473_000 picoseconds. - Weight::from_parts(9_246_006, 247) - // Standard Error: 47 - .saturating_add(Weight::from_parts(1_468, 0).saturating_mul(n.into())) + // Minimum execution time: 8_586_000 picoseconds. + Weight::from_parts(9_536_041, 247) + // Standard Error: 54 + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1296,10 +1356,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_996_000 picoseconds. - Weight::from_parts(8_784_165, 247) + // Minimum execution time: 8_162_000 picoseconds. + Weight::from_parts(8_813_128, 247) // Standard Error: 43 - .saturating_add(Weight::from_parts(591, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(880, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1310,10 +1370,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 9_246_000 picoseconds. - Weight::from_parts(10_239_803, 247) - // Standard Error: 57 - .saturating_add(Weight::from_parts(1_305, 0).saturating_mul(n.into())) + // Minimum execution time: 9_717_000 picoseconds. + Weight::from_parts(10_635_621, 247) + // Standard Error: 58 + .saturating_add(Weight::from_parts(1_430, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1322,36 +1382,36 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_422_000 picoseconds. - Weight::from_parts(1_531_000, 0) + // Minimum execution time: 1_503_000 picoseconds. + Weight::from_parts(1_592_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_858_000 picoseconds. - Weight::from_parts(1_944_000, 0) + // Minimum execution time: 1_903_000 picoseconds. + Weight::from_parts(1_996_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_443_000 picoseconds. - Weight::from_parts(1_506_000, 0) + // Minimum execution time: 1_432_000 picoseconds. + Weight::from_parts(1_534_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_592_000 picoseconds. - Weight::from_parts(1_651_000, 0) + // Minimum execution time: 1_606_000 picoseconds. + Weight::from_parts(1_669_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 960_000 picoseconds. - Weight::from_parts(1_060_000, 0) + Weight::from_parts(1_089_000, 0) } /// The range of component `n` is `[0, 512]`. /// The range of component `o` is `[0, 512]`. @@ -1359,62 +1419,62 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_151_000 picoseconds. - Weight::from_parts(2_294_801, 0) + // Minimum execution time: 2_317_000 picoseconds. + Weight::from_parts(2_449_933, 0) // Standard Error: 11 - .saturating_add(Weight::from_parts(375, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(315, 0).saturating_mul(n.into())) // Standard Error: 11 - .saturating_add(Weight::from_parts(415, 0).saturating_mul(o.into())) + .saturating_add(Weight::from_parts(387, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_880_000 picoseconds. - Weight::from_parts(2_259_773, 0) + // Minimum execution time: 1_980_000 picoseconds. + Weight::from_parts(2_352_243, 0) // Standard Error: 17 - .saturating_add(Weight::from_parts(356, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(372, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_755_000 picoseconds. - Weight::from_parts(1_968_235, 0) - // Standard Error: 15 - .saturating_add(Weight::from_parts(387, 0).saturating_mul(n.into())) + // Minimum execution time: 1_857_000 picoseconds. + Weight::from_parts(2_082_050, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(373, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_618_000 picoseconds. - Weight::from_parts(1_811_972, 0) - // Standard Error: 12 - .saturating_add(Weight::from_parts(174, 0).saturating_mul(n.into())) + // Minimum execution time: 1_693_000 picoseconds. + Weight::from_parts(1_892_600, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(193, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 512]`. fn seal_take_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_482_000 picoseconds. - Weight::from_parts(2_682_484, 0) - // Standard Error: 13 - .saturating_add(Weight::from_parts(2, 0).saturating_mul(n.into())) + // Minimum execution time: 2_620_000 picoseconds. + Weight::from_parts(2_818_388, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(39, 0).saturating_mul(n.into())) } fn seal_transfer() -> Weight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `0` - // Minimum execution time: 9_899_000 picoseconds. - Weight::from_parts(10_342_000, 0) + // Minimum execution time: 10_207_000 picoseconds. + Weight::from_parts(10_627_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Revive::CodeInfoOf` (r:1 w:0) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Revive::PristineCode` (r:1 w:0) @@ -1423,12 +1483,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `626 + t * (140 ±0)` - // Estimated: `4091 + t * (140 ±0)` - // Minimum execution time: 33_645_000 picoseconds. - Weight::from_parts(34_407_662, 4091) - // Standard Error: 36_930 - .saturating_add(Weight::from_parts(2_062_425, 0).saturating_mul(t.into())) + // Measured: `630 + t * (140 ±0)` + // Estimated: `4095 + t * (140 ±0)` + // Minimum execution time: 34_452_000 picoseconds. + Weight::from_parts(34_837_900, 4095) + // Standard Error: 30_385 + .saturating_add(Weight::from_parts(1_981_565, 0).saturating_mul(t.into())) // Standard Error: 0 .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) @@ -1443,8 +1503,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `457` // Estimated: `3922` - // Minimum execution time: 26_924_000 picoseconds. - Weight::from_parts(27_753_000, 3922) + // Minimum execution time: 27_287_000 picoseconds. + Weight::from_parts(28_450_000, 3922) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -1452,7 +1512,7 @@ impl WeightInfo for () { /// Storage: `Revive::PristineCode` (r:1 w:0) /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// Storage: `Revive::ContractInfoOf` (r:1 w:1) - /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1775), added: 4250, mode: `Measured`) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// The range of component `i` is `[0, 262144]`. @@ -1460,10 +1520,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `703` // Estimated: `4160` - // Minimum execution time: 117_979_000 picoseconds. - Weight::from_parts(105_415_117, 4160) + // Minimum execution time: 118_660_000 picoseconds. + Weight::from_parts(104_638_796, 4160) // Standard Error: 11 - .saturating_add(Weight::from_parts(4_293, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(4_296, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1472,64 +1532,64 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 620_000 picoseconds. - Weight::from_parts(3_414_286, 0) + // Minimum execution time: 683_000 picoseconds. + Weight::from_parts(3_836_563, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(1_463, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_456, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_043_000 picoseconds. - Weight::from_parts(3_402_639, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(3_667, 0).saturating_mul(n.into())) + // Minimum execution time: 1_082_000 picoseconds. + Weight::from_parts(5_027_764, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_644, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 642_000 picoseconds. - Weight::from_parts(3_359_294, 0) + // Minimum execution time: 660_000 picoseconds. + Weight::from_parts(3_585_640, 0) // Standard Error: 2 - .saturating_add(Weight::from_parts(1_590, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_586, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 606_000 picoseconds. - Weight::from_parts(3_789_868, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_575, 0).saturating_mul(n.into())) + // Minimum execution time: 638_000 picoseconds. + Weight::from_parts(3_763_242, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_582, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 46_195_000 picoseconds. - Weight::from_parts(31_420_941, 0) + // Minimum execution time: 42_962_000 picoseconds. + Weight::from_parts(27_938_396, 0) // Standard Error: 13 - .saturating_add(Weight::from_parts(5_165, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(5_269, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 46_933_000 picoseconds. - Weight::from_parts(48_054_000, 0) + // Minimum execution time: 47_133_000 picoseconds. + Weight::from_parts(48_458_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_531_000 picoseconds. - Weight::from_parts(12_690_000, 0) + // Minimum execution time: 13_249_000 picoseconds. + Weight::from_parts(13_518_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -1537,8 +1597,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `266` // Estimated: `3731` - // Minimum execution time: 14_694_000 picoseconds. - Weight::from_parts(15_032_000, 3731) + // Minimum execution time: 14_696_000 picoseconds. + Weight::from_parts(15_106_000, 3731) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1548,8 +1608,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `304` // Estimated: `3769` - // Minimum execution time: 10_205_000 picoseconds. - Weight::from_parts(10_707_000, 3769) + // Minimum execution time: 10_292_000 picoseconds. + Weight::from_parts(10_670_000, 3769) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1559,8 +1619,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `304` // Estimated: `3561` - // Minimum execution time: 9_025_000 picoseconds. - Weight::from_parts(9_517_000, 3561) + // Minimum execution time: 9_056_000 picoseconds. + Weight::from_parts(9_350_000, 3561) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1569,9 +1629,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_451_000 picoseconds. - Weight::from_parts(10_620_260, 0) - // Standard Error: 77 - .saturating_add(Weight::from_parts(84_885, 0).saturating_mul(r.into())) + // Minimum execution time: 9_145_000 picoseconds. + Weight::from_parts(10_744_073, 0) + // Standard Error: 72 + .saturating_add(Weight::from_parts(84_813, 0).saturating_mul(r.into())) } } diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index 816fdec3aaaf..2106b8fb49b7 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -56,6 +56,30 @@ pub trait HostFn: private::Sealed { /// otherwise. fn lock_delegate_dependency(code_hash: &[u8; 32]); + /// Get the contract immutable data. + /// + /// Traps if: + /// - Called from within the deploy export. + /// - Called by contracts that didn't set immutable data by calling `set_immutable_data` during + /// their constructor execution. + /// + /// # Parameters + /// - `output`: A reference to the output buffer to write the immutable bytes. + fn get_immutable_data(output: &mut &mut [u8]); + + /// Set the contract immutable data. + /// + /// It is only valid to set non-empty immutable data in the constructor once. + /// + /// Traps if: + /// - Called from within the call export. + /// - Called more than once. + /// - The provided data was empty. + /// + /// # Parameters + /// - `data`: A reference to the data to be stored as immutable bytes. + fn set_immutable_data(data: &[u8]); + /// Stores the **reducible** balance of the current account into the supplied buffer. /// /// # Parameters diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index d5ea94c1a910..866b0ee8dd17 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -81,6 +81,8 @@ mod sys { pub fn address(out_ptr: *mut u8); pub fn weight_to_fee(ref_time: u64, proof_size: u64, out_ptr: *mut u8); pub fn weight_left(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn get_immutable_data(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn set_immutable_data(ptr: *const u8, len: u32); pub fn balance(out_ptr: *mut u8); pub fn balance_of(addr_ptr: *const u8, out_ptr: *mut u8); pub fn chain_id(out_ptr: *mut u8); @@ -502,6 +504,16 @@ impl HostFn for HostFnImpl { ret_val.into_bool() } + fn get_immutable_data(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { sys::get_immutable_data(output.as_mut_ptr(), &mut output_len) }; + extract_from_slice(output, output_len as usize); + } + + fn set_immutable_data(data: &[u8]) { + unsafe { sys::set_immutable_data(data.as_ptr(), data.len() as u32) } + } + fn balance_of(address: &[u8; 20], output: &mut [u8; 32]) { unsafe { sys::balance_of(address.as_ptr(), output.as_mut_ptr()) }; } diff --git a/substrate/frame/support/src/traits/tokens/fungible/hold.rs b/substrate/frame/support/src/traits/tokens/fungible/hold.rs index 28ece25c91d4..6737cfe707ac 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/hold.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/hold.rs @@ -430,7 +430,11 @@ pub trait Mutate: } /// Trait for slashing a fungible asset which can be place on hold. -pub trait Balanced: super::Balanced + Unbalanced { +pub trait Balanced: + super::Balanced + + Unbalanced + + DoneSlash +{ /// Reduce the balance of some funds on hold in an account. /// /// The resulting imbalance is the first item of the tuple returned. @@ -449,6 +453,16 @@ pub trait Balanced: super::Balanced + Unbalanced { + fn done_slash(_reason: &Reason, _who: &AccountId, _amount: Balance) {} +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl DoneSlash for Tuple { + fn done_slash(reason: &Reason, who: &AccountId, amount: Balance) { + for_tuples!( #( Tuple::done_slash(reason, who, amount); )* ); + } } diff --git a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs index c9f366911a8b..309288d8278f 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs @@ -30,6 +30,7 @@ use crate::traits::{ WithdrawConsequence, }, }; +use frame_support::traits::fungible::hold::DoneSlash; use sp_core::Get; use sp_runtime::{DispatchError, DispatchResult}; @@ -467,5 +468,21 @@ impl< } } +impl< + F: fungibles::BalancedHold, + A: Get<>::AssetId>, + AccountId, + > DoneSlash for ItemOf +{ + fn done_slash(reason: &F::Reason, who: &AccountId, amount: F::Balance) { + >::done_slash( + A::get(), + reason, + who, + amount, + ) + } +} + #[test] fn test() {} diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs index 3adbbdda3143..5cb1d0a9e7b0 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs @@ -844,8 +844,10 @@ impl< } impl< - Left: fungible::BalancedHold, - Right: fungibles::BalancedHold, + Left: fungible::BalancedHold + + fungible::hold::DoneSlash, + Right: fungibles::BalancedHold + + fungibles::hold::DoneSlash, Criterion: Convert>, AssetKind: AssetId, AccountId, @@ -871,6 +873,29 @@ impl< } } } +impl< + Reason, + Balance, + Left: fungible::hold::DoneSlash, + Right: fungibles::hold::DoneSlash + + fungibles::Inspect, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::hold::DoneSlash + for UnionOf +{ + fn done_slash(asset: AssetKind, reason: &Reason, who: &AccountId, amount: Balance) { + match Criterion::convert(asset.clone()) { + Left(()) => { + Left::done_slash(reason, who, amount); + }, + Right(a) => { + Right::done_slash(a, reason, who, amount); + }, + } + } +} impl< Left: fungible::Inspect, diff --git a/substrate/frame/support/src/traits/tokens/fungibles/hold.rs b/substrate/frame/support/src/traits/tokens/fungibles/hold.rs index ef3fef7a300d..026bfc872e0c 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/hold.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/hold.rs @@ -214,7 +214,11 @@ pub trait Unbalanced: Inspect { } /// Trait for slashing a fungible asset which can be place on hold. -pub trait Balanced: super::Balanced + Unbalanced { +pub trait Balanced: + super::Balanced + + Unbalanced + + DoneSlash +{ /// Reduce the balance of some funds on hold in an account. /// /// The resulting imbalance is the first item of the tuple returned. @@ -238,13 +242,19 @@ pub trait Balanced: super::Balanced + Unbalanced { + fn done_slash(_asset: AssetId, _reason: &Reason, _who: &AccountId, _amount: Balance) {} +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl DoneSlash + for Tuple +{ + fn done_slash(asset_id: AssetId, reason: &Reason, who: &AccountId, amount: Balance) { + for_tuples!( #( Tuple::done_slash(asset_id, reason, who, amount); )* ); } } diff --git a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs index 77047150e00c..ec066dddcfac 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs @@ -825,8 +825,10 @@ impl< } impl< - Left: fungibles::BalancedHold, - Right: fungibles::BalancedHold, + Left: fungibles::BalancedHold + + fungibles::hold::DoneSlash, + Right: fungibles::BalancedHold + + fungibles::hold::DoneSlash, Criterion: Convert>, AssetKind: AssetId, AccountId, @@ -853,6 +855,31 @@ impl< } } +impl< + Reason, + Balance, + Left: fungibles::Inspect + + fungibles::hold::DoneSlash, + Right: fungibles::Inspect + + fungibles::hold::DoneSlash, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::hold::DoneSlash + for UnionOf +{ + fn done_slash(asset: AssetKind, reason: &Reason, who: &AccountId, amount: Balance) { + match Criterion::convert(asset.clone()) { + Left(a) => { + Left::done_slash(a, reason, who, amount); + }, + Right(a) => { + Right::done_slash(a, reason, who, amount); + }, + } + } +} + impl< Left: fungibles::Inspect + fungibles::Create, Right: fungibles::Inspect + fungibles::Create, diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index 840081003b84..74965264c034 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -391,6 +391,7 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; + type DoneSlashHandler = (); } impl substrate_test_pallet::Config for Runtime {} diff --git a/templates/minimal/node/src/chain_spec.rs b/templates/minimal/node/src/chain_spec.rs index 0646460acef6..17b98137b416 100644 --- a/templates/minimal/node/src/chain_spec.rs +++ b/templates/minimal/node/src/chain_spec.rs @@ -15,13 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use minimal_template_runtime::{BalancesConfig, SudoConfig, WASM_BINARY}; +use minimal_template_runtime::WASM_BINARY; use polkadot_sdk::{ sc_service::{ChainType, Properties}, - sp_keyring::AccountKeyring, *, }; -use serde_json::{json, Value}; /// This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec; @@ -33,26 +31,12 @@ fn props() -> Properties { properties } -pub fn development_config() -> Result { +pub fn development_chain_spec() -> Result { Ok(ChainSpec::builder(WASM_BINARY.expect("Development wasm not available"), Default::default()) .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) - .with_genesis_config_patch(testnet_genesis()) + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .with_properties(props()) .build()) } - -/// Configure initial storage state for FRAME pallets. -fn testnet_genesis() -> Value { - use minimal_template_runtime::interface::{Balance, MinimumBalance}; - use polkadot_sdk::polkadot_sdk_frame::traits::Get; - let endowment = >::get().max(1) * 1000; - let balances = AccountKeyring::iter() - .map(|a| (a.to_account_id(), endowment)) - .collect::>(); - json!({ - "balances": BalancesConfig { balances }, - "sudo": SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, - }) -} diff --git a/templates/minimal/node/src/command.rs b/templates/minimal/node/src/command.rs index b09ea1fab237..5cb0694d9828 100644 --- a/templates/minimal/node/src/command.rs +++ b/templates/minimal/node/src/command.rs @@ -49,7 +49,7 @@ impl SubstrateCli for Cli { fn load_spec(&self, id: &str) -> Result, String> { Ok(match id { - "dev" => Box::new(chain_spec::development_config()?), + "dev" => Box::new(chain_spec::development_chain_spec()?), path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), }) diff --git a/templates/minimal/runtime/Cargo.toml b/templates/minimal/runtime/Cargo.toml index 49ddf3987e96..74a09b9396e5 100644 --- a/templates/minimal/runtime/Cargo.toml +++ b/templates/minimal/runtime/Cargo.toml @@ -21,6 +21,7 @@ polkadot-sdk = { workspace = true, features = [ "pallet-transaction-payment-rpc-runtime-api", "runtime", ] } +serde_json = { workspace = true, default-features = false, features = ["alloc"] } # local pallet templates pallet-minimal-template = { workspace = true } @@ -37,4 +38,5 @@ std = [ "pallet-minimal-template/std", "polkadot-sdk/std", "scale-info/std", + "serde_json/std", ] diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index cce13c48af71..7379e33b6b3e 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -25,7 +25,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); extern crate alloc; -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_sdk::{ polkadot_sdk_frame::{ @@ -36,6 +36,54 @@ use polkadot_sdk::{ *, }; +/// Provides getters for genesis configuration presets. +pub mod genesis_config_presets { + use crate::{ + interface::{Balance, MinimumBalance}, + sp_genesis_builder::PresetId, + sp_keyring::AccountKeyring, + BalancesConfig, RuntimeGenesisConfig, SudoConfig, + }; + + use alloc::{vec, vec::Vec}; + use polkadot_sdk::{sp_core::Get, sp_genesis_builder}; + use serde_json::Value; + + /// Returns a development genesis config preset. + pub fn development_config_genesis() -> Value { + let endowment = >::get().max(1) * 1000; + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: AccountKeyring::iter() + .map(|a| (a.to_account_id(), endowment)) + .collect::>(), + }, + sudo: SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") + } + + /// Get the set of the available genesis config presets. + pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) + } + + /// List of supported presets. + pub fn preset_names() -> Vec { + vec![PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET)] + } +} + /// The runtime version. #[runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { @@ -272,11 +320,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, self::genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + self::genesis_config_presets::preset_names() } } } diff --git a/templates/parachain/node/src/chain_spec.rs b/templates/parachain/node/src/chain_spec.rs index 7cdd362dffb8..af82607bc8e4 100644 --- a/templates/parachain/node/src/chain_spec.rs +++ b/templates/parachain/node/src/chain_spec.rs @@ -24,7 +24,7 @@ impl Extensions { } } -pub fn development_config() -> ChainSpec { +pub fn development_chain_spec() -> ChainSpec { // Give your base currency a unit name and decimal places let mut properties = sc_chain_spec::Properties::new(); properties.insert("tokenSymbol".into(), "UNIT".into()); @@ -46,7 +46,7 @@ pub fn development_config() -> ChainSpec { .build() } -pub fn local_testnet_config() -> ChainSpec { +pub fn local_chain_spec() -> ChainSpec { // Give your base currency a unit name and decimal places let mut properties = sc_chain_spec::Properties::new(); properties.insert("tokenSymbol".into(), "UNIT".into()); diff --git a/templates/parachain/node/src/command.rs b/templates/parachain/node/src/command.rs index 610dbd7a686a..6b9deade1271 100644 --- a/templates/parachain/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -17,9 +17,9 @@ use crate::{ fn load_spec(id: &str) -> std::result::Result, String> { Ok(match id { - "dev" => Box::new(chain_spec::development_config()), - "template-rococo" => Box::new(chain_spec::local_testnet_config()), - "" | "local" => Box::new(chain_spec::local_testnet_config()), + "dev" => Box::new(chain_spec::development_chain_spec()), + "template-rococo" => Box::new(chain_spec::local_chain_spec()), + "" | "local" => Box::new(chain_spec::local_chain_spec()), path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), }) } diff --git a/templates/parachain/runtime/Cargo.toml b/templates/parachain/runtime/Cargo.toml index 45c77d18e816..236b7c048632 100644 --- a/templates/parachain/runtime/Cargo.toml +++ b/templates/parachain/runtime/Cargo.toml @@ -27,7 +27,7 @@ scale-info = { features = [ ], workspace = true } smallvec = { workspace = true, default-features = true } docify = { workspace = true } -serde_json = { workspace = true, default-features = false } +serde_json = { workspace = true, default-features = false, features = ["alloc"] } # Local pallet-parachain-template = { workspace = true } diff --git a/templates/parachain/runtime/src/configs/mod.rs b/templates/parachain/runtime/src/configs/mod.rs index 456187337121..43ab63bd0b67 100644 --- a/templates/parachain/runtime/src/configs/mod.rs +++ b/templates/parachain/runtime/src/configs/mod.rs @@ -158,6 +158,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf; + type DoneSlashHandler = (); } parameter_types! { diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml index 8f74c6b3cb55..a0285e048d1b 100644 --- a/templates/solochain/node/Cargo.toml +++ b/templates/solochain/node/Cargo.toml @@ -35,6 +35,7 @@ sp-consensus-aura = { workspace = true, default-features = true } sc-consensus = { workspace = true, default-features = true } sc-consensus-grandpa = { workspace = true, default-features = true } sp-consensus-grandpa = { workspace = true, default-features = true } +sp-genesis-builder = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } sc-basic-authorship = { workspace = true, default-features = true } @@ -66,9 +67,7 @@ substrate-build-script-utils = { workspace = true, default-features = true } [features] default = ["std"] -std = [ - "solochain-template-runtime/std", -] +std = ["solochain-template-runtime/std"] # Dependencies that are only required if runtime benchmarking should be build. runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", diff --git a/templates/solochain/node/src/chain_spec.rs b/templates/solochain/node/src/chain_spec.rs index 651025e68ded..086bf7accf3a 100644 --- a/templates/solochain/node/src/chain_spec.rs +++ b/templates/solochain/node/src/chain_spec.rs @@ -1,39 +1,10 @@ use sc_service::ChainType; -use solochain_template_runtime::{AccountId, Signature, WASM_BINARY}; -use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_consensus_grandpa::AuthorityId as GrandpaId; -use sp_core::{sr25519, Pair, Public}; -use sp_runtime::traits::{IdentifyAccount, Verify}; - -// The URL for the telemetry server. -// const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; +use solochain_template_runtime::WASM_BINARY; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec; -/// Generate a crypto pair from seed. -pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - -type AccountPublic = ::Signer; - -/// Generate an account ID from seed. -pub fn get_account_id_from_seed(seed: &str) -> AccountId -where - AccountPublic: From<::Public>, -{ - AccountPublic::from(get_from_seed::(seed)).into_account() -} - -/// Generate an Aura authority key. -pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { - (get_from_seed::(s), get_from_seed::(s)) -} - -pub fn development_config() -> Result { +pub fn development_chain_spec() -> Result { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, None, @@ -41,24 +12,11 @@ pub fn development_config() -> Result { .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) - .with_genesis_config_patch(testnet_genesis( - // Initial PoA authorities - vec![authority_keys_from_seed("Alice")], - // Sudo account - get_account_id_from_seed::("Alice"), - // Pre-funded accounts - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - true, - )) + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .build()) } -pub fn local_testnet_config() -> Result { +pub fn local_chain_spec() -> Result { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, None, @@ -66,52 +24,6 @@ pub fn local_testnet_config() -> Result { .with_name("Local Testnet") .with_id("local_testnet") .with_chain_type(ChainType::Local) - .with_genesis_config_patch(testnet_genesis( - // Initial PoA authorities - vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")], - // Sudo account - get_account_id_from_seed::("Alice"), - // Pre-funded accounts - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - true, - )) + .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) .build()) } - -/// Configure initial storage state for FRAME modules. -fn testnet_genesis( - initial_authorities: Vec<(AuraId, GrandpaId)>, - root_key: AccountId, - endowed_accounts: Vec, - _enable_println: bool, -) -> serde_json::Value { - serde_json::json!({ - "balances": { - // Configure endowed accounts with initial balance of 1 << 60. - "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), - }, - "aura": { - "authorities": initial_authorities.iter().map(|x| (x.0.clone())).collect::>(), - }, - "grandpa": { - "authorities": initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect::>(), - }, - "sudo": { - // Assign network admin rights. - "key": Some(root_key), - }, - }) -} diff --git a/templates/solochain/node/src/command.rs b/templates/solochain/node/src/command.rs index 624ace1bf350..e2c7657c95cc 100644 --- a/templates/solochain/node/src/command.rs +++ b/templates/solochain/node/src/command.rs @@ -37,8 +37,8 @@ impl SubstrateCli for Cli { fn load_spec(&self, id: &str) -> Result, String> { Ok(match id { - "dev" => Box::new(chain_spec::development_config()?), - "" | "local" => Box::new(chain_spec::local_testnet_config()?), + "dev" => Box::new(chain_spec::development_chain_spec()?), + "" | "local" => Box::new(chain_spec::local_chain_spec()?), path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), }) diff --git a/templates/solochain/runtime/Cargo.toml b/templates/solochain/runtime/Cargo.toml index 9a1f7145c2ca..8a7fa74a5977 100644 --- a/templates/solochain/runtime/Cargo.toml +++ b/templates/solochain/runtime/Cargo.toml @@ -20,6 +20,7 @@ scale-info = { features = [ "derive", "serde", ], workspace = true } +serde_json = { workspace = true, default-features = false, features = ["alloc"] } # frame frame-support = { features = ["experimental"], workspace = true } @@ -45,6 +46,7 @@ sp-consensus-aura = { features = [ sp-consensus-grandpa = { features = [ "serde", ], workspace = true } +sp-keyring = { workspace = true } sp-core = { features = [ "serde", ], workspace = true } @@ -114,6 +116,8 @@ std = [ "sp-transaction-pool/std", "sp-version/std", + "serde_json/std", + "sp-keyring/std", "substrate-wasm-builder", ] diff --git a/templates/solochain/runtime/src/apis.rs b/templates/solochain/runtime/src/apis.rs index 1e3dc452857c..87a09a5f7fee 100644 --- a/templates/solochain/runtime/src/apis.rs +++ b/templates/solochain/runtime/src/apis.rs @@ -24,7 +24,7 @@ // For more information, please refer to // External crates imports -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; use frame_support::{ genesis_builder_helper::{build_state, get_preset}, weights::Weight, @@ -285,11 +285,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, crate::genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + crate::genesis_config_presets::preset_names() } } } diff --git a/templates/solochain/runtime/src/configs/mod.rs b/templates/solochain/runtime/src/configs/mod.rs index a5c12fbd79ab..4f77ad37fe64 100644 --- a/templates/solochain/runtime/src/configs/mod.rs +++ b/templates/solochain/runtime/src/configs/mod.rs @@ -134,6 +134,7 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = VariantCountOf; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeHoldReason; + type DoneSlashHandler = (); } parameter_types! { diff --git a/templates/solochain/runtime/src/genesis_config_presets.rs b/templates/solochain/runtime/src/genesis_config_presets.rs new file mode 100644 index 000000000000..693ae5c2221f --- /dev/null +++ b/templates/solochain/runtime/src/genesis_config_presets.rs @@ -0,0 +1,112 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{AccountId, BalancesConfig, RuntimeGenesisConfig, SudoConfig}; +use alloc::{vec, vec::Vec}; +use serde_json::Value; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_consensus_grandpa::AuthorityId as GrandpaId; +use sp_genesis_builder::{self, PresetId}; +use sp_keyring::AccountKeyring; + +// Returns the genesis config presets populated with given parameters. +fn testnet_genesis( + initial_authorities: Vec<(AuraId, GrandpaId)>, + endowed_accounts: Vec, + root: AccountId, +) -> Value { + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1u128 << 60)) + .collect::>(), + }, + aura: pallet_aura::GenesisConfig { + authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect::>(), + }, + grandpa: pallet_grandpa::GenesisConfig { + authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect::>(), + ..Default::default() + }, + sudo: SudoConfig { key: Some(root) }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +/// Return the development genesis config. +pub fn development_config_genesis() -> Value { + testnet_genesis( + vec![( + sp_keyring::Sr25519Keyring::Alice.public().into(), + sp_keyring::Ed25519Keyring::Alice.public().into(), + )], + vec![ + AccountKeyring::Alice.to_account_id(), + AccountKeyring::Bob.to_account_id(), + AccountKeyring::AliceStash.to_account_id(), + AccountKeyring::BobStash.to_account_id(), + ], + sp_keyring::AccountKeyring::Alice.to_account_id(), + ) +} + +/// Return the local genesis config preset. +pub fn local_config_genesis() -> Value { + testnet_genesis( + vec![ + ( + sp_keyring::Sr25519Keyring::Alice.public().into(), + sp_keyring::Ed25519Keyring::Alice.public().into(), + ), + ( + sp_keyring::Sr25519Keyring::Bob.public().into(), + sp_keyring::Ed25519Keyring::Bob.public().into(), + ), + ], + AccountKeyring::iter() + .filter(|v| v != &AccountKeyring::One && v != &AccountKeyring::Two) + .map(|v| v.to_account_id()) + .collect::>(), + AccountKeyring::Alice.to_account_id(), + ) +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => local_config_genesis(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + ] +} diff --git a/templates/solochain/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs index 279518c8f47c..bc47f3aeac85 100644 --- a/templates/solochain/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -25,6 +25,8 @@ pub use pallet_timestamp::Call as TimestampCall; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; +pub mod genesis_config_presets; + /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades