diff --git a/.github/workflows/auto-add-parachain-issues.yml b/.github/workflows/auto-add-parachain-issues.yml new file mode 100644 index 0000000000000000000000000000000000000000..6b5222b6ff74147b063d913ec0dcdec299a6fcea --- /dev/null +++ b/.github/workflows/auto-add-parachain-issues.yml @@ -0,0 +1,30 @@ +# If there are new issues related to the async backing feature, +# add it to the parachain team's board and set a custom "meta" field. + +name: Add selected issues to Parachain team board +on: + issues: + types: + - labeled + +jobs: + add-parachain-issues: + if: github.event.label.name == 'T16-async_backing' + runs-on: ubuntu-latest + steps: + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@v2.1.0 + with: + app_id: ${{ secrets.PROJECT_APP_ID }} + private_key: ${{ secrets.PROJECT_APP_KEY }} + - name: Sync issues + uses: paritytech/github-issue-sync@v0.3.2 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PROJECT_TOKEN: ${{ steps.generate_token.outputs.token }} + project: 119 # Parachain team board + project_field: 'meta' + project_value: 'async backing' + labels: | + T16-async_backing diff --git a/.github/workflows/sync-templates.yml b/.github/workflows/sync-templates.yml new file mode 100644 index 0000000000000000000000000000000000000000..511c9d0e8cd06f7b4b7b16126d6565cae9047a00 --- /dev/null +++ b/.github/workflows/sync-templates.yml @@ -0,0 +1,159 @@ +name: Synchronize templates + + +# This job is used to keep the repository templates up-to-date. +# The code of the templates exist inside the monorepo, and upon releases we synchronize the repositories: +# - https://github.com/paritytech/polkadot-sdk-minimal-template +# - https://github.com/paritytech/polkadot-sdk-parachain-template +# - https://github.com/paritytech/polkadot-sdk-solochain-template +# +# The job moves the template code out of the monorepo, +# replaces any references to the monorepo workspace using psvm and toml-cli, +# checks that it builds successfully, +# and commits and pushes the result to each respective repository. +# If the build fails, a PR is created instead for manual inspection. + + +on: + # A manual dispatch for now - automatic on releases later. + workflow_dispatch: + inputs: + crate_release_version: + description: 'A release version to use, e.g. 1.9.0' + required: true + + +jobs: + sync-templates: + runs-on: ubuntu-latest + environment: master + strategy: + fail-fast: false + matrix: + template: ["minimal", "solochain", "parachain"] + env: + template-path: "polkadot-sdk-${{ matrix.template }}-template" + steps: + + # 1. Prerequisites. + + - name: Configure git identity + run: | + git config --global user.name "Template Bot" + git config --global user.email "163342540+paritytech-polkadotsdk-templatebot[bot]@users.noreply.github.com" + - uses: actions/checkout@v3 + with: + path: polkadot-sdk + ref: "release-crates-io-v${{ github.event.inputs.crate_release_version }}" + - name: Generate a token for the template repository + id: app_token + uses: actions/create-github-app-token@v1.9.3 + with: + owner: "paritytech" + repositories: "polkadot-sdk-${{ matrix.template }}-template" + app-id: ${{ secrets.TEMPLATE_APP_ID }} + private-key: ${{ secrets.TEMPLATE_APP_KEY }} + - uses: actions/checkout@v3 + with: + repository: "paritytech/polkadot-sdk-${{ matrix.template }}-template" + path: "${{ env.template-path }}" + token: ${{ steps.app_token.outputs.token }} + - name: Install toml-cli + run: cargo install --git https://github.com/gnprice/toml-cli --rev ea69e9d2ca4f0f858110dc7a5ae28bcb918c07fb # v0.2.3 + - name: Install Polkadot SDK Version Manager + run: cargo install --git https://github.com/paritytech/psvm --rev c41261ffb52ab0c115adbbdb17e2cb7900d2bdfd psvm # master + - name: Rust compilation prerequisites + run: | + sudo apt update + sudo apt install -y \ + protobuf-compiler + rustup target add wasm32-unknown-unknown + rustup component add rustfmt clippy rust-src + + # 2. Yanking the template out of the monorepo workspace. + + - name: Use psvm to replace git references with released creates. + run: find . -type f -name 'Cargo.toml' -exec psvm -o -v ${{ github.event.inputs.crate_release_version }} -p {} \; + working-directory: polkadot-sdk/templates/${{ matrix.template }}/ + - name: Create a new workspace Cargo.toml + run: | + cat << EOF > Cargo.toml + [workspace.package] + license = "MIT-0" + authors = ["Parity Technologies <admin@parity.io>"] + homepage = "https://substrate.io" + + [workspace] + members = [ + "node", + "pallets/template", + "runtime", + ] + resolver = "2" + EOF + shell: bash + working-directory: polkadot-sdk/templates/${{ matrix.template }}/ + - name: Update workspace configuration + run: | + set -euo pipefail + # toml-cli has no overwrite functionality yet, so we use temporary files. + # We cannot pipe the output straight to the same file while the CLI still reads and processes it. + + toml set templates/${{ matrix.template }}/Cargo.toml 'workspace.package.repository' "https://github.com/paritytech/polkadot-sdk-${{ matrix.template }}-template.git" > Cargo.temp + mv Cargo.temp ./templates/${{ matrix.template }}/Cargo.toml + + toml set templates/${{ matrix.template }}/Cargo.toml 'workspace.package.edition' "$(toml get --raw Cargo.toml 'workspace.package.edition')" > Cargo.temp + mv Cargo.temp ./templates/${{ matrix.template }}/Cargo.toml + + toml get Cargo.toml 'workspace.lints' --output-toml >> ./templates/${{ matrix.template }}/Cargo.toml + + toml get Cargo.toml 'workspace.dependencies' --output-toml >> ./templates/${{ matrix.template }}/Cargo.toml + working-directory: polkadot-sdk + - name: Print the result Cargo.tomls for debugging + if: runner.debug == '1' + run: find . -type f -name 'Cargo.toml' -exec cat {} \; + working-directory: polkadot-sdk/templates/${{ matrix.template }}/ + + - name: Clean the destination repository + run: rm -rf ./* + working-directory: "${{ env.template-path }}" + - name: Copy over the new changes + run: | + cp -r polkadot-sdk/templates/${{ matrix.template }}/* "${{ env.template-path }}/" + + # 3. Verify the build. Push the changes or create a PR. + + # We've run into out-of-disk error when compiling in the next step, so we free up some space this way. + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # 1.3.1 + with: + android: true # This alone is a 12 GB save. + # We disable the rest because it caused some problems. (they're enabled by default) + # The Android removal is enough. + dotnet: false + haskell: false + large-packages: false + swap-storage: false + + - name: Check if it compiles + id: check-compilation + run: cargo check && cargo test + working-directory: "${{ env.template-path }}" + timeout-minutes: 90 + - name: Create PR on failure + if: failure() && steps.check-compilation.outcome == 'failure' + uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 # v5 + with: + path: "${{ env.template-path }}" + token: ${{ steps.app_token.outputs.token }} + add-paths: | + ./* + title: "[Don't merge] Update the ${{ matrix.template }} template" + body: "The template has NOT been successfully built and needs to be inspected." + branch: "update-template/${{ github.event_name }}" + - name: Push changes + run: | + git add -A . + git commit --allow-empty -m "Update template triggered by ${{ github.event_name }}" + git push + working-directory: "${{ env.template-path }}" diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index ba05c709a27b163386e36f680812e0cf24f10277..6b72075c513b73c075d1dc10c90d0461bf0e1a82 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -161,6 +161,8 @@ zombienet-polkadot-functional-0011-async-backing-6-seconds-rate: zombienet-polkadot-elastic-scaling-0001-basic-3cores-6s-blocks: extends: - .zombienet-polkadot-common + variables: + FORCED_INFRA_INSTANCE: "spot-iops" script: - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh --local-dir="${LOCAL_DIR}/elastic_scaling" diff --git a/Cargo.lock b/Cargo.lock index 8718e62aacb5a8fbae0f214e1c69f8b342c384cf..e9022ed1d8d9994eb60b224cdf1b81a17d82fad9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -809,6 +809,7 @@ dependencies = [ "parachains-common", "rococo-emulated-chain", "sp-core", + "staging-xcm", "testnet-parachains-constants", ] @@ -928,6 +929,7 @@ dependencies = [ "frame-support", "parachains-common", "sp-core", + "staging-xcm", "testnet-parachains-constants", "westend-emulated-chain", ] @@ -15793,6 +15795,7 @@ dependencies = [ "pallet-multisig", "pallet-nis", "pallet-offences", + "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", diff --git a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs new file mode 100644 index 0000000000000000000000000000000000000000..4b0c052df8008410cb531c21d173ead2c4fdd450 --- /dev/null +++ b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs @@ -0,0 +1,205 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. + +//! Transaction extension that rejects bridge-related transactions, that include +//! obsolete (duplicated) data or do not pass some additional pallet-specific +//! checks. + +use crate::messages_call_ext::MessagesCallSubType; +use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType; +use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype; +use sp_runtime::transaction_validity::TransactionValidity; + +/// A duplication of the `FilterCall` trait. +/// +/// We need this trait in order to be able to implement it for the messages pallet, +/// since the implementation is done outside of the pallet crate. +pub trait BridgeRuntimeFilterCall<Call> { + /// Checks if a runtime call is valid. + fn validate(call: &Call) -> TransactionValidity; +} + +impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> for pallet_bridge_grandpa::Pallet<T, I> +where + T: pallet_bridge_grandpa::Config<I>, + T::RuntimeCall: GrandpaCallSubType<T, I>, +{ + fn validate(call: &T::RuntimeCall) -> TransactionValidity { + GrandpaCallSubType::<T, I>::check_obsolete_submit_finality_proof(call) + } +} + +impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> + for pallet_bridge_parachains::Pallet<T, I> +where + T: pallet_bridge_parachains::Config<I>, + T::RuntimeCall: ParachainsCallSubtype<T, I>, +{ + fn validate(call: &T::RuntimeCall) -> TransactionValidity { + ParachainsCallSubtype::<T, I>::check_obsolete_submit_parachain_heads(call) + } +} + +impl<T: pallet_bridge_messages::Config<I>, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> + for pallet_bridge_messages::Pallet<T, I> +where + T::RuntimeCall: MessagesCallSubType<T, I>, +{ + /// Validate messages in order to avoid "mining" messages delivery and delivery confirmation + /// transactions, that are delivering outdated messages/confirmations. Without this validation, + /// even honest relayers may lose their funds if there are multiple relays running and + /// submitting the same messages/confirmations. + fn validate(call: &T::RuntimeCall) -> TransactionValidity { + call.check_obsolete_call() + } +} + +/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension. +/// +/// ## Example +/// +/// ```nocompile +/// generate_bridge_reject_obsolete_headers_and_messages!{ +/// Call, AccountId +/// BridgeRococoGrandpa, BridgeRococoMessages, +/// BridgeRococoParachains +/// } +/// ``` +/// +/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged +/// headers and messages. Without that extension, even honest relayers may lose their funds if +/// there are multiple relays running and submitting the same information. +#[macro_export] +macro_rules! generate_bridge_reject_obsolete_headers_and_messages { + ($call:ty, $account_id:ty, $($filter_call:ty),*) => { + #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, sp_runtime::RuntimeDebug, scale_info::TypeInfo)] + pub struct BridgeRejectObsoleteHeadersAndMessages; + impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { + const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; + type AccountId = $account_id; + type Call = $call; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> sp_std::result::Result< + (), + sp_runtime::transaction_validity::TransactionValidityError, + > { + Ok(()) + } + + fn validate( + &self, + _who: &Self::AccountId, + call: &Self::Call, + _info: &sp_runtime::traits::DispatchInfoOf<Self::Call>, + _len: usize, + ) -> sp_runtime::transaction_validity::TransactionValidity { + let valid = sp_runtime::transaction_validity::ValidTransaction::default(); + $( + let valid = valid + .combine_with(<$filter_call as $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall<$call>>::validate(call)?); + )* + Ok(valid) + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + info: &sp_runtime::traits::DispatchInfoOf<Self::Call>, + len: usize, + ) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> { + self.validate(who, call, info, len).map(drop) + } + } + }; +} + +#[cfg(test)] +mod tests { + use super::*; + use frame_support::{assert_err, assert_ok}; + use sp_runtime::{ + traits::SignedExtension, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, + }; + + pub struct MockCall { + data: u32, + } + + impl sp_runtime::traits::Dispatchable for MockCall { + type RuntimeOrigin = (); + type Config = (); + type Info = (); + type PostInfo = (); + + fn dispatch( + self, + _origin: Self::RuntimeOrigin, + ) -> sp_runtime::DispatchResultWithInfo<Self::PostInfo> { + unimplemented!() + } + } + + struct FirstFilterCall; + impl BridgeRuntimeFilterCall<MockCall> for FirstFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 1 { + return InvalidTransaction::Custom(1).into() + } + + Ok(ValidTransaction { priority: 1, ..Default::default() }) + } + } + + struct SecondFilterCall; + impl BridgeRuntimeFilterCall<MockCall> for SecondFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 2 { + return InvalidTransaction::Custom(2).into() + } + + Ok(ValidTransaction { priority: 2, ..Default::default() }) + } + } + + #[test] + fn test() { + generate_bridge_reject_obsolete_headers_and_messages!( + MockCall, + (), + FirstFilterCall, + SecondFilterCall + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0), + InvalidTransaction::Custom(1) + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0), + InvalidTransaction::Custom(2) + ); + + assert_ok!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0), + ValidTransaction { priority: 3, ..Default::default() } + ) + } +} diff --git a/bridges/bin/runtime-common/src/extensions/mod.rs b/bridges/bin/runtime-common/src/extensions/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..3f1b506aaae3ef66fe6f44379258356a2074464c --- /dev/null +++ b/bridges/bin/runtime-common/src/extensions/mod.rs @@ -0,0 +1,21 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. + +//! Bridge-specific transaction extensions. + +pub mod check_obsolete_extension; +pub mod priority_calculator; +pub mod refund_relayer_extension; diff --git a/bridges/bin/runtime-common/src/priority_calculator.rs b/bridges/bin/runtime-common/src/extensions/priority_calculator.rs similarity index 100% rename from bridges/bin/runtime-common/src/priority_calculator.rs rename to bridges/bin/runtime-common/src/extensions/priority_calculator.rs diff --git a/bridges/bin/runtime-common/src/refund_relayer_extension.rs b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs similarity index 99% rename from bridges/bin/runtime-common/src/refund_relayer_extension.rs rename to bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs index 455392a0a277f3520cd7f58150f12e7420d36014..f97b23ecaaa99fdaed0a54d00b2b89c14847750a 100644 --- a/bridges/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs @@ -520,8 +520,9 @@ where } // compute priority boost - let priority_boost = - crate::priority_calculator::compute_priority_boost::<T::Priority>(bundled_messages); + let priority_boost = crate::extensions::priority_calculator::compute_priority_boost::< + T::Priority, + >(bundled_messages); let valid_transaction = ValidTransactionBuilder::default().priority(priority_boost); log::trace!( diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs index 2722f6f1c6d14f09ab215f8f020f2c449eda4d4b..5679acd6006ccb8540f940f0f90363f902d643f7 100644 --- a/bridges/bin/runtime-common/src/lib.rs +++ b/bridges/bin/runtime-common/src/lib.rs @@ -19,11 +19,7 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -use crate::messages_call_ext::MessagesCallSubType; -use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType; -use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype; -use sp_runtime::transaction_validity::TransactionValidity; - +pub mod extensions; pub mod messages; pub mod messages_api; pub mod messages_benchmarking; @@ -31,8 +27,6 @@ pub mod messages_call_ext; pub mod messages_generation; pub mod messages_xcm_extension; pub mod parachains_benchmarking; -pub mod priority_calculator; -pub mod refund_relayer_extension; mod mock; @@ -40,184 +34,3 @@ mod mock; pub mod integrity; const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch"; - -/// A duplication of the `FilterCall` trait. -/// -/// We need this trait in order to be able to implement it for the messages pallet, -/// since the implementation is done outside of the pallet crate. -pub trait BridgeRuntimeFilterCall<Call> { - /// Checks if a runtime call is valid. - fn validate(call: &Call) -> TransactionValidity; -} - -impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> for pallet_bridge_grandpa::Pallet<T, I> -where - T: pallet_bridge_grandpa::Config<I>, - T::RuntimeCall: GrandpaCallSubType<T, I>, -{ - fn validate(call: &T::RuntimeCall) -> TransactionValidity { - GrandpaCallSubType::<T, I>::check_obsolete_submit_finality_proof(call) - } -} - -impl<T, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> - for pallet_bridge_parachains::Pallet<T, I> -where - T: pallet_bridge_parachains::Config<I>, - T::RuntimeCall: ParachainsCallSubtype<T, I>, -{ - fn validate(call: &T::RuntimeCall) -> TransactionValidity { - ParachainsCallSubtype::<T, I>::check_obsolete_submit_parachain_heads(call) - } -} - -impl<T: pallet_bridge_messages::Config<I>, I: 'static> BridgeRuntimeFilterCall<T::RuntimeCall> - for pallet_bridge_messages::Pallet<T, I> -where - T::RuntimeCall: MessagesCallSubType<T, I>, -{ - /// Validate messages in order to avoid "mining" messages delivery and delivery confirmation - /// transactions, that are delivering outdated messages/confirmations. Without this validation, - /// even honest relayers may lose their funds if there are multiple relays running and - /// submitting the same messages/confirmations. - fn validate(call: &T::RuntimeCall) -> TransactionValidity { - call.check_obsolete_call() - } -} - -/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension. -/// -/// ## Example -/// -/// ```nocompile -/// generate_bridge_reject_obsolete_headers_and_messages!{ -/// Call, AccountId -/// BridgeRococoGrandpa, BridgeRococoMessages, -/// BridgeRococoParachains -/// } -/// ``` -/// -/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged -/// headers and messages. Without that extension, even honest relayers may lose their funds if -/// there are multiple relays running and submitting the same information. -#[macro_export] -macro_rules! generate_bridge_reject_obsolete_headers_and_messages { - ($call:ty, $account_id:ty, $($filter_call:ty),*) => { - #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, sp_runtime::RuntimeDebug, scale_info::TypeInfo)] - pub struct BridgeRejectObsoleteHeadersAndMessages; - impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { - const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; - type AccountId = $account_id; - type Call = $call; - type AdditionalSigned = (); - type Pre = (); - - fn additional_signed(&self) -> sp_std::result::Result< - (), - sp_runtime::transaction_validity::TransactionValidityError, - > { - Ok(()) - } - - fn validate( - &self, - _who: &Self::AccountId, - call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf<Self::Call>, - _len: usize, - ) -> sp_runtime::transaction_validity::TransactionValidity { - let valid = sp_runtime::transaction_validity::ValidTransaction::default(); - $( - let valid = valid - .combine_with(<$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?); - )* - Ok(valid) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &sp_runtime::traits::DispatchInfoOf<Self::Call>, - len: usize, - ) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> { - self.validate(who, call, info, len).map(drop) - } - } - }; -} - -#[cfg(test)] -mod tests { - use crate::BridgeRuntimeFilterCall; - use frame_support::{assert_err, assert_ok}; - use sp_runtime::{ - traits::SignedExtension, - transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, - }; - - pub struct MockCall { - data: u32, - } - - impl sp_runtime::traits::Dispatchable for MockCall { - type RuntimeOrigin = (); - type Config = (); - type Info = (); - type PostInfo = (); - - fn dispatch( - self, - _origin: Self::RuntimeOrigin, - ) -> sp_runtime::DispatchResultWithInfo<Self::PostInfo> { - unimplemented!() - } - } - - struct FirstFilterCall; - impl BridgeRuntimeFilterCall<MockCall> for FirstFilterCall { - fn validate(call: &MockCall) -> TransactionValidity { - if call.data <= 1 { - return InvalidTransaction::Custom(1).into() - } - - Ok(ValidTransaction { priority: 1, ..Default::default() }) - } - } - - struct SecondFilterCall; - impl BridgeRuntimeFilterCall<MockCall> for SecondFilterCall { - fn validate(call: &MockCall) -> TransactionValidity { - if call.data <= 2 { - return InvalidTransaction::Custom(2).into() - } - - Ok(ValidTransaction { priority: 2, ..Default::default() }) - } - } - - #[test] - fn test() { - generate_bridge_reject_obsolete_headers_and_messages!( - MockCall, - (), - FirstFilterCall, - SecondFilterCall - ); - - assert_err!( - BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0), - InvalidTransaction::Custom(1) - ); - - assert_err!( - BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0), - InvalidTransaction::Custom(2) - ); - - assert_ok!( - BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0), - ValidTransaction { priority: 3, ..Default::default() } - ) - } -} diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index 5d0be41b1b5588e3ddc8c6306c9bf83ec29d6056..c6008802ae9a50c02ec54d981e5bd503ff659816 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -191,6 +191,10 @@ pub mod pallet { impl<T: Config<I>, I: 'static> Pallet<T, I> { /// Called when new message is sent (queued to local outbound XCM queue) over the bridge. pub(crate) fn on_message_sent_to_bridge(message_size: u32) { + log::trace!( + target: LOG_TARGET, + "on_message_sent_to_bridge - message_size: {message_size:?}", + ); let _ = Bridge::<T, I>::try_mutate(|bridge| { let is_channel_with_bridge_hub_congested = T::WithBridgeHubChannel::is_congested(); let is_bridge_congested = bridge.is_congested; @@ -238,14 +242,16 @@ impl<T: Config<I>, I: 'static> ExporterFor for Pallet<T, I> { remote_location: &InteriorLocation, message: &Xcm<()>, ) -> Option<(Location, Option<Asset>)> { + log::trace!( + target: LOG_TARGET, + "exporter_for - network: {network:?}, remote_location: {remote_location:?}, msg: {message:?}", + ); // ensure that the message is sent to the expected bridged network (if specified). if let Some(bridged_network) = T::BridgedNetworkId::get() { if *network != bridged_network { log::trace!( target: LOG_TARGET, - "Router with bridged_network_id {:?} does not support bridging to network {:?}!", - bridged_network, - network, + "Router with bridged_network_id {bridged_network:?} does not support bridging to network {network:?}!", ); return None } @@ -300,7 +306,7 @@ impl<T: Config<I>, I: 'static> ExporterFor for Pallet<T, I> { log::info!( target: LOG_TARGET, - "Going to send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", + "Validate send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", (network, remote_location), message_size, fee, @@ -321,6 +327,7 @@ impl<T: Config<I>, I: 'static> SendXcm for Pallet<T, I> { dest: &mut Option<Location>, xcm: &mut Option<Xcm<()>>, ) -> SendResult<Self::Ticket> { + log::trace!(target: LOG_TARGET, "validate - msg: {xcm:?}, destination: {dest:?}"); // `dest` and `xcm` are required here let dest_ref = dest.as_ref().ok_or(SendError::MissingArgument)?; let xcm_ref = xcm.as_ref().ok_or(SendError::MissingArgument)?; @@ -366,6 +373,7 @@ impl<T: Config<I>, I: 'static> SendXcm for Pallet<T, I> { // increase delivery fee factor if required Self::on_message_sent_to_bridge(message_size); + log::trace!(target: LOG_TARGET, "deliver - message sent, xcm_hash: {xcm_hash:?}"); Ok(xcm_hash) } } diff --git a/bridges/primitives/relayers/src/registration.rs b/bridges/primitives/relayers/src/registration.rs index bc2d0d127aefec3c6982b17915202cc0f87984f2..38fa7c2d9075b7e84986eb4ff173cdb24db5610f 100644 --- a/bridges/primitives/relayers/src/registration.rs +++ b/bridges/primitives/relayers/src/registration.rs @@ -21,7 +21,7 @@ //! required finality proofs). This extension boosts priority of message delivery //! transactions, based on the number of bundled messages. So transaction with more //! messages has larger priority than the transaction with less messages. -//! See `bridge_runtime_common::priority_calculator` for details; +//! See `bridge_runtime_common::extensions::priority_calculator` for details; //! //! This encourages relayers to include more messages to their delivery transactions. //! At the same time, we are not verifying storage proofs before boosting diff --git a/bridges/relays/messages/src/message_race_delivery.rs b/bridges/relays/messages/src/message_race_delivery.rs index 137deb5b74f757aa111d5652cbb251a94979e166..f18c43cc7f0e084100d8096432dccafbd61301be 100644 --- a/bridges/relays/messages/src/message_race_delivery.rs +++ b/bridges/relays/messages/src/message_race_delivery.rs @@ -446,7 +446,7 @@ where )) } - /// Returns lastest confirmed message at source chain, given source block. + /// Returns latest confirmed message at source chain, given source block. fn latest_confirmed_nonce_at_source(&self, at: &SourceHeaderIdOf<P>) -> Option<MessageNonce> { self.latest_confirmed_nonces_at_source .iter() diff --git a/bridges/relays/messages/src/message_race_strategy.rs b/bridges/relays/messages/src/message_race_strategy.rs index 93d178e55b04f64a9631f04b4e93b67594d67e54..3a532331d79dc83f680ca7b5e21e471e60335b84 100644 --- a/bridges/relays/messages/src/message_race_strategy.rs +++ b/bridges/relays/messages/src/message_race_strategy.rs @@ -567,7 +567,7 @@ mod tests { let source_header_1 = header_id(1); let target_header_1 = header_id(1); - // we start in perfec sync state - all headers are synced and finalized on both ends + // we start in perfect sync state - all headers are synced and finalized on both ends let mut state = TestRaceStateImpl { best_finalized_source_header_id_at_source: Some(source_header_1), best_finalized_source_header_id_at_best_target: Some(source_header_1), diff --git a/bridges/relays/parachains/README.md b/bridges/relays/parachains/README.md index 9043b0b0a9cdd84c984d62b4e7c3adc6df44d6bc..f24e7a4c5d3040750361c31d7196e684f46edd13 100644 --- a/bridges/relays/parachains/README.md +++ b/bridges/relays/parachains/README.md @@ -23,7 +23,7 @@ to return the best known head of given parachain. When required, it must be able finality delivery transaction to the target node. The main entrypoint for the crate is the [`run` function](./src/parachains_loop.rs), which takes source and target -clients and [`ParachainSyncParams`](./src/parachains_loop.rs) parameters. The most imporant parameter is the +clients and [`ParachainSyncParams`](./src/parachains_loop.rs) parameters. The most important parameter is the `parachains` - it is the set of parachains, which relay tracks and updates. The other important parameter that may affect the relay operational costs is the `strategy`. If it is set to `Any`, then the finality delivery transaction is submitted if at least one of tracked parachain heads is updated. The other option is `All`. Then diff --git a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh index 66c9ddc037b8efb005d2239b174eb5710dddaf53..41aa862be5764ea93fbe09fa706621486131d4c6 100755 --- a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh +++ b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh @@ -212,19 +212,19 @@ case "$1" in "ws://127.0.0.1:8943" \ "//Alice" \ "$ASSET_HUB_ROCOCO_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_ROCOCO" \ - $((1000000000000 + 50000000000 * 20)) + 100000000000000 # drip SA of lane dedicated to asset hub for paying rewards for delivery transfer_balance \ "ws://127.0.0.1:8943" \ "//Alice" \ "$ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain" \ - $((1000000000000 + 2000000000000)) + 100000000000000 # drip SA of lane dedicated to asset hub for paying rewards for delivery confirmation transfer_balance \ "ws://127.0.0.1:8943" \ "//Alice" \ "$ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain" \ - $((1000000000000 + 2000000000000)) + 100000000000000 # set XCM version of remote BridgeHubWestend force_xcm_version \ "ws://127.0.0.1:9942" \ @@ -270,19 +270,19 @@ case "$1" in "ws://127.0.0.1:8945" \ "//Alice" \ "$ASSET_HUB_WESTEND_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_WESTEND" \ - $((1000000000000000 + 50000000000 * 20)) + 100000000000000 # drip SA of lane dedicated to asset hub for paying rewards for delivery transfer_balance \ "ws://127.0.0.1:8945" \ "//Alice" \ "$ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain" \ - $((1000000000000000 + 2000000000000)) + 100000000000000 # drip SA of lane dedicated to asset hub for paying rewards for delivery confirmation transfer_balance \ "ws://127.0.0.1:8945" \ "//Alice" \ "$ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain" \ - $((1000000000000000 + 2000000000000)) + 100000000000000 # set XCM version of remote BridgeHubRococo force_xcm_version \ "ws://127.0.0.1:9945" \ diff --git a/bridges/testing/framework/utils/bridges.sh b/bridges/testing/framework/utils/bridges.sh index 7c8399461584a85e4e8eedf5f347d9d74725f1c9..07d9e4cd50b1651961724ac2d4c2badca2030e71 100755 --- a/bridges/testing/framework/utils/bridges.sh +++ b/bridges/testing/framework/utils/bridges.sh @@ -53,7 +53,7 @@ function call_polkadot_js_api() { # With it, it just submits it to the tx pool and exits. # --nonce -1: means to compute transaction nonce using `system_accountNextIndex` RPC, which includes all # transaction that are in the tx pool. - polkadot-js-api --noWait --nonce -1 "$@" + polkadot-js-api --nonce -1 "$@" || true } function generate_hex_encoded_call_data() { diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh index 3a604b3876d96241903c1c5a110cc6392f26cb7e..32419dc84f59e14d779eef30b26939b297240e55 100755 --- a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh +++ b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh @@ -24,12 +24,6 @@ echo -e "Sleeping 90s before starting relayer ...\n" sleep 90 ${BASH_SOURCE%/*}/../../environments/rococo-westend/start_relayer.sh $rococo_dir $westend_dir relayer_pid -# Sometimes the relayer syncs multiple parachain heads in the beginning leading to test failures. -# See issue: https://github.com/paritytech/parity-bridges-common/issues/2838. -# TODO: Remove this sleep after the issue is fixed. -echo -e "Sleeping 180s before runing the tests ...\n" -sleep 180 - run_zndsl ${BASH_SOURCE%/*}/rococo-to-westend.zndsl $westend_dir run_zndsl ${BASH_SOURCE%/*}/westend-to-rococo.zndsl $rococo_dir diff --git a/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs b/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs index aa5e67e453f69bd02920888a19d73b94e92addce..06f19941165a26374555064efcd797e1b9ebde34 100644 --- a/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs +++ b/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs @@ -451,6 +451,17 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { ) -> Result<BTreeMap<CoreIndex, VecDeque<cumulus_primitives_core::ParaId>>, ApiError> { Ok(self.rpc_client.parachain_host_claim_queue(at).await?) } + + async fn candidates_pending_availability( + &self, + at: Hash, + para_id: cumulus_primitives_core::ParaId, + ) -> Result<Vec<polkadot_primitives::CommittedCandidateReceipt<Hash>>, sp_api::ApiError> { + Ok(self + .rpc_client + .parachain_host_candidates_pending_availability(at, para_id) + .await?) + } } #[async_trait::async_trait] diff --git a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs index 547803865c28a28a7534b3a5d7e0075a2332c71b..864ce6c57125ae5c8dcb5419561dd61a43c38579 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs @@ -655,6 +655,20 @@ impl RelayChainRpcClient { .await } + /// Get the receipt of all candidates pending availability. + pub async fn parachain_host_candidates_pending_availability( + &self, + at: RelayHash, + para_id: ParaId, + ) -> Result<Vec<CommittedCandidateReceipt>, RelayChainError> { + self.call_remote_runtime_function( + "ParachainHost_candidates_pending_availability", + at, + Some(para_id), + ) + .await + } + pub async fn validation_code_hash( &self, at: RelayHash, diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index b4cd925d540ead4ef17af337f59192c4cfec0042..deced13a9e814ff2a4f05a9a02fe68d169b5a6be 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -942,7 +942,10 @@ impl<T: Config> SendXcm for Pallet<T> { Self::deposit_event(Event::XcmpMessageSent { message_hash: hash }); Ok(hash) }, - Err(e) => Err(SendError::Transport(e.into())), + Err(e) => { + log::error!(target: LOG_TARGET, "Deliver error: {e:?}"); + Err(SendError::Transport(e.into())) + }, } } } diff --git a/cumulus/parachains/chain-specs/coretime-westend.json b/cumulus/parachains/chain-specs/coretime-westend.json index 377870f9e2b3dd8f9303936cc54668a8ed830bf4..8f096fa6a9629dda3891efd00349467baff41bbf 100644 --- a/cumulus/parachains/chain-specs/coretime-westend.json +++ b/cumulus/parachains/chain-specs/coretime-westend.json @@ -7,7 +7,9 @@ "/dns/westend-coretime-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWMh2imeAzsZKGQgm2cv6Uoep3GBYtwGfujt1bs5YfVzkH", "/dns/boot.metaspan.io/tcp/33019/p2p/12D3KooWCa1uNnEZqiqJY9jkKNQxwSLGPeZ5MjWHhjQMGwga9JMM", "/dns/boot-node.helikon.io/tcp/9420/p2p/12D3KooWFBPartM873MNm1AmVK3etUz34cAE9A9rwPztPno2epQ3", - "/dns/boot-node.helikon.io/tcp/9422/wss/p2p/12D3KooWFBPartM873MNm1AmVK3etUz34cAE9A9rwPztPno2epQ3" + "/dns/boot-node.helikon.io/tcp/9422/wss/p2p/12D3KooWFBPartM873MNm1AmVK3etUz34cAE9A9rwPztPno2epQ3", + "/dns/coretime-westend-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWHewSFwJueRprNZNfkncdjud9DrGzvP1qfmgPd7VK66gw", + "/dns/coretime-westend-boot-ng.dwellir.com/tcp/30356/p2p/12D3KooWHewSFwJueRprNZNfkncdjud9DrGzvP1qfmgPd7VK66gw" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/people-westend.json b/cumulus/parachains/chain-specs/people-westend.json index d6f0e15e0248e967151767c64771c062d5a89741..6dd8579cf257049edc1159f6b06d5d670284f7a9 100644 --- a/cumulus/parachains/chain-specs/people-westend.json +++ b/cumulus/parachains/chain-specs/people-westend.json @@ -24,7 +24,7 @@ "/dns/people-westend-bootnode.turboflakes.io/tcp/30650/p2p/12D3KooWQEhmZg3uMkuxVUx3jbsD84zEX4dUKtvHfmCoBWMhybKW", "/dns/people-westend-bootnode.turboflakes.io/tcp/30750/wss/p2p/12D3KooWQEhmZg3uMkuxVUx3jbsD84zEX4dUKtvHfmCoBWMhybKW", "/dns/people-westend-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWBdCpCabhgBpLn67LWcXE2JJCCTMhuJHrfDNiTiCCr3KX", - "/dns/people-westend-boot-ng.dwellir.com/tcp/30355/p2p/12D3KooWBdCpCabhgBpLn67LWcXE2JJCCTMhuJHrfDNiTiCCr3KX" + "/dns/people-westend-boot-ng.dwellir.com/tcp/30355/p2p/12D3KooWBdCpCabhgBpLn67LWcXE2JJCCTMhuJHrfDNiTiCCr3KX" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml index 98762beb0cb23132c3880515287328bb09bde032..8100e681348836fb28c9236b9ba20d27f117d71b 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml @@ -23,3 +23,6 @@ emulated-integration-tests-common = { path = "../../../../common", default-featu asset-hub-rococo-runtime = { path = "../../../../../../runtimes/assets/asset-hub-rococo" } rococo-emulated-chain = { path = "../../../relays/rococo" } testnet-parachains-constants = { path = "../../../../../../runtimes/constants", features = ["rococo"] } + +# Polkadot +xcm = { package = "staging-xcm", path = "../../../../../../../../polkadot/xcm", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs index f1e972e869dc94465aa28356a7eaa1c4cd4503ef..202d02b250bb2e90261a01c13c6aab59c674b511 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs @@ -22,7 +22,8 @@ use frame_support::traits::OnInitialize; use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, impl_assets_helpers_for_parachain, impl_assets_helpers_for_system_parachain, - impl_xcm_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, + impl_foreign_assets_helpers_for_parachain, impl_xcm_helpers_for_parachain, impls::Parachain, + xcm_emulator::decl_test_parachains, }; use rococo_emulated_chain::Rococo; @@ -56,4 +57,5 @@ impl_accounts_helpers_for_parachain!(AssetHubRococo); impl_assert_events_helpers_for_parachain!(AssetHubRococo); impl_assets_helpers_for_system_parachain!(AssetHubRococo, Rococo); impl_assets_helpers_for_parachain!(AssetHubRococo); +impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, xcm::v3::Location); impl_xcm_helpers_for_parachain!(AssetHubRococo); diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml index a42a9abf618d403852561d5d4b20e7fb6ad576e7..e0abaa66c5cabba445b91c19436f9a4ce3642386 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml @@ -23,3 +23,6 @@ emulated-integration-tests-common = { path = "../../../../common", default-featu asset-hub-westend-runtime = { path = "../../../../../../runtimes/assets/asset-hub-westend" } westend-emulated-chain = { path = "../../../relays/westend" } testnet-parachains-constants = { path = "../../../../../../runtimes/constants", features = ["westend"] } + +# Polkadot +xcm = { package = "staging-xcm", path = "../../../../../../../../polkadot/xcm", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs index 7f05eefb4c208dab2192bc111347ffa4f2760fc0..6043a6aeda48f1e1ec010ac42e98a50feaae3a30 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs @@ -22,7 +22,8 @@ use frame_support::traits::OnInitialize; use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, impl_assets_helpers_for_parachain, impl_assets_helpers_for_system_parachain, - impl_xcm_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, + impl_foreign_assets_helpers_for_parachain, impl_xcm_helpers_for_parachain, impls::Parachain, + xcm_emulator::decl_test_parachains, }; use westend_emulated_chain::Westend; @@ -56,4 +57,5 @@ impl_accounts_helpers_for_parachain!(AssetHubWestend); impl_assert_events_helpers_for_parachain!(AssetHubWestend); impl_assets_helpers_for_system_parachain!(AssetHubWestend, Westend); impl_assets_helpers_for_parachain!(AssetHubWestend); +impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, xcm::v3::Location); impl_xcm_helpers_for_parachain!(AssetHubWestend); diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs index d81ab8143ddba678617aaa67db122298ce29606c..450439f5ea3080b66c5c572dfdae972c23c52a4b 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs @@ -17,8 +17,6 @@ use frame_support::parameter_types; use sp_core::{sr25519, storage::Storage}; -// Polkadot -use xcm::v3::Location; // Cumulus use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, SAFE_XCM_VERSION, @@ -79,20 +77,9 @@ pub fn genesis(para_id: u32) -> Storage { foreign_assets: penpal_runtime::ForeignAssetsConfig { assets: vec![ // Relay Native asset representation - ( - Location::try_from(RelayLocation::get()).expect("conversion works"), - PenpalAssetOwner::get(), - true, - ED, - ), + (RelayLocation::get(), PenpalAssetOwner::get(), true, ED), // Sufficient AssetHub asset representation - ( - Location::try_from(LocalReservableFromAssetHub::get()) - .expect("conversion works"), - PenpalAssetOwner::get(), - true, - ED, - ), + (LocalReservableFromAssetHub::get(), PenpalAssetOwner::get(), true, ED), ], ..Default::default() }, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs index 0b49c7a3e091a615632728b6ba74ca4dffefae66..c268b014bfa34e1b8c0a450ae2e446bb6f636c9d 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs @@ -16,16 +16,20 @@ mod genesis; pub use genesis::{genesis, PenpalAssetOwner, PenpalSudoAccount, ED, PARA_ID_A, PARA_ID_B}; pub use penpal_runtime::xcm_config::{ - CustomizableAssetFromSystemAssetHub, LocalTeleportableToAssetHub, XcmConfig, + CustomizableAssetFromSystemAssetHub, RelayNetworkId as PenpalRelayNetworkId, }; // Substrate use frame_support::traits::OnInitialize; +use sp_core::Encode; // Cumulus use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, - impl_assets_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, + impl_assets_helpers_for_parachain, impl_foreign_assets_helpers_for_parachain, + impl_xcm_helpers_for_parachain, + impls::{NetworkId, Parachain}, + xcm_emulator::decl_test_parachains, }; // Penpal Parachain declaration @@ -34,6 +38,10 @@ decl_test_parachains! { genesis = genesis(PARA_ID_A), on_init = { penpal_runtime::AuraExt::on_initialize(1); + frame_support::assert_ok!(penpal_runtime::System::set_storage( + penpal_runtime::RuntimeOrigin::root(), + vec![(PenpalRelayNetworkId::key().to_vec(), NetworkId::Rococo.encode())], + )); }, runtime = penpal_runtime, core = { @@ -53,6 +61,10 @@ decl_test_parachains! { genesis = genesis(PARA_ID_B), on_init = { penpal_runtime::AuraExt::on_initialize(1); + frame_support::assert_ok!(penpal_runtime::System::set_storage( + penpal_runtime::RuntimeOrigin::root(), + vec![(PenpalRelayNetworkId::key().to_vec(), NetworkId::Westend.encode())], + )); }, runtime = penpal_runtime, core = { @@ -76,4 +88,8 @@ impl_accounts_helpers_for_parachain!(PenpalB); impl_assert_events_helpers_for_parachain!(PenpalA); impl_assert_events_helpers_for_parachain!(PenpalB); impl_assets_helpers_for_parachain!(PenpalA); +impl_foreign_assets_helpers_for_parachain!(PenpalA, xcm::latest::Location); impl_assets_helpers_for_parachain!(PenpalB); +impl_foreign_assets_helpers_for_parachain!(PenpalB, xcm::latest::Location); +impl_xcm_helpers_for_parachain!(PenpalA); +impl_xcm_helpers_for_parachain!(PenpalB); diff --git a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs index 618c3addc5d0c67c3954610425345d3ec8b2f36b..c8a2f097abe95bb0f6003957c7ef5cc90c4f09c3 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs @@ -38,9 +38,7 @@ pub use polkadot_runtime_parachains::{ inclusion::{AggregateMessageOrigin, UmpQueueId}, }; pub use xcm::{ - prelude::{Location, OriginKind, Outcome, VersionedXcm, XcmVersion}, - v3, - v4::Error as XcmError, + prelude::{Location, OriginKind, Outcome, VersionedXcm, XcmError, XcmVersion}, DoubleEncoded, }; @@ -696,12 +694,12 @@ macro_rules! impl_assets_helpers_for_system_parachain { #[macro_export] macro_rules! impl_assets_helpers_for_parachain { - ( $chain:ident) => { + ($chain:ident) => { $crate::impls::paste::paste! { impl<N: $crate::impls::Network> $chain<N> { - /// Create foreign assets using sudo `ForeignAssets::force_create()` - pub fn force_create_foreign_asset( - id: $crate::impls::v3::Location, + /// Create assets using sudo `Assets::force_create()` + pub fn force_create_asset( + id: u32, owner: $crate::impls::AccountId, is_sufficient: bool, min_balance: u128, @@ -711,20 +709,20 @@ macro_rules! impl_assets_helpers_for_parachain { let sudo_origin = <$chain<N> as $crate::impls::Chain>::RuntimeOrigin::root(); <Self as $crate::impls::TestExt>::execute_with(|| { $crate::impls::assert_ok!( - <Self as [<$chain ParaPallet>]>::ForeignAssets::force_create( + <Self as [<$chain ParaPallet>]>::Assets::force_create( sudo_origin, - id.clone(), + id.clone().into(), owner.clone().into(), is_sufficient, min_balance, ) ); - assert!(<Self as [<$chain ParaPallet>]>::ForeignAssets::asset_exists(id.clone())); + assert!(<Self as [<$chain ParaPallet>]>::Assets::asset_exists(id.clone())); type RuntimeEvent<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent; $crate::impls::assert_expected_events!( Self, vec![ - RuntimeEvent::<N>::ForeignAssets( + RuntimeEvent::<N>::Assets( $crate::impls::pallet_assets::Event::ForceCreated { asset_id, .. @@ -736,19 +734,19 @@ macro_rules! impl_assets_helpers_for_parachain { for (beneficiary, amount) in prefund_accounts.into_iter() { let signed_origin = <$chain<N> as $crate::impls::Chain>::RuntimeOrigin::signed(owner.clone()); - Self::mint_foreign_asset(signed_origin, id.clone(), beneficiary, amount); + Self::mint_asset(signed_origin, id.clone(), beneficiary, amount); } } - /// Mint assets making use of the ForeignAssets pallet-assets instance - pub fn mint_foreign_asset( + /// Mint assets making use of the assets pallet + pub fn mint_asset( signed_origin: <Self as $crate::impls::Chain>::RuntimeOrigin, - id: $crate::impls::v3::Location, + id: u32, beneficiary: $crate::impls::AccountId, amount_to_mint: u128, ) { <Self as $crate::impls::TestExt>::execute_with(|| { - $crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::ForeignAssets::mint( + $crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::Assets::mint( signed_origin, id.clone().into(), beneficiary.clone().into(), @@ -760,7 +758,7 @@ macro_rules! impl_assets_helpers_for_parachain { $crate::impls::assert_expected_events!( Self, vec![ - RuntimeEvent::<N>::ForeignAssets( + RuntimeEvent::<N>::Assets( $crate::impls::pallet_assets::Event::Issued { asset_id, owner, amount } ) => { asset_id: *asset_id == id, @@ -771,9 +769,39 @@ macro_rules! impl_assets_helpers_for_parachain { ); }); } - /// Create assets using sudo `Assets::force_create()` - pub fn force_create_asset( - id: u32, + + /// Returns the encoded call for `create` from the assets pallet + pub fn create_asset_call( + asset_id: u32, + min_balance: $crate::impls::Balance, + admin: $crate::impls::AccountId, + ) -> $crate::impls::DoubleEncoded<()> { + use $crate::impls::{Chain, Encode}; + + <Self as Chain>::RuntimeCall::Assets($crate::impls::pallet_assets::Call::< + <Self as Chain>::Runtime, + $crate::impls::pallet_assets::Instance1, + >::create { + id: asset_id.into(), + min_balance, + admin: admin.into(), + }) + .encode() + .into() + } + } + } + }; +} + +#[macro_export] +macro_rules! impl_foreign_assets_helpers_for_parachain { + ($chain:ident, $asset_id_type:ty) => { + $crate::impls::paste::paste! { + impl<N: $crate::impls::Network> $chain<N> { + /// Create foreign assets using sudo `ForeignAssets::force_create()` + pub fn force_create_foreign_asset( + id: $asset_id_type, owner: $crate::impls::AccountId, is_sufficient: bool, min_balance: u128, @@ -783,20 +811,20 @@ macro_rules! impl_assets_helpers_for_parachain { let sudo_origin = <$chain<N> as $crate::impls::Chain>::RuntimeOrigin::root(); <Self as $crate::impls::TestExt>::execute_with(|| { $crate::impls::assert_ok!( - <Self as [<$chain ParaPallet>]>::Assets::force_create( + <Self as [<$chain ParaPallet>]>::ForeignAssets::force_create( sudo_origin, - id.clone().into(), + id.clone(), owner.clone().into(), is_sufficient, min_balance, ) ); - assert!(<Self as [<$chain ParaPallet>]>::Assets::asset_exists(id.clone())); + assert!(<Self as [<$chain ParaPallet>]>::ForeignAssets::asset_exists(id.clone())); type RuntimeEvent<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent; $crate::impls::assert_expected_events!( Self, vec![ - RuntimeEvent::<N>::Assets( + RuntimeEvent::<N>::ForeignAssets( $crate::impls::pallet_assets::Event::ForceCreated { asset_id, .. @@ -808,19 +836,19 @@ macro_rules! impl_assets_helpers_for_parachain { for (beneficiary, amount) in prefund_accounts.into_iter() { let signed_origin = <$chain<N> as $crate::impls::Chain>::RuntimeOrigin::signed(owner.clone()); - Self::mint_asset(signed_origin, id.clone(), beneficiary, amount); + Self::mint_foreign_asset(signed_origin, id.clone(), beneficiary, amount); } } - /// Mint assets making use of the assets pallet - pub fn mint_asset( + /// Mint assets making use of the ForeignAssets pallet-assets instance + pub fn mint_foreign_asset( signed_origin: <Self as $crate::impls::Chain>::RuntimeOrigin, - id: u32, + id: $asset_id_type, beneficiary: $crate::impls::AccountId, amount_to_mint: u128, ) { <Self as $crate::impls::TestExt>::execute_with(|| { - $crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::Assets::mint( + $crate::impls::assert_ok!(<Self as [<$chain ParaPallet>]>::ForeignAssets::mint( signed_origin, id.clone().into(), beneficiary.clone().into(), @@ -832,7 +860,7 @@ macro_rules! impl_assets_helpers_for_parachain { $crate::impls::assert_expected_events!( Self, vec![ - RuntimeEvent::<N>::Assets( + RuntimeEvent::<N>::ForeignAssets( $crate::impls::pallet_assets::Event::Issued { asset_id, owner, amount } ) => { asset_id: *asset_id == id, @@ -844,29 +872,9 @@ macro_rules! impl_assets_helpers_for_parachain { }); } - /// Returns the encoded call for `create` from the assets pallet - pub fn create_asset_call( - asset_id: u32, - min_balance: $crate::impls::Balance, - admin: $crate::impls::AccountId, - ) -> $crate::impls::DoubleEncoded<()> { - use $crate::impls::{Chain, Encode}; - - <Self as Chain>::RuntimeCall::Assets($crate::impls::pallet_assets::Call::< - <Self as Chain>::Runtime, - $crate::impls::pallet_assets::Instance1, - >::create { - id: asset_id.into(), - min_balance, - admin: admin.into(), - }) - .encode() - .into() - } - /// Returns the encoded call for `create` from the foreign assets pallet pub fn create_foreign_asset_call( - asset_id: $crate::impls::v3::Location, + asset_id: $asset_id_type, min_balance: $crate::impls::Balance, admin: $crate::impls::AccountId, ) -> $crate::impls::DoubleEncoded<()> { diff --git a/cumulus/parachains/integration-tests/emulated/networks/rococo-westend-system/src/lib.rs b/cumulus/parachains/integration-tests/emulated/networks/rococo-westend-system/src/lib.rs index ee8b038a364d73301732f278786b30b18d534643..d87bc5aa9633468a0f379da1c417a1caaba2505f 100644 --- a/cumulus/parachains/integration-tests/emulated/networks/rococo-westend-system/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/networks/rococo-westend-system/src/lib.rs @@ -25,7 +25,7 @@ use asset_hub_rococo_emulated_chain::AssetHubRococo; use asset_hub_westend_emulated_chain::AssetHubWestend; use bridge_hub_rococo_emulated_chain::BridgeHubRococo; use bridge_hub_westend_emulated_chain::BridgeHubWestend; -use penpal_emulated_chain::PenpalA; +use penpal_emulated_chain::{PenpalA, PenpalB}; use rococo_emulated_chain::Rococo; use westend_emulated_chain::Westend; @@ -48,13 +48,13 @@ decl_test_networks! { PenpalA, ], bridge = RococoWestendMockBridge - }, pub struct WestendMockNet { relay_chain = Westend, parachains = vec![ AssetHubWestend, BridgeHubWestend, + PenpalB, ], bridge = WestendRococoMockBridge }, @@ -96,5 +96,6 @@ decl_test_sender_receiver_accounts_parameter_types! { WestendRelay { sender: ALICE, receiver: BOB }, AssetHubWestendPara { sender: ALICE, receiver: BOB }, BridgeHubWestendPara { sender: ALICE, receiver: BOB }, - PenpalAPara { sender: ALICE, receiver: BOB } + PenpalAPara { sender: ALICE, receiver: BOB }, + PenpalBPara { sender: ALICE, receiver: BOB } } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs index a5a4914e21d826ea6c70af4ae31a0d4dee43ef64..322c6cf1f2282670e474b0c5737fabd09afbe94d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs @@ -30,6 +30,7 @@ mod imports { prelude::{AccountId32 as AccountId32Junction, *}, v3, }; + pub use xcm_executor::traits::TransferType; // Cumulus pub use asset_test_utils::xcm_helpers; @@ -81,6 +82,7 @@ mod imports { pub type SystemParaToParaTest = Test<AssetHubRococo, PenpalA>; pub type ParaToSystemParaTest = Test<PenpalA, AssetHubRococo>; pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Rococo>; + pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubRococo>; } #[cfg(test)] diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/foreign_assets_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/foreign_assets_transfers.rs new file mode 100644 index 0000000000000000000000000000000000000000..6bdf89e6f277edb7d0e6e85383223d52b24c89ea --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/foreign_assets_transfers.rs @@ -0,0 +1,628 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::reserve_transfer::*; +use crate::{ + imports::*, + tests::teleport::do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt, +}; + +fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) { + type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent; + let sov_penpal_a_on_ah = AssetHubRococo::sovereign_account_id_of( + AssetHubRococo::sibling_location_of(PenpalA::para_id()), + ); + let sov_penpal_b_on_ah = AssetHubRococo::sovereign_account_id_of( + AssetHubRococo::sibling_location_of(PenpalB::para_id()), + ); + + assert_expected_events!( + AssetHubRococo, + vec![ + // Withdrawn from sender parachain SA + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == sov_penpal_a_on_ah, + amount: *amount == t.args.amount, + }, + // Deposited to receiver parachain SA + RuntimeEvent::Balances( + pallet_balances::Event::Minted { who, .. } + ) => { + who: *who == sov_penpal_b_on_ah, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + +fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + <AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::LocalReserve), + bx!(fee.id.into()), + bx!(TransferType::LocalReserve), + t.args.weight_limit, + ) +} + +fn para_to_ah_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + <PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::DestinationReserve), + bx!(fee.id.into()), + bx!(TransferType::DestinationReserve), + t.args.weight_limit, + ) +} + +fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubRococo::para_id()); + <PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())), + bx!(fee.id.into()), + bx!(TransferType::RemoteReserve(asset_hub_location.into())), + t.args.weight_limit, + ) +} + +fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + <PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::Teleport), + bx!(fee.id.into()), + bx!(TransferType::DestinationReserve), + t.args.weight_limit, + ) +} + +fn asset_hub_to_para_teleport_foreign_assets(t: SystemParaToParaTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + <AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::Teleport), + bx!(fee.id.into()), + bx!(TransferType::LocalReserve), + t.args.weight_limit, + ) +} + +// =========================================================================== +// ======= Transfer - Native + Bridged Assets - AssetHub->Parachain ========== +// =========================================================================== +/// Transfers of native asset plus bridged asset from AssetHub to some Parachain +/// while paying fees using native asset. +#[test] +fn transfer_foreign_assets_from_asset_hub_to_para() { + let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sender = AssetHubRococoSender::get(); + let native_amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; + let native_asset_location = RelayLocation::get(); + let receiver = PenpalAReceiver::get(); + let assets_owner = PenpalAssetOwner::get(); + // Foreign asset used: bridged WND + let foreign_amount_to_send = ASSET_HUB_ROCOCO_ED * 10_000_000; + let wnd_at_rococo_parachains = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); + + // Configure destination chain to trust AH as reserve of WND + PenpalA::execute_with(|| { + assert_ok!(<PenpalA as Chain>::System::set_storage( + <PenpalA as Chain>::RuntimeOrigin::root(), + vec![( + penpal_runtime::xcm_config::CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Westend)]).encode(), + )], + )); + }); + PenpalA::force_create_foreign_asset( + wnd_at_rococo_parachains.clone(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + AssetHubRococo::force_create_foreign_asset( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + AssetHubRococo::mint_foreign_asset( + <AssetHubRococo as Chain>::RuntimeOrigin::signed(assets_owner), + wnd_at_rococo_parachains.clone().try_into().unwrap(), + sender.clone(), + foreign_amount_to_send * 2, + ); + + // Assets to send + let assets: Vec<Asset> = vec![ + (Parent, native_amount_to_send).into(), + (wnd_at_rococo_parachains.clone(), foreign_amount_to_send).into(), + ]; + let fee_asset_id = AssetId(Parent.into()); + let fee_asset_item = assets.iter().position(|a| a.id == fee_asset_id).unwrap() as u32; + + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver.clone(), + native_amount_to_send, + assets.into(), + None, + fee_asset_item, + ), + }; + let mut test = SystemParaToParaTest::new(test_args); + + // Query initial balances + let sender_balance_before = test.sender.balance; + let sender_wnds_before = AssetHubRococo::execute_with(|| { + type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + &sender, + ) + }); + let receiver_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(native_asset_location.clone(), &receiver) + }); + let receiver_wnds_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains.clone(), &receiver) + }); + + // Set assertions and dispatchables + test.set_assertion::<AssetHubRococo>(system_para_to_para_sender_assertions); + test.set_assertion::<PenpalA>(system_para_to_para_receiver_assertions); + test.set_dispatchable::<AssetHubRococo>(ah_to_para_transfer_assets); + test.assert(); + + // Query final balances + let sender_balance_after = test.sender.balance; + let sender_wnds_after = AssetHubRococo::execute_with(|| { + type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + &sender, + ) + }); + let receiver_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(native_asset_location, &receiver) + }); + let receiver_wnds_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &receiver) + }); + + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_balance_after < sender_balance_before - native_amount_to_send); + // Sender's balance is reduced by foreign amount sent + assert_eq!(sender_wnds_after, sender_wnds_before - foreign_amount_to_send); + // Receiver's assets is increased + assert!(receiver_assets_after > receiver_assets_before); + // Receiver's assets increased by `amount_to_send - delivery_fees - bought_execution`; + // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but + // should be non-zero + assert!(receiver_assets_after < receiver_assets_before + native_amount_to_send); + // Receiver's balance is increased by foreign amount sent + assert_eq!(receiver_wnds_after, receiver_wnds_before + foreign_amount_to_send); +} + +/// Reserve Transfers of native asset from Parachain to System Parachain should work +// =========================================================================== +// ======= Transfer - Native + Bridged Assets - Parachain->AssetHub ========== +// =========================================================================== +/// Transfers of native asset plus bridged asset from some Parachain to AssetHub +/// while paying fees using native asset. +#[test] +fn transfer_foreign_assets_from_para_to_asset_hub() { + // Init values for Parachain + let destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()); + let sender = PenpalASender::get(); + let native_amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; + let native_asset_location = RelayLocation::get(); + let assets_owner = PenpalAssetOwner::get(); + + // Foreign asset used: bridged WND + let foreign_amount_to_send = ASSET_HUB_ROCOCO_ED * 10_000_000; + let wnd_at_rococo_parachains = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); + + // Configure destination chain to trust AH as reserve of WND + PenpalA::execute_with(|| { + assert_ok!(<PenpalA as Chain>::System::set_storage( + <PenpalA as Chain>::RuntimeOrigin::root(), + vec![( + penpal_runtime::xcm_config::CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Westend)]).encode(), + )], + )); + }); + PenpalA::force_create_foreign_asset( + wnd_at_rococo_parachains.clone(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + AssetHubRococo::force_create_foreign_asset( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + <PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()), + native_asset_location.clone(), + sender.clone(), + native_amount_to_send * 2, + ); + PenpalA::mint_foreign_asset( + <PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()), + wnd_at_rococo_parachains.clone(), + sender.clone(), + foreign_amount_to_send * 2, + ); + + // Init values for System Parachain + let receiver = AssetHubRococoReceiver::get(); + let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); + + // fund Parachain's SA on AssetHub with the assets held in reserve + AssetHubRococo::fund_accounts(vec![( + sov_penpal_on_ahr.clone().into(), + native_amount_to_send * 2, + )]); + AssetHubRococo::mint_foreign_asset( + <AssetHubRococo as Chain>::RuntimeOrigin::signed(assets_owner), + wnd_at_rococo_parachains.clone().try_into().unwrap(), + sov_penpal_on_ahr, + foreign_amount_to_send * 2, + ); + + // Assets to send + let assets: Vec<Asset> = vec![ + (Parent, native_amount_to_send).into(), + (wnd_at_rococo_parachains.clone(), foreign_amount_to_send).into(), + ]; + let fee_asset_id = AssetId(Parent.into()); + let fee_asset_item = assets.iter().position(|a| a.id == fee_asset_id).unwrap() as u32; + + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver.clone(), + native_amount_to_send, + assets.into(), + None, + fee_asset_item, + ), + }; + let mut test = ParaToSystemParaTest::new(test_args); + + // Query initial balances + let sender_native_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(native_asset_location.clone(), &sender) + }); + let sender_wnds_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains.clone(), &sender) + }); + let receiver_native_before = test.receiver.balance; + let receiver_wnds_before = AssetHubRococo::execute_with(|| { + type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + &receiver, + ) + }); + + // Set assertions and dispatchables + test.set_assertion::<PenpalA>(para_to_system_para_sender_assertions); + test.set_assertion::<AssetHubRococo>(para_to_system_para_receiver_assertions); + test.set_dispatchable::<PenpalA>(para_to_ah_transfer_assets); + test.assert(); + + // Query final balances + let sender_native_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(native_asset_location, &sender) + }); + let sender_wnds_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains.clone(), &sender) + }); + let receiver_native_after = test.receiver.balance; + let receiver_wnds_after = AssetHubRococo::execute_with(|| { + type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + wnd_at_rococo_parachains.try_into().unwrap(), + &receiver, + ) + }); + + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_native_after < sender_native_before - native_amount_to_send); + // Sender's balance is reduced by foreign amount sent + assert_eq!(sender_wnds_after, sender_wnds_before - foreign_amount_to_send); + // Receiver's balance is increased + assert!(receiver_native_after > receiver_native_before); + // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; + // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but + // should be non-zero + assert!(receiver_native_after < receiver_native_before + native_amount_to_send); + // Receiver's balance is increased by foreign amount sent + assert_eq!(receiver_wnds_after, receiver_wnds_before + foreign_amount_to_send); +} + +// ============================================================================== +// ===== Transfer - Native + Bridged Assets - Parachain->AssetHub->Parachain ==== +// ============================================================================== +/// Transfers of native asset plus bridged asset from Parachain to Parachain +/// (through AssetHub reserve) with fees paid using native asset. +#[test] +fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { + // Init values for Parachain Origin + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let roc_to_send: Balance = ROCOCO_ED * 10000; + let assets_owner = PenpalAssetOwner::get(); + let roc_location = RelayLocation::get(); + let sender_as_seen_by_ah = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_ah = AssetHubRococo::sovereign_account_id_of(sender_as_seen_by_ah); + let receiver_as_seen_by_ah = AssetHubRococo::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_ah = AssetHubRococo::sovereign_account_id_of(receiver_as_seen_by_ah); + let wnd_to_send = ASSET_HUB_ROCOCO_ED * 10_000_000; + + // Configure destination chain to trust AH as reserve of WND + PenpalB::execute_with(|| { + assert_ok!(<PenpalB as Chain>::System::set_storage( + <PenpalB as Chain>::RuntimeOrigin::root(), + vec![( + penpal_runtime::xcm_config::CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Westend)]).encode(), + )], + )); + }); + + // Register WND as foreign asset and transfer it around the Rococo ecosystem + let wnd_at_rococo_parachains = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); + AssetHubRococo::force_create_foreign_asset( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + PenpalA::force_create_foreign_asset( + wnd_at_rococo_parachains.clone(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + PenpalB::force_create_foreign_asset( + wnd_at_rococo_parachains.clone(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + <PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()), + roc_location.clone(), + sender.clone(), + roc_to_send * 2, + ); + PenpalA::mint_foreign_asset( + <PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()), + wnd_at_rococo_parachains.clone(), + sender.clone(), + wnd_to_send * 2, + ); + // fund the Parachain Origin's SA on Asset Hub with the assets held in reserve + AssetHubRococo::fund_accounts(vec![(sov_of_sender_on_ah.clone().into(), roc_to_send * 2)]); + AssetHubRococo::mint_foreign_asset( + <AssetHubRococo as Chain>::RuntimeOrigin::signed(assets_owner), + wnd_at_rococo_parachains.clone().try_into().unwrap(), + sov_of_sender_on_ah.clone(), + wnd_to_send * 2, + ); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Assets to send + let assets: Vec<Asset> = vec![ + (roc_location.clone(), roc_to_send).into(), + (wnd_at_rococo_parachains.clone(), wnd_to_send).into(), + ]; + let fee_asset_id: AssetId = roc_location.clone().into(); + let fee_asset_item = assets.iter().position(|a| a.id == fee_asset_id).unwrap() as u32; + + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + roc_to_send, + assets.into(), + None, + fee_asset_item, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // Query initial balances + let sender_rocs_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_location.clone(), &sender) + }); + let sender_wnds_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains.clone(), &sender) + }); + let rocs_in_sender_reserve_on_ahr_before = + <AssetHubRococo as Chain>::account_data_of(sov_of_sender_on_ah.clone()).free; + let wnds_in_sender_reserve_on_ahr_before = AssetHubRococo::execute_with(|| { + type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + &sov_of_sender_on_ah, + ) + }); + let rocs_in_receiver_reserve_on_ahr_before = + <AssetHubRococo as Chain>::account_data_of(sov_of_receiver_on_ah.clone()).free; + let wnds_in_receiver_reserve_on_ahr_before = AssetHubRococo::execute_with(|| { + type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + &sov_of_receiver_on_ah, + ) + }); + let receiver_rocs_before = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_location.clone(), &receiver) + }); + let receiver_wnds_before = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains.clone(), &receiver) + }); + + // Set assertions and dispatchables + test.set_assertion::<PenpalA>(para_to_para_through_hop_sender_assertions); + test.set_assertion::<AssetHubRococo>(para_to_para_assethub_hop_assertions); + test.set_assertion::<PenpalB>(para_to_para_through_hop_receiver_assertions); + test.set_dispatchable::<PenpalA>(para_to_para_transfer_assets_through_ah); + test.assert(); + + // Query final balances + let sender_rocs_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_location.clone(), &sender) + }); + let sender_wnds_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains.clone(), &sender) + }); + let wnds_in_sender_reserve_on_ahr_after = AssetHubRococo::execute_with(|| { + type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + &sov_of_sender_on_ah, + ) + }); + let rocs_in_sender_reserve_on_ahr_after = + <AssetHubRococo as Chain>::account_data_of(sov_of_sender_on_ah).free; + let wnds_in_receiver_reserve_on_ahr_after = AssetHubRococo::execute_with(|| { + type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + wnd_at_rococo_parachains.clone().try_into().unwrap(), + &sov_of_receiver_on_ah, + ) + }); + let rocs_in_receiver_reserve_on_ahr_after = + <AssetHubRococo as Chain>::account_data_of(sov_of_receiver_on_ah).free; + let receiver_rocs_after = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_location, &receiver) + }); + let receiver_wnds_after = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_at_rococo_parachains, &receiver) + }); + + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_rocs_after < sender_rocs_before - roc_to_send); + assert_eq!(sender_wnds_after, sender_wnds_before - wnd_to_send); + // Sovereign accounts on reserve are changed accordingly + assert_eq!( + rocs_in_sender_reserve_on_ahr_after, + rocs_in_sender_reserve_on_ahr_before - roc_to_send + ); + assert_eq!( + wnds_in_sender_reserve_on_ahr_after, + wnds_in_sender_reserve_on_ahr_before - wnd_to_send + ); + assert!(rocs_in_receiver_reserve_on_ahr_after > rocs_in_receiver_reserve_on_ahr_before); + assert_eq!( + wnds_in_receiver_reserve_on_ahr_after, + wnds_in_receiver_reserve_on_ahr_before + wnd_to_send + ); + // Receiver's balance is increased + assert!(receiver_rocs_after > receiver_rocs_before); + assert_eq!(receiver_wnds_after, receiver_wnds_before + wnd_to_send); +} + +// ============================================================================================== +// ==== Bidirectional Transfer - Native + Teleportable Foreign Assets - Parachain<->AssetHub ==== +// ============================================================================================== +/// Transfers of native asset plus teleportable foreign asset from Parachain to AssetHub and back +/// with fees paid using native asset. +#[test] +fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explicit_transfer_types() { + do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( + para_to_asset_hub_teleport_foreign_assets, + asset_hub_to_para_teleport_foreign_assets, + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs index b3841af0e6c38372b8fb621fac468b25bdec63a1..2402989225af2a6b3d03c7f353c8b0e7266b9fb1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod foreign_assets_transfers; mod reserve_transfer; mod send; mod set_xcm_versions; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index a0738839087a51e87df3187ac6f06d2889cce64e..5aef70f5cbfc08522a5693d38bb2c774209a2469 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -47,7 +47,7 @@ fn para_to_relay_sender_assertions(t: ParaToRelayTest) { RuntimeEvent::ForeignAssets( pallet_assets::Event::Burned { asset_id, owner, balance, .. } ) => { - asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + asset_id: *asset_id == RelayLocation::get(), owner: *owner == t.sender.account_id, balance: *balance == t.args.amount, }, @@ -55,70 +55,92 @@ fn para_to_relay_sender_assertions(t: ParaToRelayTest) { ); } -fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { +pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent; - - AssetHubRococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 864_610_000, - 8_799, - ))); - + AssetHubRococo::assert_xcm_pallet_attempted_complete(None); + + let sov_acc_of_dest = AssetHubRococo::sovereign_account_id_of(t.args.dest.clone()); + for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() { + let expected_id = asset.id.0.clone().try_into().unwrap(); + let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); + if idx == t.args.fee_asset_item as usize { + assert_expected_events!( + AssetHubRococo, + vec![ + // Amount of native asset is transferred to Parachain's Sovereign account + RuntimeEvent::Balances( + pallet_balances::Event::Transfer { from, to, amount } + ) => { + from: *from == t.sender.account_id, + to: *to == sov_acc_of_dest, + amount: *amount == asset_amount, + }, + ] + ); + } else { + assert_expected_events!( + AssetHubRococo, + vec![ + // Amount of foreign asset is transferred to Parachain's Sovereign account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Transferred { asset_id, from, to, amount }, + ) => { + asset_id: *asset_id == expected_id, + from: *from == t.sender.account_id, + to: *to == sov_acc_of_dest, + amount: *amount == asset_amount, + }, + ] + ); + } + } assert_expected_events!( AssetHubRococo, vec![ - // Amount to reserve transfer is transferred to Parachain's Sovereign account - RuntimeEvent::Balances( - pallet_balances::Event::Transfer { from, to, amount } - ) => { - from: *from == t.sender.account_id, - to: *to == AssetHubRococo::sovereign_account_id_of( - t.args.dest.clone() - ), - amount: *amount == t.args.amount, - }, // Transport fees are paid - RuntimeEvent::PolkadotXcm( - pallet_xcm::Event::FeesPaid { .. } - ) => {}, + RuntimeEvent::PolkadotXcm(pallet_xcm::Event::FeesPaid { .. }) => {}, ] ); AssetHubRococo::assert_xcm_pallet_sent(); } -fn system_para_to_para_receiver_assertions(t: SystemParaToParaTest) { +pub fn system_para_to_para_receiver_assertions(t: SystemParaToParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); PenpalA::assert_xcmp_queue_success(None); - - assert_expected_events!( - PenpalA, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == system_para_native_asset_location, - owner: *owner == t.receiver.account_id, - }, - ] - ); + for asset in t.args.assets.into_inner().into_iter() { + let expected_id = asset.id.0.try_into().unwrap(); + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.receiver.account_id, + }, + ] + ); + } } -fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) { +pub fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799))); - assert_expected_events!( - PenpalA, - vec![ - // Amount to reserve transfer is transferred to Parachain's Sovereign account - RuntimeEvent::ForeignAssets( - pallet_assets::Event::Burned { asset_id, owner, balance, .. } - ) => { - asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), - owner: *owner == t.sender.account_id, - balance: *balance == t.args.amount, - }, - ] - ); + PenpalA::assert_xcm_pallet_attempted_complete(None); + for asset in t.args.assets.into_inner().into_iter() { + let expected_id = asset.id.0; + let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance } + ) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.sender.account_id, + balance: *balance == asset_amount, + }, + ] + ); + } } fn para_to_relay_receiver_assertions(t: ParaToRelayTest) { @@ -150,25 +172,57 @@ fn para_to_relay_receiver_assertions(t: ParaToRelayTest) { ); } -fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) { +pub fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent; - let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of( - AssetHubRococo::sibling_location_of(PenpalA::para_id()), - ); - AssetHubRococo::assert_xcmp_queue_success(None); + let sov_acc_of_penpal = AssetHubRococo::sovereign_account_id_of(t.args.dest.clone()); + for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() { + let expected_id = asset.id.0.clone().try_into().unwrap(); + let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); + if idx == t.args.fee_asset_item as usize { + assert_expected_events!( + AssetHubRococo, + vec![ + // Amount of native is withdrawn from Parachain's Sovereign account + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == sov_acc_of_penpal.clone().into(), + amount: *amount == asset_amount, + }, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == t.receiver.account_id, + }, + ] + ); + } else { + assert_expected_events!( + AssetHubRococo, + vec![ + // Amount of foreign asset is transferred from Parachain's Sovereign account + // to Receiver's account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance }, + ) => { + asset_id: *asset_id == expected_id, + owner: *owner == sov_acc_of_penpal, + balance: *balance == asset_amount, + }, + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Issued { asset_id, owner, amount }, + ) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.receiver.account_id, + amount: *amount == asset_amount, + }, + ] + ); + } + } assert_expected_events!( AssetHubRococo, vec![ - // Amount to reserve transfer is withdrawn from Parachain's Sovereign account - RuntimeEvent::Balances( - pallet_balances::Event::Burned { who, amount } - ) => { - who: *who == sov_penpal_on_ahr.clone().into(), - amount: *amount == t.args.amount, - }, - RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } ) => {}, @@ -212,10 +266,8 @@ fn system_para_to_para_assets_sender_assertions(t: SystemParaToParaTest) { fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - let reservable_asset_location = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); + let reservable_asset_location = PenpalLocalReservableFromAssetHub::get(); PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8799))); assert_expected_events!( PenpalA, @@ -245,14 +297,13 @@ fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) { fn system_para_to_para_assets_receiver_assertions(t: SystemParaToParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - let system_para_asset_location = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let system_para_asset_location = PenpalLocalReservableFromAssetHub::get(); PenpalA::assert_xcmp_queue_success(None); assert_expected_events!( PenpalA, vec![ RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + asset_id: *asset_id == RelayLocation::get(), owner: *owner == t.receiver.account_id, }, RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { @@ -304,7 +355,7 @@ fn relay_to_para_assets_receiver_assertions(t: RelayToParaTest) { PenpalA, vec![ RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + asset_id: *asset_id == RelayLocation::get(), owner: *owner == t.receiver.account_id, }, RuntimeEvent::MessageQueue( @@ -314,29 +365,27 @@ fn relay_to_para_assets_receiver_assertions(t: RelayToParaTest) { ); } -fn para_to_para_through_relay_sender_assertions(t: ParaToParaThroughRelayTest) { +pub fn para_to_para_through_hop_sender_assertions<Hop: Clone>(t: Test<PenpalA, PenpalB, Hop>) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - let relay_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - PenpalA::assert_xcm_pallet_attempted_complete(None); - // XCM sent to relay reserve - PenpalA::assert_parachain_system_ump_sent(); - - assert_expected_events!( - PenpalA, - vec![ - // Amount to reserve transfer is transferred to Parachain's Sovereign account - RuntimeEvent::ForeignAssets( - pallet_assets::Event::Burned { asset_id, owner, balance }, - ) => { - asset_id: *asset_id == relay_asset_location, - owner: *owner == t.sender.account_id, - balance: *balance == t.args.amount, - }, - ] - ); + for asset in t.args.assets.into_inner() { + let expected_id = asset.id.0.clone().try_into().unwrap(); + let amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); + assert_expected_events!( + PenpalA, + vec![ + // Amount to reserve transfer is transferred to Parachain's Sovereign account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance }, + ) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.sender.account_id, + balance: *balance == amount, + }, + ] + ); + } } fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { @@ -369,22 +418,22 @@ fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { ); } -fn para_to_para_through_relay_receiver_assertions(t: ParaToParaThroughRelayTest) { +pub fn para_to_para_through_hop_receiver_assertions<Hop: Clone>(t: Test<PenpalA, PenpalB, Hop>) { type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent; - let relay_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); PenpalB::assert_xcmp_queue_success(None); - - assert_expected_events!( - PenpalB, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == relay_asset_location, - owner: *owner == t.receiver.account_id, - }, - ] - ); + for asset in t.args.assets.into_inner().into_iter() { + let expected_id = asset.id.0.try_into().unwrap(); + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.receiver.account_id, + }, + ] + ); + } } fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult { @@ -526,8 +575,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() { let amount_to_send: Balance = ROCOCO_ED * 1000; // Init values fot Parachain - let relay_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let relay_native_asset_location = RelayLocation::get(); let receiver = PenpalAReceiver::get(); // Init Test @@ -542,7 +590,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() { let sender_balance_before = test.sender.balance; let receiver_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.into(), &receiver) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &receiver) }); // Set assertions and dispatchables @@ -555,7 +603,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() { let sender_balance_after = test.sender.balance; let receiver_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.into(), &receiver) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &receiver) }); // Sender's balance is reduced by amount sent plus delivery fees @@ -577,13 +625,12 @@ fn reserve_transfer_native_asset_from_para_to_relay() { let amount_to_send: Balance = ROCOCO_ED * 1000; let assets: Assets = (Parent, amount_to_send).into(); let asset_owner = PenpalAssetOwner::get(); - let relay_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let relay_native_asset_location = RelayLocation::get(); // fund Parachain's sender account PenpalA::mint_foreign_asset( <PenpalA as Chain>::RuntimeOrigin::signed(asset_owner), - relay_native_asset_location, + relay_native_asset_location.clone(), sender.clone(), amount_to_send * 2, ); @@ -614,7 +661,7 @@ fn reserve_transfer_native_asset_from_para_to_relay() { // Query initial balances let sender_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.into(), &sender) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; @@ -627,7 +674,7 @@ fn reserve_transfer_native_asset_from_para_to_relay() { // Query final balances let sender_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.into(), &sender) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &sender) }); let receiver_balance_after = test.receiver.balance; @@ -654,8 +701,7 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { let assets: Assets = (Parent, amount_to_send).into(); // Init values for Parachain - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); let receiver = PenpalAReceiver::get(); // Init Test @@ -677,7 +723,7 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { let sender_balance_before = test.sender.balance; let receiver_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.into(), &receiver) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &receiver) }); // Set assertions and dispatchables @@ -711,14 +757,13 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { let sender = PenpalASender::get(); let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; let assets: Assets = (Parent, amount_to_send).into(); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); let asset_owner = PenpalAssetOwner::get(); // fund Parachain's sender account PenpalA::mint_foreign_asset( <PenpalA as Chain>::RuntimeOrigin::signed(asset_owner), - system_para_native_asset_location, + system_para_native_asset_location.clone(), sender.clone(), amount_to_send * 2, ); @@ -749,7 +794,7 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { // Query initial balances let sender_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; @@ -776,9 +821,9 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { assert!(receiver_balance_after < receiver_balance_before + amount_to_send); } -// ========================================================================= -// ======= Reserve Transfers - Non-system Asset - AssetHub<>Parachain ====== -// ========================================================================= +// ================================================================================== +// ======= Reserve Transfers - Native + Non-system Asset - AssetHub<>Parachain ====== +// ================================================================================== /// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should /// work #[test] @@ -817,10 +862,8 @@ fn reserve_transfer_assets_from_system_para_to_para() { // Init values for Parachain let receiver = PenpalAReceiver::get(); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - let system_para_foreign_asset_location = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); + let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); // Init Test let para_test_args = TestContext { @@ -845,11 +888,14 @@ fn reserve_transfer_assets_from_system_para_to_para() { }); let receiver_system_native_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location, &receiver) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &receiver) }); let receiver_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_foreign_asset_location, &receiver) + <ForeignAssets as Inspect<_>>::balance( + system_para_foreign_asset_location.clone(), + &receiver, + ) }); // Set assertions and dispatchables @@ -866,7 +912,7 @@ fn reserve_transfer_assets_from_system_para_to_para() { }); let receiver_system_native_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location, &receiver) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &receiver) }); let receiver_foreign_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; @@ -904,14 +950,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { let asset_amount_to_send = ASSET_HUB_ROCOCO_ED * 10000; let penpal_asset_owner = PenpalAssetOwner::get(); let penpal_asset_owner_signer = <PenpalA as Chain>::RuntimeOrigin::signed(penpal_asset_owner); - let asset_location_on_penpal = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); - let asset_location_on_penpal_latest: Location = asset_location_on_penpal.try_into().unwrap(); - let system_asset_location_on_penpal = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let asset_location_on_penpal = PenpalLocalReservableFromAssetHub::get(); + let system_asset_location_on_penpal = RelayLocation::get(); let assets: Assets = vec![ (Parent, fee_amount_to_send).into(), - (asset_location_on_penpal_latest, asset_amount_to_send).into(), + (asset_location_on_penpal.clone(), asset_amount_to_send).into(), ] .into(); let fee_asset_index = assets @@ -938,10 +981,8 @@ fn reserve_transfer_assets_from_para_to_system_para() { let receiver = AssetHubRococoReceiver::get(); let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - let system_para_foreign_asset_location = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); + let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); let ah_asset_owner = AssetHubRococoAssetOwner::get(); let ah_asset_owner_signer = <AssetHubRococo as Chain>::RuntimeOrigin::signed(ah_asset_owner); @@ -976,11 +1017,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query initial balances let sender_system_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &sender) }); let sender_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_foreign_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(system_para_foreign_asset_location.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; let receiver_assets_before = AssetHubRococo::execute_with(|| { @@ -997,7 +1038,7 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query final balances let sender_system_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &sender) }); let sender_foreign_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; @@ -1029,22 +1070,21 @@ fn reserve_transfer_assets_from_para_to_system_para() { /// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should /// work #[test] -fn reserve_transfer_native_asset_from_para_to_para_trough_relay() { +fn reserve_transfer_native_asset_from_para_to_para_through_relay() { // Init values for Parachain Origin let destination = PenpalA::sibling_location_of(PenpalB::para_id()); let sender = PenpalASender::get(); let amount_to_send: Balance = ROCOCO_ED * 10000; let asset_owner = PenpalAssetOwner::get(); let assets = (Parent, amount_to_send).into(); - let relay_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let relay_native_asset_location = RelayLocation::get(); let sender_as_seen_by_relay = Rococo::child_location_of(PenpalA::para_id()); let sov_of_sender_on_relay = Rococo::sovereign_account_id_of(sender_as_seen_by_relay); // fund Parachain's sender account PenpalA::mint_foreign_asset( <PenpalA as Chain>::RuntimeOrigin::signed(asset_owner), - relay_native_asset_location, + relay_native_asset_location.clone(), sender.clone(), amount_to_send * 2, ); @@ -1066,24 +1106,24 @@ fn reserve_transfer_native_asset_from_para_to_para_trough_relay() { // Query initial balances let sender_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender) }); let receiver_assets_before = PenpalB::execute_with(|| { type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &receiver) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &receiver) }); // Set assertions and dispatchables - test.set_assertion::<PenpalA>(para_to_para_through_relay_sender_assertions); + test.set_assertion::<PenpalA>(para_to_para_through_hop_sender_assertions); test.set_assertion::<Rococo>(para_to_para_relay_hop_assertions); - test.set_assertion::<PenpalB>(para_to_para_through_relay_receiver_assertions); + test.set_assertion::<PenpalB>(para_to_para_through_hop_receiver_assertions); test.set_dispatchable::<PenpalA>(para_to_para_through_relay_limited_reserve_transfer_assets); test.assert(); // Query final balances let sender_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender) }); let receiver_assets_after = PenpalB::execute_with(|| { type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs index e13300b7c11426416f543c2ee026702277b695b1..919e0080ba62d90a78a3738c4c8f141c01979feb 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs @@ -17,7 +17,10 @@ use crate::imports::*; #[test] fn swap_locally_on_chain_using_local_assets() { - let asset_native = Box::new(asset_hub_rococo_runtime::xcm_config::TokenLocationV3::get()); + let asset_native = Box::new( + v3::Location::try_from(asset_hub_rococo_runtime::xcm_config::TokenLocation::get()) + .expect("conversion works"), + ); let asset_one = Box::new(v3::Location::new( 0, [ @@ -112,10 +115,9 @@ fn swap_locally_on_chain_using_local_assets() { #[test] fn swap_locally_on_chain_using_foreign_assets() { - let asset_native = - Box::new(v3::Location::try_from(RelayLocation::get()).expect("conversion works")); + let asset_native = Box::new(v3::Location::try_from(RelayLocation::get()).unwrap()); let asset_location_on_penpal = - v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion works"); + v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).unwrap(); let foreign_asset_at_asset_hub_rococo = v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) @@ -228,11 +230,9 @@ fn swap_locally_on_chain_using_foreign_assets() { #[test] fn cannot_create_pool_from_pool_assets() { - let asset_native = Box::new(asset_hub_rococo_runtime::xcm_config::TokenLocationV3::get()); - let mut asset_one = asset_hub_rococo_runtime::xcm_config::PoolAssetsPalletLocationV3::get(); - asset_one - .append_with(v3::Junction::GeneralIndex(ASSET_ID.into())) - .expect("pool assets"); + let asset_native = asset_hub_rococo_runtime::xcm_config::TokenLocation::get(); + let mut asset_one = asset_hub_rococo_runtime::xcm_config::PoolAssetsPalletLocation::get(); + asset_one.append_with(GeneralIndex(ASSET_ID.into())).expect("pool assets"); AssetHubRococo::execute_with(|| { let pool_owner_account_id = asset_hub_rococo_runtime::AssetConversionOrigin::get(); @@ -255,8 +255,8 @@ fn cannot_create_pool_from_pool_assets() { assert_matches::assert_matches!( <AssetHubRococo as AssetHubRococoPallet>::AssetConversion::create_pool( <AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get()), - asset_native, - Box::new(asset_one), + Box::new(v3::Location::try_from(asset_native).expect("conversion works")), + Box::new(v3::Location::try_from(asset_one).expect("conversion works")), ), Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("Unknown")) ); @@ -265,7 +265,9 @@ fn cannot_create_pool_from_pool_assets() { #[test] fn pay_xcm_fee_with_some_asset_swapped_for_native() { - let asset_native = asset_hub_rococo_runtime::xcm_config::TokenLocationV3::get(); + let asset_native = + v3::Location::try_from(asset_hub_rococo_runtime::xcm_config::TokenLocation::get()) + .expect("conversion works"); let asset_one = xcm::v3::Location { parents: 0, interior: [ diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs index 1cbb7fb8c193accc65ef160a3a09514bd51debf5..f74378d7631a610a57c61153e62aedfcb588a611 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/teleport.rs @@ -110,8 +110,7 @@ fn para_dest_assertions(t: RelayToSystemParaTest) { fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); let expected_asset_id = t.args.asset_id.unwrap(); let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); @@ -204,8 +203,7 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); let checking_account = <PenpalA as PenpalAPallet>::PolkadotXcm::check_account(); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); PenpalA::assert_xcmp_queue_success(None); @@ -414,29 +412,28 @@ fn teleport_to_other_system_parachains_works() { ); } -/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets should work -/// (using native reserve-based transfer for fees) -#[test] -fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { +/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets while paying +/// fees using (reserve transferred) native asset. +pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( + para_to_ah_dispatchable: fn(ParaToSystemParaTest) -> DispatchResult, + ah_to_para_dispatchable: fn(SystemParaToParaTest) -> DispatchResult, +) { // Init values for Parachain let fee_amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000; - let asset_location_on_penpal = - v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion works"); + let asset_location_on_penpal = PenpalLocalTeleportableToAssetHub::get(); let asset_id_on_penpal = match asset_location_on_penpal.last() { - Some(v3::Junction::GeneralIndex(id)) => *id as u32, + Some(Junction::GeneralIndex(id)) => *id as u32, _ => unreachable!(), }; let asset_amount_to_send = ASSET_HUB_ROCOCO_ED * 1000; let asset_owner = PenpalAssetOwner::get(); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); let sender = PenpalASender::get(); let penpal_check_account = <PenpalA as PenpalAPallet>::PolkadotXcm::check_account(); let ah_as_seen_by_penpal = PenpalA::sibling_location_of(AssetHubRococo::para_id()); - let asset_location_on_penpal_latest: Location = asset_location_on_penpal.try_into().unwrap(); let penpal_assets: Assets = vec![ (Parent, fee_amount_to_send).into(), - (asset_location_on_penpal_latest, asset_amount_to_send).into(), + (asset_location_on_penpal.clone(), asset_amount_to_send).into(), ] .into(); let fee_asset_index = penpal_assets @@ -448,7 +445,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { // fund Parachain's sender account PenpalA::mint_foreign_asset( <PenpalA as Chain>::RuntimeOrigin::signed(asset_owner.clone()), - system_para_native_asset_location, + system_para_native_asset_location.clone(), sender.clone(), fee_amount_to_send * 2, ); @@ -472,7 +469,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { // Init values for System Parachain let foreign_asset_at_asset_hub_rococo = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); let penpal_to_ah_beneficiary_id = AssetHubRococoReceiver::get(); @@ -494,7 +491,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let penpal_sender_balance_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - system_para_native_asset_location, + system_para_native_asset_location.clone(), &PenpalASender::get(), ) }); @@ -508,20 +505,20 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let ah_receiver_assets_before = AssetHubRococo::execute_with(|| { type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; <Assets as Inspect<_>>::balance( - foreign_asset_at_asset_hub_rococo, + foreign_asset_at_asset_hub_rococo.clone().try_into().unwrap(), &AssetHubRococoReceiver::get(), ) }); penpal_to_ah.set_assertion::<PenpalA>(penpal_to_ah_foreign_assets_sender_assertions); penpal_to_ah.set_assertion::<AssetHubRococo>(penpal_to_ah_foreign_assets_receiver_assertions); - penpal_to_ah.set_dispatchable::<PenpalA>(para_to_system_para_transfer_assets); + penpal_to_ah.set_dispatchable::<PenpalA>(para_to_ah_dispatchable); penpal_to_ah.assert(); let penpal_sender_balance_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - system_para_native_asset_location, + system_para_native_asset_location.clone(), &PenpalASender::get(), ) }); @@ -535,7 +532,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let ah_receiver_assets_after = AssetHubRococo::execute_with(|| { type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; <Assets as Inspect<_>>::balance( - foreign_asset_at_asset_hub_rococo, + foreign_asset_at_asset_hub_rococo.clone().try_into().unwrap(), &AssetHubRococoReceiver::get(), ) }); @@ -563,19 +560,17 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; assert_ok!(ForeignAssets::transfer( <AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoReceiver::get()), - foreign_asset_at_asset_hub_rococo, + foreign_asset_at_asset_hub_rococo.clone().try_into().unwrap(), AssetHubRococoSender::get().into(), asset_amount_to_send, )); }); - let foreign_asset_at_asset_hub_rococo_latest: Location = - foreign_asset_at_asset_hub_rococo.try_into().unwrap(); let ah_to_penpal_beneficiary_id = PenpalAReceiver::get(); let penpal_as_seen_by_ah = AssetHubRococo::sibling_location_of(PenpalA::para_id()); let ah_assets: Assets = vec![ (Parent, fee_amount_to_send).into(), - (foreign_asset_at_asset_hub_rococo_latest, asset_amount_to_send).into(), + (foreign_asset_at_asset_hub_rococo.clone(), asset_amount_to_send).into(), ] .into(); let fee_asset_index = ah_assets @@ -603,7 +598,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let penpal_receiver_balance_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - system_para_native_asset_location, + system_para_native_asset_location.clone(), &PenpalAReceiver::get(), ) }); @@ -611,7 +606,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let ah_sender_assets_before = AssetHubRococo::execute_with(|| { type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - foreign_asset_at_asset_hub_rococo, + foreign_asset_at_asset_hub_rococo.clone().try_into().unwrap(), &AssetHubRococoSender::get(), ) }); @@ -622,7 +617,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { ah_to_penpal.set_assertion::<AssetHubRococo>(ah_to_penpal_foreign_assets_sender_assertions); ah_to_penpal.set_assertion::<PenpalA>(ah_to_penpal_foreign_assets_receiver_assertions); - ah_to_penpal.set_dispatchable::<AssetHubRococo>(system_para_to_para_transfer_assets); + ah_to_penpal.set_dispatchable::<AssetHubRococo>(ah_to_para_dispatchable); ah_to_penpal.assert(); let ah_sender_balance_after = ah_to_penpal.sender.balance; @@ -637,7 +632,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let ah_sender_assets_after = AssetHubRococo::execute_with(|| { type ForeignAssets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - foreign_asset_at_asset_hub_rococo, + foreign_asset_at_asset_hub_rococo.try_into().unwrap(), &AssetHubRococoSender::get(), ) }); @@ -660,3 +655,13 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { // Receiver's balance is increased by exact amount assert_eq!(penpal_receiver_assets_after, penpal_receiver_assets_before + asset_amount_to_send); } + +/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets should work +/// (using native reserve-based transfer for fees) +#[test] +fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { + do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( + para_to_system_para_transfer_assets, + system_para_to_para_transfer_assets, + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index c9f5fe0647e12ba0121261505e27ff56c3f82f96..e687251c14f9e2059787e817f6f480c738881e7b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -30,6 +30,7 @@ mod imports { prelude::{AccountId32 as AccountId32Junction, *}, v3, }; + pub use xcm_executor::traits::TransferType; // Cumulus pub use asset_test_utils::xcm_helpers; @@ -85,6 +86,7 @@ mod imports { pub type SystemParaToParaTest = Test<AssetHubWestend, PenpalA>; pub type ParaToSystemParaTest = Test<PenpalA, AssetHubWestend>; pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Westend>; + pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubWestend>; } #[cfg(test)] diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/foreign_assets_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/foreign_assets_transfers.rs new file mode 100644 index 0000000000000000000000000000000000000000..8cfda37c84c9495acce070bad7a42ad8ef058277 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/foreign_assets_transfers.rs @@ -0,0 +1,629 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::reserve_transfer::*; +use crate::{ + imports::*, + tests::teleport::do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt, +}; + +fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) { + type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent; + let sov_penpal_a_on_ah = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalA::para_id()), + ); + let sov_penpal_b_on_ah = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalB::para_id()), + ); + + assert_expected_events!( + AssetHubWestend, + vec![ + // Withdrawn from sender parachain SA + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == sov_penpal_a_on_ah, + amount: *amount == t.args.amount, + }, + // Deposited to receiver parachain SA + RuntimeEvent::Balances( + pallet_balances::Event::Minted { who, .. } + ) => { + who: *who == sov_penpal_b_on_ah, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + +fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + <AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::LocalReserve), + bx!(fee.id.into()), + bx!(TransferType::LocalReserve), + t.args.weight_limit, + ) +} + +fn para_to_ah_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + <PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::DestinationReserve), + bx!(fee.id.into()), + bx!(TransferType::DestinationReserve), + t.args.weight_limit, + ) +} + +fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubWestend::para_id()); + <PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())), + bx!(fee.id.into()), + bx!(TransferType::RemoteReserve(asset_hub_location.into())), + t.args.weight_limit, + ) +} + +fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + <PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::Teleport), + bx!(fee.id.into()), + bx!(TransferType::DestinationReserve), + t.args.weight_limit, + ) +} + +fn asset_hub_to_para_teleport_foreign_assets(t: SystemParaToParaTest) -> DispatchResult { + let fee_idx = t.args.fee_asset_item as usize; + let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap(); + <AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::transfer_assets_using_type( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + bx!(TransferType::Teleport), + bx!(fee.id.into()), + bx!(TransferType::LocalReserve), + t.args.weight_limit, + ) +} + +// =========================================================================== +// ======= Transfer - Native + Bridged Assets - AssetHub->Parachain ========== +// =========================================================================== +/// Transfers of native asset plus bridged asset from AssetHub to some Parachain +/// while paying fees using native asset. +#[test] +fn transfer_foreign_assets_from_asset_hub_to_para() { + let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sender = AssetHubWestendSender::get(); + let native_amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; + let native_asset_location = RelayLocation::get(); + let receiver = PenpalAReceiver::get(); + let assets_owner = PenpalAssetOwner::get(); + // Foreign asset used: bridged ROC + let foreign_amount_to_send = ASSET_HUB_WESTEND_ED * 10_000_000; + let roc_at_westend_parachains = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Rococo)]); + + // Configure destination chain to trust AH as reserve of ROC + PenpalA::execute_with(|| { + assert_ok!(<PenpalA as Chain>::System::set_storage( + <PenpalA as Chain>::RuntimeOrigin::root(), + vec![( + penpal_runtime::xcm_config::CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Rococo)]).encode(), + )], + )); + }); + PenpalA::force_create_foreign_asset( + roc_at_westend_parachains.clone(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + AssetHubWestend::force_create_foreign_asset( + roc_at_westend_parachains.clone().try_into().unwrap(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + AssetHubWestend::mint_foreign_asset( + <AssetHubWestend as Chain>::RuntimeOrigin::signed(assets_owner), + roc_at_westend_parachains.clone().try_into().unwrap(), + sender.clone(), + foreign_amount_to_send * 2, + ); + + // Assets to send + let assets: Vec<Asset> = vec![ + (Parent, native_amount_to_send).into(), + (roc_at_westend_parachains.clone(), foreign_amount_to_send).into(), + ]; + let fee_asset_id = AssetId(Parent.into()); + let fee_asset_item = assets.iter().position(|a| a.id == fee_asset_id).unwrap() as u32; + + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver.clone(), + native_amount_to_send, + assets.into(), + None, + fee_asset_item, + ), + }; + let mut test = SystemParaToParaTest::new(test_args); + + // Query initial balances + let sender_balance_before = test.sender.balance; + let sender_rocs_before = AssetHubWestend::execute_with(|| { + type ForeignAssets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + roc_at_westend_parachains.clone().try_into().unwrap(), + &sender, + ) + }); + let receiver_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(native_asset_location.clone(), &receiver) + }); + let receiver_rocs_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_at_westend_parachains.clone(), &receiver) + }); + + // Set assertions and dispatchables + test.set_assertion::<AssetHubWestend>(system_para_to_para_sender_assertions); + test.set_assertion::<PenpalA>(system_para_to_para_receiver_assertions); + test.set_dispatchable::<AssetHubWestend>(ah_to_para_transfer_assets); + test.assert(); + + // Query final balances + let sender_balance_after = test.sender.balance; + let sender_rocs_after = AssetHubWestend::execute_with(|| { + type ForeignAssets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + roc_at_westend_parachains.clone().try_into().unwrap(), + &sender, + ) + }); + let receiver_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(native_asset_location, &receiver) + }); + let receiver_rocs_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_at_westend_parachains, &receiver) + }); + + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_balance_after < sender_balance_before - native_amount_to_send); + // Sender's balance is reduced by foreign amount sent + assert_eq!(sender_rocs_after, sender_rocs_before - foreign_amount_to_send); + // Receiver's assets is increased + assert!(receiver_assets_after > receiver_assets_before); + // Receiver's assets increased by `amount_to_send - delivery_fees - bought_execution`; + // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but + // should be non-zero + assert!(receiver_assets_after < receiver_assets_before + native_amount_to_send); + // Receiver's balance is increased by foreign amount sent + assert_eq!(receiver_rocs_after, receiver_rocs_before + foreign_amount_to_send); +} + +/// Reserve Transfers of native asset from Parachain to System Parachain should work +// =========================================================================== +// ======= Transfer - Native + Bridged Assets - Parachain->AssetHub ========== +// =========================================================================== +/// Transfers of native asset plus bridged asset from some Parachain to AssetHub +/// while paying fees using native asset. +#[test] +fn transfer_foreign_assets_from_para_to_asset_hub() { + // Init values for Parachain + let destination = PenpalA::sibling_location_of(AssetHubWestend::para_id()); + let sender = PenpalASender::get(); + let native_amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 10000; + let native_asset_location = RelayLocation::get(); + let assets_owner = PenpalAssetOwner::get(); + + // Foreign asset used: bridged ROC + let foreign_amount_to_send = ASSET_HUB_WESTEND_ED * 10_000_000; + let roc_at_westend_parachains = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Rococo)]); + + // Configure destination chain to trust AH as reserve of ROC + PenpalA::execute_with(|| { + assert_ok!(<PenpalA as Chain>::System::set_storage( + <PenpalA as Chain>::RuntimeOrigin::root(), + vec![( + penpal_runtime::xcm_config::CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Rococo)]).encode(), + )], + )); + }); + PenpalA::force_create_foreign_asset( + roc_at_westend_parachains.clone(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + AssetHubWestend::force_create_foreign_asset( + roc_at_westend_parachains.clone().try_into().unwrap(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + <PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()), + native_asset_location.clone(), + sender.clone(), + native_amount_to_send * 2, + ); + PenpalA::mint_foreign_asset( + <PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()), + roc_at_westend_parachains.clone(), + sender.clone(), + foreign_amount_to_send * 2, + ); + + // Init values for System Parachain + let receiver = AssetHubWestendReceiver::get(); + let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ahr = + AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); + + // fund Parachain's SA on AssetHub with the assets held in reserve + AssetHubWestend::fund_accounts(vec![( + sov_penpal_on_ahr.clone().into(), + native_amount_to_send * 2, + )]); + AssetHubWestend::mint_foreign_asset( + <AssetHubWestend as Chain>::RuntimeOrigin::signed(assets_owner), + roc_at_westend_parachains.clone().try_into().unwrap(), + sov_penpal_on_ahr, + foreign_amount_to_send * 2, + ); + + // Assets to send + let assets: Vec<Asset> = vec![ + (Parent, native_amount_to_send).into(), + (roc_at_westend_parachains.clone(), foreign_amount_to_send).into(), + ]; + let fee_asset_id = AssetId(Parent.into()); + let fee_asset_item = assets.iter().position(|a| a.id == fee_asset_id).unwrap() as u32; + + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination.clone(), + receiver.clone(), + native_amount_to_send, + assets.into(), + None, + fee_asset_item, + ), + }; + let mut test = ParaToSystemParaTest::new(test_args); + + // Query initial balances + let sender_native_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(native_asset_location.clone(), &sender) + }); + let sender_rocs_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_at_westend_parachains.clone(), &sender) + }); + let receiver_native_before = test.receiver.balance; + let receiver_rocs_before = AssetHubWestend::execute_with(|| { + type ForeignAssets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + roc_at_westend_parachains.clone().try_into().unwrap(), + &receiver, + ) + }); + + // Set assertions and dispatchables + test.set_assertion::<PenpalA>(para_to_system_para_sender_assertions); + test.set_assertion::<AssetHubWestend>(para_to_system_para_receiver_assertions); + test.set_dispatchable::<PenpalA>(para_to_ah_transfer_assets); + test.assert(); + + // Query final balances + let sender_native_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(native_asset_location, &sender) + }); + let sender_rocs_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_at_westend_parachains.clone(), &sender) + }); + let receiver_native_after = test.receiver.balance; + let receiver_rocs_after = AssetHubWestend::execute_with(|| { + type ForeignAssets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + roc_at_westend_parachains.try_into().unwrap(), + &receiver, + ) + }); + + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_native_after < sender_native_before - native_amount_to_send); + // Sender's balance is reduced by foreign amount sent + assert_eq!(sender_rocs_after, sender_rocs_before - foreign_amount_to_send); + // Receiver's balance is increased + assert!(receiver_native_after > receiver_native_before); + // Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`; + // `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but + // should be non-zero + assert!(receiver_native_after < receiver_native_before + native_amount_to_send); + // Receiver's balance is increased by foreign amount sent + assert_eq!(receiver_rocs_after, receiver_rocs_before + foreign_amount_to_send); +} + +// ============================================================================== +// ===== Transfer - Native + Bridged Assets - Parachain->AssetHub->Parachain ==== +// ============================================================================== +/// Transfers of native asset plus bridged asset from Parachain to Parachain +/// (through AssetHub reserve) with fees paid using native asset. +#[test] +fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { + // Init values for Parachain Origin + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let wnd_to_send: Balance = WESTEND_ED * 10000; + let assets_owner = PenpalAssetOwner::get(); + let wnd_location = RelayLocation::get(); + let sender_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_ah = AssetHubWestend::sovereign_account_id_of(sender_as_seen_by_ah); + let receiver_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_ah = AssetHubWestend::sovereign_account_id_of(receiver_as_seen_by_ah); + let roc_to_send = ASSET_HUB_WESTEND_ED * 10_000_000; + + // Configure destination chain to trust AH as reserve of ROC + PenpalB::execute_with(|| { + assert_ok!(<PenpalB as Chain>::System::set_storage( + <PenpalB as Chain>::RuntimeOrigin::root(), + vec![( + penpal_runtime::xcm_config::CustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Rococo)]).encode(), + )], + )); + }); + + // Register ROC as foreign asset and transfer it around the Westend ecosystem + let roc_at_westend_parachains = + Location::new(2, [Junction::GlobalConsensus(NetworkId::Rococo)]); + AssetHubWestend::force_create_foreign_asset( + roc_at_westend_parachains.clone().try_into().unwrap(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + PenpalA::force_create_foreign_asset( + roc_at_westend_parachains.clone(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + PenpalB::force_create_foreign_asset( + roc_at_westend_parachains.clone(), + assets_owner.clone(), + false, + ASSET_MIN_BALANCE, + vec![], + ); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + <PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()), + wnd_location.clone(), + sender.clone(), + wnd_to_send * 2, + ); + PenpalA::mint_foreign_asset( + <PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()), + roc_at_westend_parachains.clone(), + sender.clone(), + roc_to_send * 2, + ); + // fund the Parachain Origin's SA on Asset Hub with the assets held in reserve + AssetHubWestend::fund_accounts(vec![(sov_of_sender_on_ah.clone().into(), wnd_to_send * 2)]); + AssetHubWestend::mint_foreign_asset( + <AssetHubWestend as Chain>::RuntimeOrigin::signed(assets_owner), + roc_at_westend_parachains.clone().try_into().unwrap(), + sov_of_sender_on_ah.clone(), + roc_to_send * 2, + ); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Assets to send + let assets: Vec<Asset> = vec![ + (wnd_location.clone(), wnd_to_send).into(), + (roc_at_westend_parachains.clone(), roc_to_send).into(), + ]; + let fee_asset_id: AssetId = wnd_location.clone().into(); + let fee_asset_item = assets.iter().position(|a| a.id == fee_asset_id).unwrap() as u32; + + // Init Test + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + wnd_to_send, + assets.into(), + None, + fee_asset_item, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // Query initial balances + let sender_wnds_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_location.clone(), &sender) + }); + let sender_rocs_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_at_westend_parachains.clone(), &sender) + }); + let wnds_in_sender_reserve_on_ah_before = + <AssetHubWestend as Chain>::account_data_of(sov_of_sender_on_ah.clone()).free; + let rocs_in_sender_reserve_on_ah_before = AssetHubWestend::execute_with(|| { + type Assets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + roc_at_westend_parachains.clone().try_into().unwrap(), + &sov_of_sender_on_ah, + ) + }); + let wnds_in_receiver_reserve_on_ah_before = + <AssetHubWestend as Chain>::account_data_of(sov_of_receiver_on_ah.clone()).free; + let rocs_in_receiver_reserve_on_ah_before = AssetHubWestend::execute_with(|| { + type Assets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + roc_at_westend_parachains.clone().try_into().unwrap(), + &sov_of_receiver_on_ah, + ) + }); + let receiver_wnds_before = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_location.clone(), &receiver) + }); + let receiver_rocs_before = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_at_westend_parachains.clone(), &receiver) + }); + + // Set assertions and dispatchables + test.set_assertion::<PenpalA>(para_to_para_through_hop_sender_assertions); + test.set_assertion::<AssetHubWestend>(para_to_para_assethub_hop_assertions); + test.set_assertion::<PenpalB>(para_to_para_through_hop_receiver_assertions); + test.set_dispatchable::<PenpalA>(para_to_para_transfer_assets_through_ah); + test.assert(); + + // Query final balances + let sender_wnds_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_location.clone(), &sender) + }); + let sender_rocs_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_at_westend_parachains.clone(), &sender) + }); + let rocs_in_sender_reserve_on_ah_after = AssetHubWestend::execute_with(|| { + type Assets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + roc_at_westend_parachains.clone().try_into().unwrap(), + &sov_of_sender_on_ah, + ) + }); + let wnds_in_sender_reserve_on_ah_after = + <AssetHubWestend as Chain>::account_data_of(sov_of_sender_on_ah).free; + let rocs_in_receiver_reserve_on_ah_after = AssetHubWestend::execute_with(|| { + type Assets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + roc_at_westend_parachains.clone().try_into().unwrap(), + &sov_of_receiver_on_ah, + ) + }); + let wnds_in_receiver_reserve_on_ah_after = + <AssetHubWestend as Chain>::account_data_of(sov_of_receiver_on_ah).free; + let receiver_wnds_after = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_location, &receiver) + }); + let receiver_rocs_after = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_at_westend_parachains, &receiver) + }); + + // Sender's balance is reduced by amount sent plus delivery fees + assert!(sender_wnds_after < sender_wnds_before - wnd_to_send); + assert_eq!(sender_rocs_after, sender_rocs_before - roc_to_send); + // Sovereign accounts on reserve are changed accordingly + assert_eq!( + wnds_in_sender_reserve_on_ah_after, + wnds_in_sender_reserve_on_ah_before - wnd_to_send + ); + assert_eq!( + rocs_in_sender_reserve_on_ah_after, + rocs_in_sender_reserve_on_ah_before - roc_to_send + ); + assert!(wnds_in_receiver_reserve_on_ah_after > wnds_in_receiver_reserve_on_ah_before); + assert_eq!( + rocs_in_receiver_reserve_on_ah_after, + rocs_in_receiver_reserve_on_ah_before + roc_to_send + ); + // Receiver's balance is increased + assert!(receiver_wnds_after > receiver_wnds_before); + assert_eq!(receiver_rocs_after, receiver_rocs_before + roc_to_send); +} + +// ============================================================================================== +// ==== Bidirectional Transfer - Native + Teleportable Foreign Assets - Parachain<->AssetHub ==== +// ============================================================================================== +/// Transfers of native asset plus teleportable foreign asset from Parachain to AssetHub and back +/// with fees paid using native asset. +#[test] +fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explicit_transfer_types() { + do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( + para_to_asset_hub_teleport_foreign_assets, + asset_hub_to_para_teleport_foreign_assets, + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs index 3cd7c9c46d69edd738f067724485824ef51d3259..e463e21e9e5295a7c33efc30006299ee2a801902 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs @@ -14,6 +14,7 @@ // limitations under the License. mod fellowship_treasury; +mod foreign_assets_transfers; mod reserve_transfer; mod send; mod set_xcm_versions; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index a26dfef8e8e702ee3f22870116adc03ee8ed1ca2..df01eb0d48ad929194e81808e36cf77528b54a21 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -47,7 +47,7 @@ fn para_to_relay_sender_assertions(t: ParaToRelayTest) { RuntimeEvent::ForeignAssets( pallet_assets::Event::Burned { asset_id, owner, balance, .. } ) => { - asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + asset_id: *asset_id == RelayLocation::get(), owner: *owner == t.sender.account_id, balance: *balance == t.args.amount, }, @@ -55,70 +55,92 @@ fn para_to_relay_sender_assertions(t: ParaToRelayTest) { ); } -fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { +pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent; - - AssetHubWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts( - 864_610_000, - 8_799, - ))); - + AssetHubWestend::assert_xcm_pallet_attempted_complete(None); + + let sov_acc_of_dest = AssetHubWestend::sovereign_account_id_of(t.args.dest.clone()); + for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() { + let expected_id = asset.id.0.clone().try_into().unwrap(); + let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); + if idx == t.args.fee_asset_item as usize { + assert_expected_events!( + AssetHubWestend, + vec![ + // Amount of native asset is transferred to Parachain's Sovereign account + RuntimeEvent::Balances( + pallet_balances::Event::Transfer { from, to, amount } + ) => { + from: *from == t.sender.account_id, + to: *to == sov_acc_of_dest, + amount: *amount == asset_amount, + }, + ] + ); + } else { + assert_expected_events!( + AssetHubWestend, + vec![ + // Amount of foreign asset is transferred to Parachain's Sovereign account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Transferred { asset_id, from, to, amount }, + ) => { + asset_id: *asset_id == expected_id, + from: *from == t.sender.account_id, + to: *to == sov_acc_of_dest, + amount: *amount == asset_amount, + }, + ] + ); + } + } assert_expected_events!( AssetHubWestend, vec![ - // Amount to reserve transfer is transferred to Parachain's Sovereign account - RuntimeEvent::Balances( - pallet_balances::Event::Transfer { from, to, amount } - ) => { - from: *from == t.sender.account_id, - to: *to == AssetHubWestend::sovereign_account_id_of( - t.args.dest.clone() - ), - amount: *amount == t.args.amount, - }, // Transport fees are paid - RuntimeEvent::PolkadotXcm( - pallet_xcm::Event::FeesPaid { .. } - ) => {}, + RuntimeEvent::PolkadotXcm(pallet_xcm::Event::FeesPaid { .. }) => {}, ] ); AssetHubWestend::assert_xcm_pallet_sent(); } -fn system_para_to_para_receiver_assertions(t: SystemParaToParaTest) { +pub fn system_para_to_para_receiver_assertions(t: SystemParaToParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); PenpalA::assert_xcmp_queue_success(None); - - assert_expected_events!( - PenpalA, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == system_para_native_asset_location, - owner: *owner == t.receiver.account_id, - }, - ] - ); + for asset in t.args.assets.into_inner().into_iter() { + let expected_id = asset.id.0.try_into().unwrap(); + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.receiver.account_id, + }, + ] + ); + } } -fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) { +pub fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799))); - assert_expected_events!( - PenpalA, - vec![ - // Amount to reserve transfer is transferred to Parachain's Sovereign account - RuntimeEvent::ForeignAssets( - pallet_assets::Event::Burned { asset_id, owner, balance, .. } - ) => { - asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), - owner: *owner == t.sender.account_id, - balance: *balance == t.args.amount, - }, - ] - ); + PenpalA::assert_xcm_pallet_attempted_complete(None); + for asset in t.args.assets.into_inner().into_iter() { + let expected_id = asset.id.0; + let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance } + ) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.sender.account_id, + balance: *balance == asset_amount, + }, + ] + ); + } } fn para_to_relay_receiver_assertions(t: ParaToRelayTest) { @@ -150,25 +172,57 @@ fn para_to_relay_receiver_assertions(t: ParaToRelayTest) { ); } -fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) { +pub fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent; - let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of( - AssetHubWestend::sibling_location_of(PenpalA::para_id()), - ); - AssetHubWestend::assert_xcmp_queue_success(None); + let sov_acc_of_penpal = AssetHubWestend::sovereign_account_id_of(t.args.dest.clone()); + for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() { + let expected_id = asset.id.0.clone().try_into().unwrap(); + let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); + if idx == t.args.fee_asset_item as usize { + assert_expected_events!( + AssetHubWestend, + vec![ + // Amount of native is withdrawn from Parachain's Sovereign account + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == sov_acc_of_penpal.clone().into(), + amount: *amount == asset_amount, + }, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == t.receiver.account_id, + }, + ] + ); + } else { + assert_expected_events!( + AssetHubWestend, + vec![ + // Amount of foreign asset is transferred from Parachain's Sovereign account + // to Receiver's account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance }, + ) => { + asset_id: *asset_id == expected_id, + owner: *owner == sov_acc_of_penpal, + balance: *balance == asset_amount, + }, + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Issued { asset_id, owner, amount }, + ) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.receiver.account_id, + amount: *amount == asset_amount, + }, + ] + ); + } + } assert_expected_events!( AssetHubWestend, vec![ - // Amount to reserve transfer is withdrawn from Parachain's Sovereign account - RuntimeEvent::Balances( - pallet_balances::Event::Burned { who, amount } - ) => { - who: *who == sov_penpal_on_ahr.clone().into(), - amount: *amount == t.args.amount, - }, - RuntimeEvent::Balances(pallet_balances::Event::Minted { .. }) => {}, RuntimeEvent::MessageQueue( pallet_message_queue::Event::Processed { success: true, .. } ) => {}, @@ -212,10 +266,8 @@ fn system_para_to_para_assets_sender_assertions(t: SystemParaToParaTest) { fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - let reservable_asset_location = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); + let reservable_asset_location = PenpalLocalReservableFromAssetHub::get(); PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8799))); assert_expected_events!( PenpalA, @@ -245,14 +297,13 @@ fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) { fn system_para_to_para_assets_receiver_assertions(t: SystemParaToParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - let system_para_asset_location = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let system_para_asset_location = PenpalLocalReservableFromAssetHub::get(); PenpalA::assert_xcmp_queue_success(None); assert_expected_events!( PenpalA, vec![ RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + asset_id: *asset_id == RelayLocation::get(), owner: *owner == t.receiver.account_id, }, RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, amount }) => { @@ -304,7 +355,7 @@ fn relay_to_para_assets_receiver_assertions(t: RelayToParaTest) { PenpalA, vec![ RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == v3::Location::try_from(RelayLocation::get()).expect("conversion works"), + asset_id: *asset_id == RelayLocation::get(), owner: *owner == t.receiver.account_id, }, RuntimeEvent::MessageQueue( @@ -314,29 +365,27 @@ fn relay_to_para_assets_receiver_assertions(t: RelayToParaTest) { ); } -fn para_to_para_through_relay_sender_assertions(t: ParaToParaThroughRelayTest) { +pub fn para_to_para_through_hop_sender_assertions<Hop: Clone>(t: Test<PenpalA, PenpalB, Hop>) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - - let relay_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - PenpalA::assert_xcm_pallet_attempted_complete(None); - // XCM sent to relay reserve - PenpalA::assert_parachain_system_ump_sent(); - assert_expected_events!( - PenpalA, - vec![ - // Amount to reserve transfer is transferred to Parachain's Sovereign account - RuntimeEvent::ForeignAssets( - pallet_assets::Event::Burned { asset_id, owner, balance }, - ) => { - asset_id: *asset_id == relay_asset_location, - owner: *owner == t.sender.account_id, - balance: *balance == t.args.amount, - }, - ] - ); + for asset in t.args.assets.into_inner() { + let expected_id = asset.id.0.clone().try_into().unwrap(); + let amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); + assert_expected_events!( + PenpalA, + vec![ + // Amount to reserve transfer is transferred to Parachain's Sovereign account + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance }, + ) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.sender.account_id, + balance: *balance == amount, + }, + ] + ); + } } fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { @@ -369,22 +418,22 @@ fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { ); } -fn para_to_para_through_relay_receiver_assertions(t: ParaToParaThroughRelayTest) { +pub fn para_to_para_through_hop_receiver_assertions<Hop: Clone>(t: Test<PenpalA, PenpalB, Hop>) { type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent; - let relay_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); PenpalB::assert_xcmp_queue_success(None); - - assert_expected_events!( - PenpalB, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { - asset_id: *asset_id == relay_asset_location, - owner: *owner == t.receiver.account_id, - }, - ] - ); + for asset in t.args.assets.into_inner().into_iter() { + let expected_id = asset.id.0.try_into().unwrap(); + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == expected_id, + owner: *owner == t.receiver.account_id, + }, + ] + ); + } } fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult { @@ -526,8 +575,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() { let amount_to_send: Balance = WESTEND_ED * 1000; // Init values fot Parachain - let relay_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let relay_native_asset_location = RelayLocation::get(); let receiver = PenpalAReceiver::get(); // Init Test @@ -542,7 +590,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() { let sender_balance_before = test.sender.balance; let receiver_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.into(), &receiver) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &receiver) }); // Set assertions and dispatchables @@ -555,7 +603,7 @@ fn reserve_transfer_native_asset_from_relay_to_para() { let sender_balance_after = test.sender.balance; let receiver_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.into(), &receiver) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &receiver) }); // Sender's balance is reduced by amount sent plus delivery fees @@ -577,13 +625,12 @@ fn reserve_transfer_native_asset_from_para_to_relay() { let amount_to_send: Balance = WESTEND_ED * 1000; let assets: Assets = (Parent, amount_to_send).into(); let asset_owner = PenpalAssetOwner::get(); - let relay_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let relay_native_asset_location = RelayLocation::get(); // fund Parachain's sender account PenpalA::mint_foreign_asset( <PenpalA as Chain>::RuntimeOrigin::signed(asset_owner), - relay_native_asset_location, + relay_native_asset_location.clone(), sender.clone(), amount_to_send * 2, ); @@ -614,7 +661,7 @@ fn reserve_transfer_native_asset_from_para_to_relay() { // Query initial balances let sender_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.into(), &sender) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; @@ -627,7 +674,7 @@ fn reserve_transfer_native_asset_from_para_to_relay() { // Query final balances let sender_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.into(), &sender) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &sender) }); let receiver_balance_after = test.receiver.balance; @@ -654,8 +701,7 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { let assets: Assets = (Parent, amount_to_send).into(); // Init values for Parachain - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); let receiver = PenpalAReceiver::get(); // Init Test @@ -677,7 +723,7 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { let sender_balance_before = test.sender.balance; let receiver_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.into(), &receiver) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &receiver) }); // Set assertions and dispatchables @@ -711,14 +757,13 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { let sender = PenpalASender::get(); let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000; let assets: Assets = (Parent, amount_to_send).into(); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); let asset_owner = PenpalAssetOwner::get(); // fund Parachain's sender account PenpalA::mint_foreign_asset( <PenpalA as Chain>::RuntimeOrigin::signed(asset_owner), - system_para_native_asset_location, + system_para_native_asset_location.clone(), sender.clone(), amount_to_send * 2, ); @@ -750,7 +795,7 @@ fn reserve_transfer_native_asset_from_para_to_system_para() { // Query initial balances let sender_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; @@ -818,10 +863,8 @@ fn reserve_transfer_assets_from_system_para_to_para() { // Init values for Parachain let receiver = PenpalAReceiver::get(); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - let system_para_foreign_asset_location = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); + let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); // Init Test let para_test_args = TestContext { @@ -846,11 +889,14 @@ fn reserve_transfer_assets_from_system_para_to_para() { }); let receiver_system_native_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location, &receiver) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &receiver) }); let receiver_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_foreign_asset_location, &receiver) + <ForeignAssets as Inspect<_>>::balance( + system_para_foreign_asset_location.clone(), + &receiver, + ) }); // Set assertions and dispatchables @@ -905,14 +951,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { let asset_amount_to_send = ASSET_HUB_WESTEND_ED * 100; let penpal_asset_owner = PenpalAssetOwner::get(); let penpal_asset_owner_signer = <PenpalA as Chain>::RuntimeOrigin::signed(penpal_asset_owner); - let asset_location_on_penpal = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); - let asset_location_on_penpal_latest: Location = asset_location_on_penpal.try_into().unwrap(); - let system_asset_location_on_penpal = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let asset_location_on_penpal = PenpalLocalReservableFromAssetHub::get(); + let system_asset_location_on_penpal = RelayLocation::get(); let assets: Assets = vec![ (Parent, fee_amount_to_send).into(), - (asset_location_on_penpal_latest, asset_amount_to_send).into(), + (asset_location_on_penpal.clone(), asset_amount_to_send).into(), ] .into(); let fee_asset_index = assets @@ -940,10 +983,8 @@ fn reserve_transfer_assets_from_para_to_system_para() { let penpal_location_as_seen_by_ahr = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(penpal_location_as_seen_by_ahr); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); - let system_para_foreign_asset_location = - v3::Location::try_from(PenpalLocalReservableFromAssetHub::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); + let system_para_foreign_asset_location = PenpalLocalReservableFromAssetHub::get(); let ah_asset_owner = AssetHubWestendAssetOwner::get(); let ah_asset_owner_signer = <AssetHubWestend as Chain>::RuntimeOrigin::signed(ah_asset_owner); @@ -978,11 +1019,11 @@ fn reserve_transfer_assets_from_para_to_system_para() { // Query initial balances let sender_system_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(system_para_native_asset_location.clone(), &sender) }); let sender_foreign_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(system_para_foreign_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(system_para_foreign_asset_location.clone(), &sender) }); let receiver_balance_before = test.receiver.balance; let receiver_assets_before = AssetHubWestend::execute_with(|| { @@ -1031,22 +1072,21 @@ fn reserve_transfer_assets_from_para_to_system_para() { /// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should /// work #[test] -fn reserve_transfer_native_asset_from_para_to_para_trough_relay() { +fn reserve_transfer_native_asset_from_para_to_para_through_relay() { // Init values for Parachain Origin let destination = PenpalA::sibling_location_of(PenpalB::para_id()); let sender = PenpalASender::get(); let amount_to_send: Balance = WESTEND_ED * 10000; let asset_owner = PenpalAssetOwner::get(); let assets = (Parent, amount_to_send).into(); - let relay_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let relay_native_asset_location = RelayLocation::get(); let sender_as_seen_by_relay = Westend::child_location_of(PenpalA::para_id()); let sov_of_sender_on_relay = Westend::sovereign_account_id_of(sender_as_seen_by_relay); // fund Parachain's sender account PenpalA::mint_foreign_asset( <PenpalA as Chain>::RuntimeOrigin::signed(asset_owner), - relay_native_asset_location, + relay_native_asset_location.clone(), sender.clone(), amount_to_send * 2, ); @@ -1068,24 +1108,24 @@ fn reserve_transfer_native_asset_from_para_to_para_trough_relay() { // Query initial balances let sender_assets_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender) }); let receiver_assets_before = PenpalB::execute_with(|| { type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &receiver) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &receiver) }); // Set assertions and dispatchables - test.set_assertion::<PenpalA>(para_to_para_through_relay_sender_assertions); + test.set_assertion::<PenpalA>(para_to_para_through_hop_sender_assertions); test.set_assertion::<Westend>(para_to_para_relay_hop_assertions); - test.set_assertion::<PenpalB>(para_to_para_through_relay_receiver_assertions); + test.set_assertion::<PenpalB>(para_to_para_through_hop_receiver_assertions); test.set_dispatchable::<PenpalA>(para_to_para_through_relay_limited_reserve_transfer_assets); test.assert(); // Query final balances let sender_assets_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; - <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &sender) + <ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender) }); let receiver_assets_after = PenpalB::execute_with(|| { type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs index aa673c03483af13cae2ac146049399644b265b6b..31f763be637079292d3b1aa49bbbfe5668d86653 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs @@ -17,7 +17,10 @@ use crate::imports::*; #[test] fn swap_locally_on_chain_using_local_assets() { - let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocationV3::get()); + let asset_native = Box::new( + v3::Location::try_from(asset_hub_westend_runtime::xcm_config::WestendLocation::get()) + .expect("conversion works"), + ); let asset_one = Box::new(v3::Location { parents: 0, interior: [ @@ -111,8 +114,7 @@ fn swap_locally_on_chain_using_local_assets() { #[test] fn swap_locally_on_chain_using_foreign_assets() { - let asset_native = - Box::new(v3::Location::try_from(RelayLocation::get()).expect("conversion works")); + let asset_native = Box::new(v3::Location::try_from(RelayLocation::get()).unwrap()); let asset_location_on_penpal = v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion_works"); let foreign_asset_at_asset_hub_westend = @@ -227,11 +229,9 @@ fn swap_locally_on_chain_using_foreign_assets() { #[test] fn cannot_create_pool_from_pool_assets() { - let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocationV3::get()); - let mut asset_one = asset_hub_westend_runtime::xcm_config::PoolAssetsPalletLocationV3::get(); - asset_one - .append_with(v3::Junction::GeneralIndex(ASSET_ID.into())) - .expect("pool assets"); + let asset_native = asset_hub_westend_runtime::xcm_config::WestendLocation::get(); + let mut asset_one = asset_hub_westend_runtime::xcm_config::PoolAssetsPalletLocation::get(); + asset_one.append_with(GeneralIndex(ASSET_ID.into())).expect("pool assets"); AssetHubWestend::execute_with(|| { let pool_owner_account_id = asset_hub_westend_runtime::AssetConversionOrigin::get(); @@ -254,8 +254,8 @@ fn cannot_create_pool_from_pool_assets() { assert_matches::assert_matches!( <AssetHubWestend as AssetHubWestendPallet>::AssetConversion::create_pool( <AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get()), - asset_native, - Box::new(asset_one), + Box::new(v3::Location::try_from(asset_native).expect("conversion works")), + Box::new(v3::Location::try_from(asset_one).expect("conversion works")), ), Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("Unknown")) ); @@ -264,7 +264,9 @@ fn cannot_create_pool_from_pool_assets() { #[test] fn pay_xcm_fee_with_some_asset_swapped_for_native() { - let asset_native = asset_hub_westend_runtime::xcm_config::WestendLocationV3::get(); + let asset_native = + v3::Location::try_from(asset_hub_westend_runtime::xcm_config::WestendLocation::get()) + .expect("conversion works"); let asset_one = xcm::v3::Location { parents: 0, interior: [ diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs index ac518d2ed4a445836364a23df313b319b8193e78..a524b87b2daf3a1352af1ea33b64282c2f4a8137 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/teleport.rs @@ -110,8 +110,7 @@ fn para_dest_assertions(t: RelayToSystemParaTest) { fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) { type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent; - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); let expected_asset_id = t.args.asset_id.unwrap(); let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); @@ -204,8 +203,7 @@ fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) { let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, t.args.fee_asset_item as usize).unwrap(); let checking_account = <PenpalA as PenpalAPallet>::PolkadotXcm::check_account(); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); PenpalA::assert_xcmp_queue_success(None); @@ -414,29 +412,28 @@ fn teleport_to_other_system_parachains_works() { ); } -/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets should work -/// (using native reserve-based transfer for fees) -#[test] -fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { +/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets while paying +/// fees using (reserve transferred) native asset. +pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( + para_to_ah_dispatchable: fn(ParaToSystemParaTest) -> DispatchResult, + ah_to_para_dispatchable: fn(SystemParaToParaTest) -> DispatchResult, +) { // Init values for Parachain let fee_amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 100; - let asset_location_on_penpal = - v3::Location::try_from(PenpalLocalTeleportableToAssetHub::get()).expect("conversion works"); + let asset_location_on_penpal = PenpalLocalTeleportableToAssetHub::get(); let asset_id_on_penpal = match asset_location_on_penpal.last() { - Some(v3::Junction::GeneralIndex(id)) => *id as u32, + Some(Junction::GeneralIndex(id)) => *id as u32, _ => unreachable!(), }; let asset_amount_to_send = ASSET_HUB_WESTEND_ED * 100; let asset_owner = PenpalAssetOwner::get(); - let system_para_native_asset_location = - v3::Location::try_from(RelayLocation::get()).expect("conversion works"); + let system_para_native_asset_location = RelayLocation::get(); let sender = PenpalASender::get(); let penpal_check_account = <PenpalA as PenpalAPallet>::PolkadotXcm::check_account(); let ah_as_seen_by_penpal = PenpalA::sibling_location_of(AssetHubWestend::para_id()); - let asset_location_on_penpal_latest: Location = asset_location_on_penpal.try_into().unwrap(); let penpal_assets: Assets = vec![ (Parent, fee_amount_to_send).into(), - (asset_location_on_penpal_latest, asset_amount_to_send).into(), + (asset_location_on_penpal.clone(), asset_amount_to_send).into(), ] .into(); let fee_asset_index = penpal_assets @@ -448,7 +445,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { // fund Parachain's sender account PenpalA::mint_foreign_asset( <PenpalA as Chain>::RuntimeOrigin::signed(asset_owner.clone()), - system_para_native_asset_location, + system_para_native_asset_location.clone(), sender.clone(), fee_amount_to_send * 2, ); @@ -475,7 +472,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { // Init values for System Parachain let foreign_asset_at_asset_hub_westend = - v3::Location::new(1, [v3::Junction::Parachain(PenpalA::para_id().into())]) + Location::new(1, [Junction::Parachain(PenpalA::para_id().into())]) .appended_with(asset_location_on_penpal) .unwrap(); let penpal_to_ah_beneficiary_id = AssetHubWestendReceiver::get(); @@ -497,7 +494,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let penpal_sender_balance_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - system_para_native_asset_location, + system_para_native_asset_location.clone(), &PenpalASender::get(), ) }); @@ -511,20 +508,20 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let ah_receiver_assets_before = AssetHubWestend::execute_with(|| { type Assets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; <Assets as Inspect<_>>::balance( - foreign_asset_at_asset_hub_westend, + foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), &AssetHubWestendReceiver::get(), ) }); penpal_to_ah.set_assertion::<PenpalA>(penpal_to_ah_foreign_assets_sender_assertions); penpal_to_ah.set_assertion::<AssetHubWestend>(penpal_to_ah_foreign_assets_receiver_assertions); - penpal_to_ah.set_dispatchable::<PenpalA>(para_to_system_para_transfer_assets); + penpal_to_ah.set_dispatchable::<PenpalA>(para_to_ah_dispatchable); penpal_to_ah.assert(); let penpal_sender_balance_after = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - system_para_native_asset_location, + system_para_native_asset_location.clone(), &PenpalASender::get(), ) }); @@ -538,7 +535,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let ah_receiver_assets_after = AssetHubWestend::execute_with(|| { type Assets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; <Assets as Inspect<_>>::balance( - foreign_asset_at_asset_hub_westend, + foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), &AssetHubWestendReceiver::get(), ) }); @@ -566,19 +563,17 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { type ForeignAssets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; assert_ok!(ForeignAssets::transfer( <AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendReceiver::get()), - foreign_asset_at_asset_hub_westend, + foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), AssetHubWestendSender::get().into(), asset_amount_to_send, )); }); - let foreign_asset_at_asset_hub_westend_latest: Location = - foreign_asset_at_asset_hub_westend.try_into().unwrap(); let ah_to_penpal_beneficiary_id = PenpalAReceiver::get(); let penpal_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalA::para_id()); let ah_assets: Assets = vec![ (Parent, fee_amount_to_send).into(), - (foreign_asset_at_asset_hub_westend_latest, asset_amount_to_send).into(), + (foreign_asset_at_asset_hub_westend.clone(), asset_amount_to_send).into(), ] .into(); let fee_asset_index = ah_assets @@ -606,7 +601,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let penpal_receiver_balance_before = PenpalA::execute_with(|| { type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - system_para_native_asset_location, + system_para_native_asset_location.clone(), &PenpalAReceiver::get(), ) }); @@ -614,7 +609,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let ah_sender_assets_before = AssetHubWestend::execute_with(|| { type ForeignAssets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - foreign_asset_at_asset_hub_westend, + foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), &AssetHubWestendSender::get(), ) }); @@ -625,7 +620,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { ah_to_penpal.set_assertion::<AssetHubWestend>(ah_to_penpal_foreign_assets_sender_assertions); ah_to_penpal.set_assertion::<PenpalA>(ah_to_penpal_foreign_assets_receiver_assertions); - ah_to_penpal.set_dispatchable::<AssetHubWestend>(system_para_to_para_transfer_assets); + ah_to_penpal.set_dispatchable::<AssetHubWestend>(ah_to_para_dispatchable); ah_to_penpal.assert(); let ah_sender_balance_after = ah_to_penpal.sender.balance; @@ -640,7 +635,7 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { let ah_sender_assets_after = AssetHubWestend::execute_with(|| { type ForeignAssets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; <ForeignAssets as Inspect<_>>::balance( - foreign_asset_at_asset_hub_westend, + foreign_asset_at_asset_hub_westend.clone().try_into().unwrap(), &AssetHubWestendSender::get(), ) }); @@ -663,3 +658,13 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { // Receiver's balance is increased by exact amount assert_eq!(penpal_receiver_assets_after, penpal_receiver_assets_before + asset_amount_to_send); } + +/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets should work +/// (using native reserve-based transfer for fees) +#[test] +fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { + do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt( + para_to_system_para_transfer_assets, + system_para_to_para_transfer_assets, + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index b5e19cf3fa3a252abe28b31229c876d104b6a8e9..0415af580ef8add90c92620e93052e356abe2de9 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -25,6 +25,7 @@ mod imports { prelude::{AccountId32 as AccountId32Junction, *}, v3::{self, NetworkId::Westend as WestendId}, }; + pub use xcm_executor::traits::TransferType; // Cumulus pub use emulated_integration_tests_common::{ @@ -46,7 +47,7 @@ mod imports { bridge_hub_rococo_emulated_chain::{ genesis::ED as BRIDGE_HUB_ROCOCO_ED, BridgeHubRococoParaPallet as BridgeHubRococoPallet, }, - penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet, + penpal_emulated_chain::{PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner}, rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs index 787a82ed32f7376f1c94c584711098c15d4da198..69d625be280454c4368d223fb5092be8df9de39d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs @@ -31,6 +31,73 @@ fn send_asset_from_asset_hub_rococo_to_asset_hub_westend(id: Location, amount: u assert_bridge_hub_westend_message_received(); } +fn send_asset_from_penpal_rococo_through_local_asset_hub_to_westend_asset_hub( + id: Location, + transfer_amount: u128, +) { + let destination = asset_hub_westend_location(); + let local_asset_hub: Location = PenpalA::sibling_location_of(AssetHubRococo::para_id()); + let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of( + AssetHubRococo::sibling_location_of(PenpalA::para_id()), + ); + let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( + Westend, + AssetHubWestend::para_id(), + ); + + // fund the AHR's SA on BHR for paying bridge transport fees + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), 10_000_000_000_000u128); + + // set XCM versions + PenpalA::force_xcm_version(local_asset_hub.clone(), XCM_VERSION); + AssetHubRococo::force_xcm_version(destination.clone(), XCM_VERSION); + BridgeHubRococo::force_xcm_version(bridge_hub_westend_location(), XCM_VERSION); + + // send message over bridge + assert_ok!(PenpalA::execute_with(|| { + let signed_origin = <PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get()); + let beneficiary: Location = + AccountId32Junction { network: None, id: AssetHubWestendReceiver::get().into() }.into(); + let assets: Assets = (id.clone(), transfer_amount).into(); + let fees_id: AssetId = id.into(); + + <PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type( + signed_origin, + bx!(destination.into()), + bx!(beneficiary.into()), + bx!(assets.clone().into()), + bx!(TransferType::RemoteReserve(local_asset_hub.clone().into())), + bx!(fees_id.into()), + bx!(TransferType::RemoteReserve(local_asset_hub.into())), + WeightLimit::Unlimited, + ) + })); + AssetHubRococo::execute_with(|| { + type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent; + assert_expected_events!( + AssetHubRococo, + vec![ + // Amount to reserve transfer is withdrawn from Penpal's sovereign account + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == sov_penpal_on_ahr.clone().into(), + amount: *amount == transfer_amount, + }, + // Amount deposited in AHW's sovereign account + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == sov_ahw_on_ahr.clone().into(), + }, + RuntimeEvent::XcmpQueue( + cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. } + ) => {}, + ] + ); + }); + assert_bridge_hub_rococo_message_accepted(true); + assert_bridge_hub_westend_message_received(); +} + #[test] fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() { let roc_at_asset_hub_rococo: v3::Location = v3::Parent.into(); @@ -45,7 +112,7 @@ fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() { vec![], ); let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - NetworkId::Westend, + Westend, AssetHubWestend::para_id(), ); @@ -101,9 +168,11 @@ fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() { <Assets as Inspect<_>>::balance(roc_at_asset_hub_westend, &AssetHubWestendReceiver::get()) }); - let roc_at_asset_hub_rococo_latest: Location = roc_at_asset_hub_rococo.try_into().unwrap(); let amount = ASSET_HUB_ROCOCO_ED * 1_000_000; - send_asset_from_asset_hub_rococo_to_asset_hub_westend(roc_at_asset_hub_rococo_latest, amount); + send_asset_from_asset_hub_rococo_to_asset_hub_westend( + roc_at_asset_hub_rococo.try_into().unwrap(), + amount, + ); AssetHubWestend::execute_with(|| { type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent; assert_expected_events!( @@ -135,7 +204,7 @@ fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() { assert!(sender_rocs_before > sender_rocs_after); // Receiver's balance is increased assert!(receiver_rocs_after > receiver_rocs_before); - // Reserve balance is reduced by sent amount + // Reserve balance is increased by sent amount assert_eq!(rocs_in_reserve_on_ahr_after, rocs_in_reserve_on_ahr_before + amount); } @@ -144,7 +213,7 @@ fn send_wnds_from_asset_hub_rococo_to_asset_hub_westend() { let prefund_amount = 10_000_000_000_000u128; let wnd_at_asset_hub_rococo = v3::Location::new(2, [v3::Junction::GlobalConsensus(v3::NetworkId::Westend)]); - let owner: AccountId = AssetHubWestend::account_id_of(ALICE); + let owner: AccountId = AssetHubRococo::account_id_of(ALICE); AssetHubRococo::force_create_foreign_asset( wnd_at_asset_hub_rococo, owner, @@ -155,7 +224,7 @@ fn send_wnds_from_asset_hub_rococo_to_asset_hub_westend() { // fund the AHR's SA on AHW with the WND tokens held in reserve let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( - NetworkId::Rococo, + Rococo, AssetHubRococo::para_id(), ); AssetHubWestend::fund_accounts(vec![(sov_ahr_on_ahw.clone(), prefund_amount)]); @@ -171,10 +240,9 @@ fn send_wnds_from_asset_hub_rococo_to_asset_hub_westend() { let receiver_wnds_before = <AssetHubWestend as Chain>::account_data_of(AssetHubWestendReceiver::get()).free; - let wnd_at_asset_hub_rococo_latest: Location = wnd_at_asset_hub_rococo.try_into().unwrap(); let amount_to_send = ASSET_HUB_WESTEND_ED * 1_000; send_asset_from_asset_hub_rococo_to_asset_hub_westend( - wnd_at_asset_hub_rococo_latest.clone(), + Location::try_from(wnd_at_asset_hub_rococo).unwrap(), amount_to_send, ); AssetHubWestend::execute_with(|| { @@ -217,3 +285,95 @@ fn send_wnds_from_asset_hub_rococo_to_asset_hub_westend() { // Reserve balance is reduced by sent amount assert_eq!(wnds_in_reserve_on_ahw_after, wnds_in_reserve_on_ahw_before - amount_to_send); } + +#[test] +fn send_rocs_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_westend() { + let roc_at_rococo_parachains: Location = Parent.into(); + let roc_at_asset_hub_westend = Location::new(2, [Junction::GlobalConsensus(NetworkId::Rococo)]); + let owner: AccountId = AssetHubWestend::account_id_of(ALICE); + AssetHubWestend::force_create_foreign_asset( + roc_at_asset_hub_westend.clone().try_into().unwrap(), + owner, + true, + ASSET_MIN_BALANCE, + vec![], + ); + let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( + Westend, + AssetHubWestend::para_id(), + ); + + let amount = ASSET_HUB_ROCOCO_ED * 10_000_000; + let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location); + // fund Penpal's sovereign account on AssetHub + AssetHubRococo::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount * 2)]); + // fund Penpal's sender account + PenpalA::mint_foreign_asset( + <PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()), + roc_at_rococo_parachains.clone(), + PenpalASender::get(), + amount * 2, + ); + + let rocs_in_reserve_on_ahr_before = + <AssetHubRococo as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free; + let sender_rocs_before = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + roc_at_rococo_parachains.clone(), + &PenpalASender::get(), + ) + }); + let receiver_rocs_before = AssetHubWestend::execute_with(|| { + type Assets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + roc_at_asset_hub_westend.clone().try_into().unwrap(), + &AssetHubWestendReceiver::get(), + ) + }); + send_asset_from_penpal_rococo_through_local_asset_hub_to_westend_asset_hub( + roc_at_rococo_parachains.clone(), + amount, + ); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![ + // issue ROCs on AHW + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == roc_at_rococo_parachains.clone().try_into().unwrap(), + owner: *owner == AssetHubWestendReceiver::get(), + }, + // message processed successfully + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + }); + + let sender_rocs_after = PenpalA::execute_with(|| { + type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(roc_at_rococo_parachains, &PenpalASender::get()) + }); + let receiver_rocs_after = AssetHubWestend::execute_with(|| { + type Assets = <AssetHubWestend as AssetHubWestendPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + roc_at_asset_hub_westend.try_into().unwrap(), + &AssetHubWestendReceiver::get(), + ) + }); + let rocs_in_reserve_on_ahr_after = + <AssetHubRococo as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free; + + // Sender's balance is reduced + assert!(sender_rocs_after < sender_rocs_before); + // Receiver's balance is increased + assert!(receiver_rocs_after > receiver_rocs_before); + // Reserve balance is increased by sent amount (less fess) + assert!(rocs_in_reserve_on_ahr_after > rocs_in_reserve_on_ahr_before); + assert!(rocs_in_reserve_on_ahr_after <= rocs_in_reserve_on_ahr_before + amount); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 780ba57f78a18c87c327db2acc5cd27442a0221b..e332eb5bfda7c0a05f618c66e6b65cbf10e6bffc 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -306,8 +306,6 @@ fn send_token_from_ethereum_to_penpal() { // The Weth asset location, identified by the contract address on Ethereum let weth_asset_location: Location = (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); - // Converts the Weth asset location into an asset ID - let weth_asset_id: v3::Location = weth_asset_location.try_into().unwrap(); let origin_location = (Parent, Parent, EthereumNetwork::get()).into(); @@ -321,12 +319,12 @@ fn send_token_from_ethereum_to_penpal() { PenpalA::execute_with(|| { assert_ok!(<PenpalA as PenpalAPallet>::ForeignAssets::create( <PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get()), - weth_asset_id, + weth_asset_location.clone(), asset_hub_sovereign.into(), 1000, )); - assert!(<PenpalA as PenpalAPallet>::ForeignAssets::asset_exists(weth_asset_id)); + assert!(<PenpalA as PenpalAPallet>::ForeignAssets::asset_exists(weth_asset_location)); }); BridgeHubRococo::execute_with(|| { @@ -381,10 +379,8 @@ fn send_token_from_ethereum_to_penpal() { #[test] fn send_weth_asset_from_asset_hub_to_ethereum() { use asset_hub_rococo_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; - let assethub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new( - 1, - [Parachain(AssetHubRococo::para_id().into())], - )); + let assethub_location = BridgeHubRococo::sibling_location_of(AssetHubRococo::para_id()); + let assethub_sovereign = BridgeHubRococo::sovereign_account_id_of(assethub_location); AssetHubRococo::force_default_xcm_version(Some(XCM_VERSION)); BridgeHubRococo::force_default_xcm_version(Some(XCM_VERSION)); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 60c31ce5a4aefeb5852f87ddd6804ca267857801..36b846e103131882e36b899bdb323d9b969cddde 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -26,6 +26,7 @@ mod imports { v3, v4::NetworkId::Rococo as RococoId, }; + pub use xcm_executor::traits::TransferType; // Cumulus pub use emulated_integration_tests_common::{ @@ -48,13 +49,15 @@ mod imports { genesis::ED as BRIDGE_HUB_WESTEND_ED, BridgeHubWestendParaPallet as BridgeHubWestendPallet, }, + penpal_emulated_chain::{PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet}, westend_emulated_chain::WestendRelayPallet as WestendPallet, AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver, AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, BridgeHubWestendPara as BridgeHubWestend, - BridgeHubWestendParaSender as BridgeHubWestendSender, WestendRelay as Westend, + BridgeHubWestendParaSender as BridgeHubWestendSender, PenpalBPara as PenpalB, + PenpalBParaSender as PenpalBSender, WestendRelay as Westend, }; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs index 5b0990973d2103f7fa606c4abcccd41a893067d2..3a8ce7d43f3e6da98fb2160a62c43c3964f0fe77 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs @@ -30,6 +30,73 @@ fn send_asset_from_asset_hub_westend_to_asset_hub_rococo(id: Location, amount: u assert_bridge_hub_rococo_message_received(); } +fn send_asset_from_penpal_westend_through_local_asset_hub_to_rococo_asset_hub( + id: Location, + transfer_amount: u128, +) { + let destination = asset_hub_rococo_location(); + let local_asset_hub: Location = PenpalB::sibling_location_of(AssetHubWestend::para_id()); + let sov_penpal_on_ahw = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalB::para_id()), + ); + let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( + Rococo, + AssetHubRococo::para_id(), + ); + + // fund the AHW's SA on BHW for paying bridge transport fees + BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), 10_000_000_000_000u128); + + // set XCM versions + PenpalB::force_xcm_version(local_asset_hub.clone(), XCM_VERSION); + AssetHubWestend::force_xcm_version(destination.clone(), XCM_VERSION); + BridgeHubWestend::force_xcm_version(bridge_hub_rococo_location(), XCM_VERSION); + + // send message over bridge + assert_ok!(PenpalB::execute_with(|| { + let signed_origin = <PenpalB as Chain>::RuntimeOrigin::signed(PenpalBSender::get()); + let beneficiary: Location = + AccountId32Junction { network: None, id: AssetHubRococoReceiver::get().into() }.into(); + let assets: Assets = (id.clone(), transfer_amount).into(); + let fees_id: AssetId = id.into(); + + <PenpalB as PenpalBPallet>::PolkadotXcm::transfer_assets_using_type( + signed_origin, + bx!(destination.into()), + bx!(beneficiary.into()), + bx!(assets.into()), + bx!(TransferType::RemoteReserve(local_asset_hub.clone().into())), + bx!(fees_id.into()), + bx!(TransferType::RemoteReserve(local_asset_hub.into())), + WeightLimit::Unlimited, + ) + })); + AssetHubWestend::execute_with(|| { + type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent; + assert_expected_events!( + AssetHubWestend, + vec![ + // Amount to reserve transfer is withdrawn from Penpal's sovereign account + RuntimeEvent::Balances( + pallet_balances::Event::Burned { who, amount } + ) => { + who: *who == sov_penpal_on_ahw.clone().into(), + amount: *amount == transfer_amount, + }, + // Amount deposited in AHR's sovereign account + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { + who: *who == sov_ahr_on_ahw.clone().into(), + }, + RuntimeEvent::XcmpQueue( + cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. } + ) => {}, + ] + ); + }); + assert_bridge_hub_westend_message_accepted(true); + assert_bridge_hub_rococo_message_received(); +} + #[test] fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { let wnd_at_asset_hub_westend: Location = Parent.into(); @@ -44,7 +111,7 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { vec![], ); let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( - NetworkId::Rococo, + Rococo, AssetHubRococo::para_id(), ); @@ -153,7 +220,7 @@ fn send_rocs_from_asset_hub_westend_to_asset_hub_rococo() { // fund the AHW's SA on AHR with the ROC tokens held in reserve let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( - NetworkId::Westend, + Westend, AssetHubWestend::para_id(), ); AssetHubRococo::fund_accounts(vec![(sov_ahw_on_ahr.clone(), prefund_amount)]); @@ -169,10 +236,9 @@ fn send_rocs_from_asset_hub_westend_to_asset_hub_rococo() { let receiver_rocs_before = <AssetHubRococo as Chain>::account_data_of(AssetHubRococoReceiver::get()).free; - let roc_at_asset_hub_westend_latest: Location = roc_at_asset_hub_westend.try_into().unwrap(); let amount_to_send = ASSET_HUB_ROCOCO_ED * 1_000; send_asset_from_asset_hub_westend_to_asset_hub_rococo( - roc_at_asset_hub_westend_latest.clone(), + roc_at_asset_hub_westend.try_into().unwrap(), amount_to_send, ); AssetHubRococo::execute_with(|| { @@ -215,3 +281,95 @@ fn send_rocs_from_asset_hub_westend_to_asset_hub_rococo() { // Reserve balance is reduced by sent amount assert_eq!(rocs_in_reserve_on_ahr_after, rocs_in_reserve_on_ahr_before - amount_to_send); } + +#[test] +fn send_wnds_from_penpal_westend_through_asset_hub_westend_to_asset_hub_rococo() { + let wnd_at_westend_parachains: Location = Parent.into(); + let wnd_at_asset_hub_rococo = Location::new(2, [Junction::GlobalConsensus(NetworkId::Westend)]); + let owner: AccountId = AssetHubRococo::account_id_of(ALICE); + AssetHubRococo::force_create_foreign_asset( + wnd_at_asset_hub_rococo.clone().try_into().unwrap(), + owner, + true, + ASSET_MIN_BALANCE, + vec![], + ); + let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( + Rococo, + AssetHubRococo::para_id(), + ); + + let amount = ASSET_HUB_WESTEND_ED * 10_000_000; + let penpal_location = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + let sov_penpal_on_ahw = AssetHubWestend::sovereign_account_id_of(penpal_location); + // fund Penpal's sovereign account on AssetHub + AssetHubWestend::fund_accounts(vec![(sov_penpal_on_ahw.into(), amount * 2)]); + // fund Penpal's sender account + PenpalB::mint_foreign_asset( + <PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()), + wnd_at_westend_parachains.clone(), + PenpalBSender::get(), + amount * 2, + ); + + let wnds_in_reserve_on_ahw_before = + <AssetHubWestend as Chain>::account_data_of(sov_ahr_on_ahw.clone()).free; + let sender_wnds_before = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance( + wnd_at_westend_parachains.clone(), + &PenpalBSender::get(), + ) + }); + let receiver_wnds_before = AssetHubRococo::execute_with(|| { + type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + wnd_at_asset_hub_rococo.clone().try_into().unwrap(), + &AssetHubRococoReceiver::get(), + ) + }); + send_asset_from_penpal_westend_through_local_asset_hub_to_rococo_asset_hub( + wnd_at_westend_parachains.clone(), + amount, + ); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent; + assert_expected_events!( + AssetHubRococo, + vec![ + // issue WNDs on AHR + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { asset_id, owner, .. }) => { + asset_id: *asset_id == wnd_at_westend_parachains.clone().try_into().unwrap(), + owner: *owner == AssetHubRococoReceiver::get(), + }, + // message processed successfully + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + }); + + let sender_wnds_after = PenpalB::execute_with(|| { + type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets; + <ForeignAssets as Inspect<_>>::balance(wnd_at_westend_parachains, &PenpalBSender::get()) + }); + let receiver_wnds_after = AssetHubRococo::execute_with(|| { + type Assets = <AssetHubRococo as AssetHubRococoPallet>::ForeignAssets; + <Assets as Inspect<_>>::balance( + wnd_at_asset_hub_rococo.try_into().unwrap(), + &AssetHubRococoReceiver::get(), + ) + }); + let wnds_in_reserve_on_ahw_after = + <AssetHubWestend as Chain>::account_data_of(sov_ahr_on_ahw.clone()).free; + + // Sender's balance is reduced + assert!(sender_wnds_after < sender_wnds_before); + // Receiver's balance is increased + assert!(receiver_wnds_after > receiver_wnds_before); + // Reserve balance is increased by sent amount (less fess) + assert!(wnds_in_reserve_on_ahw_after > wnds_in_reserve_on_ahw_before); + assert!(wnds_in_reserve_on_ahw_after <= wnds_in_reserve_on_ahw_before + amount); +} 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 6a46bc79b68ddf9de16eed71c9863c8a24c5464f..942f8cf8639c8680f9c65601eafb668508cf6f04 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -322,7 +322,7 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, LocalFromLeft< - AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocationV3>, + AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocationV3, xcm::v3::Location>, AssetIdForTrustBackedAssets, xcm::v3::Location, >, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 598c058cb28385bf92a15ddfb5224118391a429d..c464fec4edd6f9195693fdb399555dba78b3ca16 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -82,8 +82,6 @@ parameter_types! { PalletInstance(<PoolAssets as PalletInfoAccess>::index() as u8).into(); pub UniquesPalletLocation: Location = PalletInstance(<Uniques as PalletInfoAccess>::index() as u8).into(); - pub PoolAssetsPalletLocationV3: xcm::v3::Location = - xcm::v3::Junction::PalletInstance(<PoolAssets as PalletInfoAccess>::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub const GovernanceLocation: Location = Location::parent(); pub StakingPot: AccountId = CollatorSelection::account_id(); @@ -179,6 +177,7 @@ pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConverte StartsWithExplicitGlobalConsensus<UniversalLocationNetworkId>, ), Balance, + xcm::v3::Location, >; /// Means for transacting foreign assets from different global consensus. @@ -581,7 +580,11 @@ impl xcm_executor::Config for XcmConfig { WeightToFee, crate::NativeAndNonPoolAssets, ( - TrustBackedAssetsAsLocation<TrustBackedAssetsPalletLocation, Balance>, + TrustBackedAssetsAsLocation< + TrustBackedAssetsPalletLocation, + Balance, + xcm::v3::Location, + >, ForeignAssetsConvertedConcreteId, ), ResolveAssetTo<StakingPot, crate::NativeAndNonPoolAssets>, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index 5fa7455ad2a0b5620a6f6934b87acfe3b85d4f57..f670c5f424efeac0e00ddf472f1948e06d93bd68 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -22,8 +22,7 @@ use asset_hub_rococo_runtime::{ xcm_config::{ bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - LocationToAccountId, StakingPot, TokenLocation, TokenLocationV3, - TrustBackedAssetsPalletLocation, TrustBackedAssetsPalletLocationV3, XcmConfig, + LocationToAccountId, StakingPot, TokenLocation, TrustBackedAssetsPalletLocation, XcmConfig, }, AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, @@ -53,17 +52,14 @@ use sp_std::ops::Mul; use std::convert::Into; use testnet_parachains_constants::rococo::{consensus::*, currency::UNITS, fee::WeightToFee}; use xcm::latest::prelude::{Assets as XcmAssets, *}; -use xcm_builder::V4V3LocationConverter; +use xcm_builder::WithLatestLocationConverter; use xcm_executor::traits::{JustTry, WeightTrader}; const ALICE: [u8; 32] = [1u8; 32]; const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; type AssetIdForTrustBackedAssetsConvert = - assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocationV3>; - -type AssetIdForTrustBackedAssetsConvertLatest = - assets_common::AssetIdForTrustBackedAssetsConvertLatest<TrustBackedAssetsPalletLocation>; + assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>; type RuntimeHelper = asset_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>; @@ -204,7 +200,7 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { let bob: AccountId = SOME_ASSET_ADMIN.into(); let staking_pot = CollatorSelection::account_id(); let asset_1: u32 = 1; - let native_location = TokenLocationV3::get(); + let native_location = TokenLocation::get(); let asset_1_location = AssetIdForTrustBackedAssetsConvert::convert_back(&asset_1).unwrap(); // bob's initial balance for native and `asset1` assets. @@ -221,14 +217,24 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(asset_1_location) + Box::new( + xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") + ), + Box::new( + xcm::v3::Location::try_from(asset_1_location.clone()) + .expect("conversion works") + ) )); assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(asset_1_location), + Box::new( + xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") + ), + Box::new( + xcm::v3::Location::try_from(asset_1_location.clone()) + .expect("conversion works") + ), pool_liquidity, pool_liquidity, 1, @@ -240,8 +246,6 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { let asset_total_issuance = Assets::total_issuance(asset_1); let native_total_issuance = Balances::total_issuance(); - let asset_1_location_latest: Location = asset_1_location.try_into().unwrap(); - // prepare input to buy weight. let weight = Weight::from_parts(4_000_000_000, 0); let fee = WeightToFee::weight_to_fee(&weight); @@ -249,7 +253,7 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); let extra_amount = 100; let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - let payment: Asset = (asset_1_location_latest.clone(), asset_fee + extra_amount).into(); + let payment: Asset = (asset_1_location.clone(), asset_fee + extra_amount).into(); // init trader and buy weight. let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new(); @@ -257,24 +261,25 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); // assert. - let unused_amount = unused_asset - .fungible - .get(&asset_1_location_latest.clone().into()) - .map_or(0, |a| *a); + let unused_amount = + unused_asset.fungible.get(&asset_1_location.clone().into()).map_or(0, |a| *a); assert_eq!(unused_amount, extra_amount); assert_eq!(Assets::total_issuance(asset_1), asset_total_issuance + asset_fee); // prepare input to refund weight. let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); - let (reserve1, reserve2) = - AssetConversion::get_reserves(native_location, asset_1_location).unwrap(); + let (reserve1, reserve2) = AssetConversion::get_reserves( + xcm::v3::Location::try_from(native_location).expect("conversion works"), + xcm::v3::Location::try_from(asset_1_location.clone()).expect("conversion works"), + ) + .unwrap(); let asset_refund = AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); // refund. let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); - assert_eq!(actual_refund, (asset_1_location_latest, asset_refund).into()); + assert_eq!(actual_refund, (asset_1_location, asset_refund).into()); // assert. assert_eq!(Balances::balance(&staking_pot), initial_balance); @@ -303,7 +308,8 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { .execute_with(|| { let bob: AccountId = SOME_ASSET_ADMIN.into(); let staking_pot = CollatorSelection::account_id(); - let native_location = TokenLocationV3::get(); + let native_location = + xcm::v3::Location::try_from(TokenLocation::get()).expect("conversion works"); let foreign_location = xcm::v3::Location { parents: 1, interior: ( @@ -435,7 +441,7 @@ fn test_asset_xcm_take_first_trader() { // get asset id as location let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&local_asset_id).unwrap(); + AssetIdForTrustBackedAssetsConvert::convert_back(&local_asset_id).unwrap(); // Set Alice as block author, who will receive fees RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); @@ -603,9 +609,7 @@ fn test_asset_xcm_take_first_trader_with_refund() { // We are going to buy 4e9 weight let bought = Weight::from_parts(4_000_000_000u64, 0); - - let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap(); + let asset_location = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); // lets calculate amount needed let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -623,7 +627,7 @@ fn test_asset_xcm_take_first_trader_with_refund() { // We actually use half of the weight let weight_used = bought / 2; - // Make sure refurnd works. + // Make sure refund works. let amount_refunded = WeightToFee::weight_to_fee(&(bought - weight_used)); assert_eq!( @@ -677,8 +681,7 @@ fn test_asset_xcm_take_first_trader_refund_not_possible_since_amount_less_than_e // We are going to buy small amount let bought = Weight::from_parts(500_000_000u64, 0); - let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap(); + let asset_location = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -730,8 +733,7 @@ fn test_that_buying_ed_refund_does_not_refund_for_take_first_trader() { // We are gonna buy ED let bought = Weight::from_parts(ExistentialDeposit::get().try_into().unwrap(), 0); - let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap(); + let asset_location = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -807,8 +809,7 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { // lets calculate amount needed let asset_amount_needed = WeightToFee::weight_to_fee(&bought); - let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap(); + let asset_location = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); let asset: Asset = (asset_location, asset_amount_needed).into(); @@ -925,13 +926,16 @@ fn test_assets_balances_api_works() { ))); // check trusted asset assert!(result.inner().iter().any(|asset| asset.eq(&( - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&local_asset_id).unwrap(), + AssetIdForTrustBackedAssetsConvert::convert_back(&local_asset_id).unwrap(), minimum_asset_balance ) .into()))); // check foreign asset assert!(result.inner().iter().any(|asset| asset.eq(&( - V4V3LocationConverter::convert_back(&foreign_asset_id_location).unwrap(), + WithLatestLocationConverter::<xcm::v3::Location>::convert_back( + &foreign_asset_id_location + ) + .unwrap(), 6 * foreign_asset_minimum_asset_balance ) .into()))); @@ -1004,7 +1008,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ XcmConfig, TrustBackedAssetsInstance, AssetIdForTrustBackedAssets, - AssetIdForTrustBackedAssetsConvertLatest, + AssetIdForTrustBackedAssetsConvert, collator_session_keys(), ExistentialDeposit::get(), 12345, @@ -1044,7 +1048,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, xcm::v3::Location, - V4V3LocationConverter, + WithLatestLocationConverter<xcm::v3::Location>, collator_session_keys(), ExistentialDeposit::get(), AssetDeposit::get(), 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 f967ceaa616853ce7cc6954f6e3959b5eef93841..35b73d89dcde120cc1937230de7fdc8cab0e4034 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -302,7 +302,7 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, LocalFromLeft< - AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocationV3>, + AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocationV3, xcm::v3::Location>, AssetIdForTrustBackedAssets, xcm::v3::Location, >, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index b92419899395e93d05536d4785739bab4e528894..9ba07ccdc0388f26206526a100fc6e4a72ec370e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -79,8 +79,6 @@ parameter_types! { PalletInstance(<PoolAssets as PalletInfoAccess>::index() as u8).into(); pub UniquesPalletLocation: Location = PalletInstance(<Uniques as PalletInfoAccess>::index() as u8).into(); - pub PoolAssetsPalletLocationV3: xcm::v3::Location = - xcm::v3::Junction::PalletInstance(<PoolAssets as PalletInfoAccess>::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub StakingPot: AccountId = CollatorSelection::account_id(); pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); @@ -173,6 +171,7 @@ pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConverte StartsWithExplicitGlobalConsensus<UniversalLocationNetworkId>, ), Balance, + xcm::v3::Location, >; /// Means for transacting foreign assets from different global consensus. @@ -604,7 +603,11 @@ impl xcm_executor::Config for XcmConfig { WeightToFee, crate::NativeAndNonPoolAssets, ( - TrustBackedAssetsAsLocation<TrustBackedAssetsPalletLocation, Balance>, + TrustBackedAssetsAsLocation< + TrustBackedAssetsPalletLocation, + Balance, + xcm::v3::Location, + >, ForeignAssetsConvertedConcreteId, ), ResolveAssetTo<StakingPot, crate::NativeAndNonPoolAssets>, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 6696cb2322391c2dbad5f6c9a0afc4d5537de68c..b5957dd5df92ff1180909535cb6e604deadd8829 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -22,8 +22,8 @@ use asset_hub_westend_runtime::{ xcm_config::{ bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - LocationToAccountId, StakingPot, TrustBackedAssetsPalletLocation, - TrustBackedAssetsPalletLocationV3, WestendLocation, WestendLocationV3, XcmConfig, + LocationToAccountId, StakingPot, TrustBackedAssetsPalletLocation, WestendLocation, + XcmConfig, }, AllPalletsWithoutSystem, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, @@ -53,17 +53,14 @@ use sp_runtime::traits::MaybeEquivalence; use std::{convert::Into, ops::Mul}; use testnet_parachains_constants::westend::{consensus::*, currency::UNITS, fee::WeightToFee}; use xcm::latest::prelude::{Assets as XcmAssets, *}; -use xcm_builder::V4V3LocationConverter; +use xcm_builder::WithLatestLocationConverter; use xcm_executor::traits::{ConvertLocation, JustTry, WeightTrader}; const ALICE: [u8; 32] = [1u8; 32]; const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; type AssetIdForTrustBackedAssetsConvert = - assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocationV3>; - -type AssetIdForTrustBackedAssetsConvertLatest = - assets_common::AssetIdForTrustBackedAssetsConvertLatest<TrustBackedAssetsPalletLocation>; + assets_common::AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation>; type RuntimeHelper = asset_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>; @@ -204,7 +201,7 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { let bob: AccountId = SOME_ASSET_ADMIN.into(); let staking_pot = CollatorSelection::account_id(); let asset_1: u32 = 1; - let native_location = WestendLocationV3::get(); + let native_location = WestendLocation::get(); let asset_1_location = AssetIdForTrustBackedAssetsConvert::convert_back(&asset_1).unwrap(); // bob's initial balance for native and `asset1` assets. @@ -221,14 +218,24 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(asset_1_location) + Box::new( + xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") + ), + Box::new( + xcm::v3::Location::try_from(asset_1_location.clone()) + .expect("conversion works") + ) )); assert_ok!(AssetConversion::add_liquidity( RuntimeHelper::origin_of(bob.clone()), - Box::new(native_location), - Box::new(asset_1_location), + Box::new( + xcm::v3::Location::try_from(native_location.clone()).expect("conversion works") + ), + Box::new( + xcm::v3::Location::try_from(asset_1_location.clone()) + .expect("conversion works") + ), pool_liquidity, pool_liquidity, 1, @@ -240,8 +247,6 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { let asset_total_issuance = Assets::total_issuance(asset_1); let native_total_issuance = Balances::total_issuance(); - let asset_1_location_latest: Location = asset_1_location.try_into().unwrap(); - // prepare input to buy weight. let weight = Weight::from_parts(4_000_000_000, 0); let fee = WeightToFee::weight_to_fee(&weight); @@ -249,7 +254,7 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); let extra_amount = 100; let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - let payment: Asset = (asset_1_location_latest.clone(), asset_fee + extra_amount).into(); + let payment: Asset = (asset_1_location.clone(), asset_fee + extra_amount).into(); // init trader and buy weight. let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new(); @@ -257,24 +262,25 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); // assert. - let unused_amount = unused_asset - .fungible - .get(&asset_1_location_latest.clone().into()) - .map_or(0, |a| *a); + let unused_amount = + unused_asset.fungible.get(&asset_1_location.clone().into()).map_or(0, |a| *a); assert_eq!(unused_amount, extra_amount); assert_eq!(Assets::total_issuance(asset_1), asset_total_issuance + asset_fee); // prepare input to refund weight. let refund_weight = Weight::from_parts(1_000_000_000, 0); let refund = WeightToFee::weight_to_fee(&refund_weight); - let (reserve1, reserve2) = - AssetConversion::get_reserves(native_location, asset_1_location).unwrap(); + let (reserve1, reserve2) = AssetConversion::get_reserves( + xcm::v3::Location::try_from(native_location).expect("conversion works"), + xcm::v3::Location::try_from(asset_1_location.clone()).expect("conversion works"), + ) + .unwrap(); let asset_refund = AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); // refund. let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); - assert_eq!(actual_refund, (asset_1_location_latest, asset_refund).into()); + assert_eq!(actual_refund, (asset_1_location, asset_refund).into()); // assert. assert_eq!(Balances::balance(&staking_pot), initial_balance); @@ -303,7 +309,8 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { .execute_with(|| { let bob: AccountId = SOME_ASSET_ADMIN.into(); let staking_pot = CollatorSelection::account_id(); - let native_location = WestendLocationV3::get(); + let native_location = + xcm::v3::Location::try_from(WestendLocation::get()).expect("conversion works"); let foreign_location = xcm::v3::Location { parents: 1, interior: ( @@ -435,7 +442,7 @@ fn test_asset_xcm_take_first_trader() { // get asset id as location let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&local_asset_id).unwrap(); + AssetIdForTrustBackedAssetsConvert::convert_back(&local_asset_id).unwrap(); // Set Alice as block author, who will receive fees RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); @@ -599,8 +606,7 @@ fn test_asset_xcm_take_first_trader_with_refund() { // We are going to buy 4e9 weight let bought = Weight::from_parts(4_000_000_000u64, 0); - let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap(); + let asset_location = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); // lets calculate amount needed let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -672,8 +678,7 @@ fn test_asset_xcm_take_first_trader_refund_not_possible_since_amount_less_than_e // We are going to buy small amount let bought = Weight::from_parts(500_000_000u64, 0); - let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap(); + let asset_location = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -724,8 +729,7 @@ fn test_that_buying_ed_refund_does_not_refund_for_take_first_trader() { let bought = Weight::from_parts(500_000_000u64, 0); - let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap(); + let asset_location = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); let amount_bought = WeightToFee::weight_to_fee(&bought); @@ -801,8 +805,7 @@ fn test_asset_xcm_take_first_trader_not_possible_for_non_sufficient_assets() { // lets calculate amount needed let asset_amount_needed = WeightToFee::weight_to_fee(&bought); - let asset_location = - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap(); + let asset_location = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); let asset: Asset = (asset_location, asset_amount_needed).into(); @@ -923,13 +926,16 @@ fn test_assets_balances_api_works() { ))); // check trusted asset assert!(result.inner().iter().any(|asset| asset.eq(&( - AssetIdForTrustBackedAssetsConvertLatest::convert_back(&local_asset_id).unwrap(), + AssetIdForTrustBackedAssetsConvert::convert_back(&local_asset_id).unwrap(), minimum_asset_balance ) .into()))); // check foreign asset assert!(result.inner().iter().any(|asset| asset.eq(&( - V4V3LocationConverter::convert_back(&foreign_asset_id_location).unwrap(), + WithLatestLocationConverter::<xcm::v3::Location>::convert_back( + &foreign_asset_id_location + ) + .unwrap(), 6 * foreign_asset_minimum_asset_balance ) .into()))); @@ -1002,7 +1008,7 @@ asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_ XcmConfig, TrustBackedAssetsInstance, AssetIdForTrustBackedAssets, - AssetIdForTrustBackedAssetsConvertLatest, + AssetIdForTrustBackedAssetsConvert, collator_session_keys(), ExistentialDeposit::get(), 12345, @@ -1043,7 +1049,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, xcm::v3::Location, - V4V3LocationConverter, + WithLatestLocationConverter<xcm::v3::Location>, collator_session_keys(), ExistentialDeposit::get(), AssetDeposit::get(), diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs index 85e8ab15bab54497658e5a75d996001ee278dde2..1b99549619ad2eba865cb87fba9170c57c3979e7 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -26,36 +26,37 @@ pub mod runtime_api; use crate::matching::{LocalLocationPattern, ParentLocation}; use frame_support::traits::{Equals, EverythingBut}; use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId}; +use sp_runtime::traits::TryConvertInto; +use xcm::latest::Location; use xcm_builder::{ - AsPrefixedGeneralIndex, MatchedConvertedConcreteId, StartsWith, V4V3LocationConverter, + AsPrefixedGeneralIndex, MatchedConvertedConcreteId, StartsWith, WithLatestLocationConverter, }; -use xcm_executor::traits::JustTry; /// `Location` vs `AssetIdForTrustBackedAssets` converter for `TrustBackedAssets` -pub type AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation> = +pub type AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation, L = Location> = AsPrefixedGeneralIndex< TrustBackedAssetsPalletLocation, AssetIdForTrustBackedAssets, - JustTry, - xcm::v3::Location, + TryConvertInto, + L, >; -pub type AssetIdForTrustBackedAssetsConvertLatest<TrustBackedAssetsPalletLocation> = - AsPrefixedGeneralIndex<TrustBackedAssetsPalletLocation, AssetIdForTrustBackedAssets, JustTry>; - /// `Location` vs `CollectionId` converter for `Uniques` pub type CollectionIdForUniquesConvert<UniquesPalletLocation> = - AsPrefixedGeneralIndex<UniquesPalletLocation, CollectionId, JustTry>; + AsPrefixedGeneralIndex<UniquesPalletLocation, CollectionId, TryConvertInto>; /// [`MatchedConvertedConcreteId`] converter dedicated for `TrustBackedAssets` -pub type TrustBackedAssetsConvertedConcreteId<TrustBackedAssetsPalletLocation, Balance> = - MatchedConvertedConcreteId< - AssetIdForTrustBackedAssets, - Balance, - StartsWith<TrustBackedAssetsPalletLocation>, - AssetIdForTrustBackedAssetsConvertLatest<TrustBackedAssetsPalletLocation>, - JustTry, - >; +pub type TrustBackedAssetsConvertedConcreteId< + TrustBackedAssetsPalletLocation, + Balance, + L = Location, +> = MatchedConvertedConcreteId< + AssetIdForTrustBackedAssets, + Balance, + StartsWith<TrustBackedAssetsPalletLocation>, + AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation, L>, + TryConvertInto, +>; /// [`MatchedConvertedConcreteId`] converter dedicated for `Uniques` pub type UniquesConvertedConcreteId<UniquesPalletLocation> = MatchedConvertedConcreteId< @@ -65,28 +66,26 @@ pub type UniquesConvertedConcreteId<UniquesPalletLocation> = MatchedConvertedCon // junction within the pallet itself. StartsWith<UniquesPalletLocation>, CollectionIdForUniquesConvert<UniquesPalletLocation>, - JustTry, + TryConvertInto, >; -/// [`MatchedConvertedConcreteId`] converter dedicated for storing `AssetId` as `Location`. -pub type LocationConvertedConcreteId<LocationFilter, Balance> = MatchedConvertedConcreteId< - xcm::v3::Location, +/// [`MatchedConvertedConcreteId`] converter dedicated for `TrustBackedAssets`, +/// it is a similar implementation to `TrustBackedAssetsConvertedConcreteId`, +/// but it converts `AssetId` to `xcm::v*::Location` type instead of `AssetIdForTrustBackedAssets = +/// u32` +pub type TrustBackedAssetsAsLocation< + TrustBackedAssetsPalletLocation, Balance, - LocationFilter, - V4V3LocationConverter, - JustTry, + L, + LocationConverter = WithLatestLocationConverter<L>, +> = MatchedConvertedConcreteId< + L, + Balance, + StartsWith<TrustBackedAssetsPalletLocation>, + LocationConverter, + TryConvertInto, >; -/// [`MatchedConvertedConcreteId`] converter dedicated for `TrustBackedAssets` -pub type TrustBackedAssetsAsLocation<TrustBackedAssetsPalletLocation, Balance> = - MatchedConvertedConcreteId< - xcm::v3::Location, - Balance, - StartsWith<TrustBackedAssetsPalletLocation>, - V4V3LocationConverter, - JustTry, - >; - /// [`MatchedConvertedConcreteId`] converter dedicated for storing `ForeignAssets` with `AssetId` as /// `Location`. /// @@ -95,21 +94,29 @@ pub type TrustBackedAssetsAsLocation<TrustBackedAssetsPalletLocation, Balance> = /// - all local Locations /// /// `AdditionalLocationExclusionFilter` can customize additional excluded Locations -pub type ForeignAssetsConvertedConcreteId<AdditionalLocationExclusionFilter, Balance> = - LocationConvertedConcreteId< - EverythingBut<( - // Excludes relay/parent chain currency - Equals<ParentLocation>, - // Here we rely on fact that something like this works: - // assert!(Location::new(1, - // [Parachain(100)]).starts_with(&Location::parent())); - // assert!([Parachain(100)].into().starts_with(&Here)); - StartsWith<LocalLocationPattern>, - // Here we can exclude more stuff or leave it as `()` - AdditionalLocationExclusionFilter, - )>, - Balance, - >; +pub type ForeignAssetsConvertedConcreteId< + AdditionalLocationExclusionFilter, + Balance, + AssetId, + LocationToAssetIdConverter = WithLatestLocationConverter<AssetId>, + BalanceConverter = TryConvertInto, +> = MatchedConvertedConcreteId< + AssetId, + Balance, + EverythingBut<( + // Excludes relay/parent chain currency + Equals<ParentLocation>, + // Here we rely on fact that something like this works: + // assert!(Location::new(1, + // [Parachain(100)]).starts_with(&Location::parent())); + // assert!([Parachain(100)].into().starts_with(&Here)); + StartsWith<LocalLocationPattern>, + // Here we can exclude more stuff or leave it as `()` + AdditionalLocationExclusionFilter, + )>, + LocationToAssetIdConverter, + BalanceConverter, +>; pub type AssetIdForPoolAssets = u32; /// `Location` vs `AssetIdForPoolAssets` converter for `PoolAssets` with explicit `v3 Location`. @@ -122,7 +129,7 @@ pub type AssetIdForPoolAssetsConvertV3Location<PoolAssetsPalletLocation> = AsPre /// `Location` vs `AssetIdForPoolAssets` converter for `PoolAssets`. pub type AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation> = - AsPrefixedGeneralIndex<PoolAssetsPalletLocation, AssetIdForPoolAssets, JustTry>; + AsPrefixedGeneralIndex<PoolAssetsPalletLocation, AssetIdForPoolAssets, TryConvertInto>; /// [`MatchedConvertedConcreteId`] converter dedicated for `PoolAssets` pub type PoolAssetsConvertedConcreteId<PoolAssetsPalletLocation, Balance> = MatchedConvertedConcreteId< @@ -130,7 +137,7 @@ pub type PoolAssetsConvertedConcreteId<PoolAssetsPalletLocation, Balance> = Balance, StartsWith<PoolAssetsPalletLocation>, AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation>, - JustTry, + TryConvertInto, >; #[cfg(test)] @@ -138,7 +145,7 @@ mod tests { use super::*; use sp_runtime::traits::MaybeEquivalence; use xcm::prelude::*; - use xcm_builder::StartsWithExplicitGlobalConsensus; + use xcm_builder::{StartsWithExplicitGlobalConsensus, WithLatestLocationConverter}; use xcm_executor::traits::{Error as MatchError, MatchesFungibles}; #[test] @@ -151,14 +158,14 @@ mod tests { Location::new(5, [PalletInstance(13), GeneralIndex(local_asset_id.into())]); assert_eq!( - AssetIdForTrustBackedAssetsConvertLatest::<TrustBackedAssetsPalletLocation>::convert_back( + AssetIdForTrustBackedAssetsConvert::<TrustBackedAssetsPalletLocation>::convert_back( &local_asset_id ) .unwrap(), expected_reverse_ref ); assert_eq!( - AssetIdForTrustBackedAssetsConvertLatest::<TrustBackedAssetsPalletLocation>::convert( + AssetIdForTrustBackedAssetsConvert::<TrustBackedAssetsPalletLocation>::convert( &expected_reverse_ref ) .unwrap(), @@ -171,7 +178,7 @@ mod tests { frame_support::parameter_types! { pub TrustBackedAssetsPalletLocation: Location = Location::new(0, [PalletInstance(13)]); } - // setup convert + // set up a converter type TrustBackedAssetsConvert = TrustBackedAssetsConvertedConcreteId<TrustBackedAssetsPalletLocation, u128>; @@ -254,19 +261,21 @@ mod tests { } #[test] - fn location_converted_concrete_id_converter_works() { + fn foreign_assets_converted_concrete_id_converter_works() { frame_support::parameter_types! { pub Parachain100Pattern: Location = Location::new(1, [Parachain(100)]); pub UniversalLocationNetworkId: NetworkId = NetworkId::ByGenesis([9; 32]); } - // setup convert + // set up a converter which uses `xcm::v3::Location` under the hood type Convert = ForeignAssetsConvertedConcreteId< ( StartsWith<Parachain100Pattern>, StartsWithExplicitGlobalConsensus<UniversalLocationNetworkId>, ), u128, + xcm::v3::Location, + WithLatestLocationConverter<xcm::v3::Location>, >; let test_data = vec![ diff --git a/cumulus/parachains/runtimes/assets/common/src/matching.rs b/cumulus/parachains/runtimes/assets/common/src/matching.rs index 478bba4565dc1a6d8a45d47b1569b406596b6be7..3aad88e177caad1095a3dbe21dd3a3308b103680 100644 --- a/cumulus/parachains/runtimes/assets/common/src/matching.rs +++ b/cumulus/parachains/runtimes/assets/common/src/matching.rs @@ -113,17 +113,14 @@ impl<UniversalLocation: Get<InteriorLocation>, Reserves: ContainsPair<Asset, Loc ); // check remote origin - let _ = match ensure_is_remote(universal_source.clone(), origin.clone()) { - Ok(devolved) => devolved, - Err(_) => { - log::trace!( - target: "xcm::contains", - "IsTrustedBridgedReserveLocationForConcreteAsset origin: {:?} is not remote to the universal_source: {:?}", - origin, universal_source - ); - return false - }, - }; + if ensure_is_remote(universal_source.clone(), origin.clone()).is_err() { + log::trace!( + target: "xcm::contains", + "IsTrustedBridgedReserveLocationForConcreteAsset origin: {:?} is not remote to the universal_source: {:?}", + origin, universal_source + ); + return false + } // check asset according to the configured reserve locations Reserves::contains(asset, origin) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index 6dbf96edc2ab0360385b8e04bf1dc52732abd9ca..8845f0538b5c828f459431f1a01b54fefc98e9dc 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -29,6 +29,10 @@ use crate::{ use bp_messages::LaneId; use bp_runtime::Chain; use bridge_runtime_common::{ + extensions::refund_relayer_extension::{ + ActualFeeRefund, RefundBridgedGrandpaMessages, RefundSignedExtensionAdapter, + RefundableMessagesLane, + }, messages, messages::{ source::{FromBridgedChainMessagesDeliveryProof, TargetHeaderChainAdapter}, @@ -39,10 +43,6 @@ use bridge_runtime_common::{ SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, }, - refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedGrandpaMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, }; use frame_support::{parameter_types, traits::PalletInfoAccess}; @@ -273,7 +273,7 @@ mod tests { // Bulletin chain - it has the same (almost) runtime for Polkadot Bulletin and Rococo // Bulletin, so we have to adhere Polkadot names here - bridge_runtime_common::priority_calculator::ensure_priority_boost_is_sane::< + bridge_runtime_common::extensions::priority_calculator::ensure_priority_boost_is_sane::< Runtime, WithRococoBulletinMessagesInstance, PriorityBoostPerMessage, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index 5d55d7afbacfdb22f6939c88e87eaf64321945ff..e5a00073407f8f754f58ef88e825ca598b7bde94 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -28,6 +28,10 @@ use crate::{ use bp_messages::LaneId; use bp_runtime::Chain; use bridge_runtime_common::{ + extensions::refund_relayer_extension::{ + ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter, + RefundableMessagesLane, RefundableParachain, + }, messages, messages::{ source::{FromBridgedChainMessagesDeliveryProof, TargetHeaderChainAdapter}, @@ -38,10 +42,6 @@ use bridge_runtime_common::{ SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, }, - refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, RefundableParachain, - }, }; use codec::Encode; @@ -318,7 +318,7 @@ mod tests { }, }); - bridge_runtime_common::priority_calculator::ensure_priority_boost_is_sane::< + bridge_runtime_common::extensions::priority_calculator::ensure_priority_boost_is_sane::< Runtime, WithBridgeHubWestendMessagesInstance, PriorityBoostPerMessage, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index bce722aa5f87d006af0ec71429d6c84eeab4972d..d5da41cce2860c8b12db49f1defc5a6764bc6535 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -25,6 +25,10 @@ use bp_messages::LaneId; use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_runtime::Chain; use bridge_runtime_common::{ + extensions::refund_relayer_extension::{ + ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter, + RefundableMessagesLane, RefundableParachain, + }, messages, messages::{ source::{FromBridgedChainMessagesDeliveryProof, TargetHeaderChainAdapter}, @@ -35,10 +39,6 @@ use bridge_runtime_common::{ SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, }, - refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, RefundableParachain, - }, }; use codec::Encode; use frame_support::{ @@ -352,7 +352,7 @@ mod tests { }, }); - bridge_runtime_common::priority_calculator::ensure_priority_boost_is_sane::< + bridge_runtime_common::extensions::priority_calculator::ensure_priority_boost_is_sane::< Runtime, WithBridgeHubRococoMessagesInstance, PriorityBoostPerMessage, diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs index 171ac6a9528f134d9c22548500805ef36e9504f9..fcd786711bbe90096f2ef5b8d427cec23879027b 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs @@ -71,7 +71,7 @@ impl Config for Runtime { type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; type MaxDelegateDependencies = ConstU32<32>; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; - type Migrations = (); + type Migrations = (pallet_contracts::migration::v16::Migration<Runtime>,); type RuntimeHoldReason = RuntimeHoldReason; type Debug = (); type Environment = (); diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 3863ea5022f9142b230f5dba9a3840f858daa127..46fcbc6319c951efa91408ce310f3451002c8b77 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -78,7 +78,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -pub type CurrencyTransactor = FungibleAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -171,7 +171,7 @@ pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = CurrencyTransactor; + type AssetTransactor = FungibleTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = TrustedTeleporter; diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs index b0b276128272b66f5b5866f3e40bec957d717e42..7580ab33b8d63ae7f4510e784b22e60bea3b78da 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs @@ -77,7 +77,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting the native currency on this chain. -pub type CurrencyTransactor = FungibleAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -106,7 +106,7 @@ pub type RegionTransactor = NonFungibleAdapter< >; /// Means for transacting assets on this chain. -pub type AssetTransactors = (CurrencyTransactor, RegionTransactor); +pub type AssetTransactors = (FungibleTransactor, RegionTransactor); /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with XCM's `Transact`. There is an `OriginKind` that can diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 919bfe83e7d7aff8d90be3af0358dd28b93581e1..89885d77378ba2c1df13116b5f2d41f547002b30 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -72,18 +72,14 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm_config::XcmOriginToTransactDispatchOrigin; +use xcm_config::{ForeignAssetsAssetId, XcmOriginToTransactDispatchOrigin}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -// Polkadot imports +use parachains_common::{AccountId, Signature}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; - use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; - -// XCM Imports -use parachains_common::{AccountId, Signature}; use xcm::latest::prelude::{AssetId as AssetLocationId, BodyId}; /// Balance of an account. @@ -474,8 +470,8 @@ pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config<ForeignAssetsInstance> for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; - type AssetId = xcm::v3::Location; - type AssetIdParameter = xcm::v3::Location; + type AssetId = ForeignAssetsAssetId; + type AssetIdParameter = ForeignAssetsAssetId; type Currency = Balances; type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<AccountId>>; type ForceOrigin = EnsureRoot<AccountId>; diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index c12372abbe90f273f46f25863ccf08353d64e125..6832e2f4f4409b833cce74d691cb0442ec6a724f 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -40,7 +40,7 @@ use pallet_xcm::XcmPassthrough; use parachains_common::{xcm_config::AssetFeeAsExistentialDepositMultiplier, TREASURY_PALLET_ID}; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::{impls::ToAuthor, xcm_sender::ExponentialPrice}; -use sp_runtime::traits::{AccountIdConversion, ConvertInto}; +use sp_runtime::traits::{AccountIdConversion, ConvertInto, Identity, TryConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, @@ -58,9 +58,16 @@ parameter_types! { pub const RelayLocation: Location = Location::parent(); // Local native currency which is stored in `pallet_balances`` pub const PenpalNativeCurrency: Location = Location::here(); - pub const RelayNetwork: Option<NetworkId> = None; + // The Penpal runtime is utilized for testing with various environment setups. + // This storage item allows us to customize the `NetworkId` where Penpal is deployed. + // By default, it is set to `NetworkId::Rococo` and can be changed using `System::set_storage`. + pub storage RelayNetworkId: NetworkId = NetworkId::Westend; + pub RelayNetwork: Option<NetworkId> = Some(RelayNetworkId::get()); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = [Parachain(ParachainInfo::parachain_id().into())].into(); + pub UniversalLocation: InteriorLocation = [ + GlobalConsensus(RelayNetworkId::get()), + Parachain(ParachainInfo::parachain_id().into()) + ].into(); pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); } @@ -77,7 +84,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting assets on this chain. -pub type CurrencyTransactor = FungibleAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -124,7 +131,11 @@ pub type FungiblesTransactor = FungiblesAdapter< CheckingAccount, >; -pub type ForeignAssetsConvertedConcreteId = assets_common::LocationConvertedConcreteId< +// Using the latest `Location`, we don't need to worry about migrations for Penpal. +pub type ForeignAssetsAssetId = Location; +pub type ForeignAssetsConvertedConcreteId = xcm_builder::MatchedConvertedConcreteId< + Location, + Balance, EverythingBut<( // Here we rely on fact that something like this works: // assert!(Location::new(1, @@ -132,7 +143,8 @@ pub type ForeignAssetsConvertedConcreteId = assets_common::LocationConvertedConc // assert!([Parachain(100)].into().starts_with(&Here)); StartsWith<assets_common::matching::LocalLocationPattern>, )>, - Balance, + Identity, + TryConvertInto, >; /// Means for transacting foreign assets from different global consensus. @@ -152,7 +164,7 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< >; /// Means for transacting assets on this chain. -pub type AssetTransactors = (CurrencyTransactor, ForeignFungiblesTransactor, FungiblesTransactor); +pub type AssetTransactors = (FungibleTransactor, ForeignFungiblesTransactor, FungiblesTransactor); /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can @@ -409,8 +421,8 @@ impl cumulus_pallet_xcm::Config for Runtime { /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] -impl pallet_assets::BenchmarkHelper<xcm::v3::Location> for XcmBenchmarkHelper { - fn create_asset_id_parameter(id: u32) -> xcm::v3::Location { - xcm::v3::Location::new(1, [xcm::v3::Junction::Parachain(id)]) +impl pallet_assets::BenchmarkHelper<ForeignAssetsAssetId> for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> ForeignAssetsAssetId { + Location::new(1, [Parachain(id)]) } } diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 154c2c4600043cc0ceb1b0cd61a84949dcdd9616..df335368be1ca29d40ed4c9fd32b6e385b33b66e 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -346,7 +346,7 @@ pub type LocationToAccountId = ( ); /// Means for transacting assets on this chain. -pub type CurrencyTransactor = FungibleAdapter< +pub type FungibleTransactor = FungibleAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: @@ -385,7 +385,7 @@ pub type FungiblesTransactor = FungiblesAdapter< CheckingAccount, >; /// Means for transacting assets on this chain. -pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); +pub type AssetTransactors = (FungibleTransactor, FungiblesTransactor); /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile index 4bfb73acda05880ef49570594d0769d1e5e4b147..938f5cc45a1141a344c223a16a026c5b86494096 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:v2023-11-07-rococo-westend-initial-relayer +ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.2.1 # metadata ARG VCS_REF diff --git a/polkadot/node/core/runtime-api/src/cache.rs b/polkadot/node/core/runtime-api/src/cache.rs index acdb256ab36ca7392c0947237df6d7feb449247d..7cd1f7ce7281719a0875394871858c65b8f6045a 100644 --- a/polkadot/node/core/runtime-api/src/cache.rs +++ b/polkadot/node/core/runtime-api/src/cache.rs @@ -48,6 +48,7 @@ pub(crate) struct RequestResultCache { validation_code: LruMap<(Hash, ParaId, OccupiedCoreAssumption), Option<ValidationCode>>, validation_code_by_hash: LruMap<ValidationCodeHash, Option<ValidationCode>>, candidate_pending_availability: LruMap<(Hash, ParaId), Option<CommittedCandidateReceipt>>, + candidates_pending_availability: LruMap<(Hash, ParaId), Vec<CommittedCandidateReceipt>>, candidate_events: LruMap<Hash, Vec<CandidateEvent>>, session_executor_params: LruMap<SessionIndex, Option<ExecutorParams>>, session_info: LruMap<SessionIndex, SessionInfo>, @@ -86,6 +87,7 @@ impl Default for RequestResultCache { validation_code: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), validation_code_by_hash: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), candidate_pending_availability: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), + candidates_pending_availability: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), candidate_events: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), session_executor_params: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), session_info: LruMap::new(ByLength::new(DEFAULT_CACHE_CAP)), @@ -261,6 +263,21 @@ impl RequestResultCache { self.candidate_pending_availability.insert(key, value); } + pub(crate) fn candidates_pending_availability( + &mut self, + key: (Hash, ParaId), + ) -> Option<&Vec<CommittedCandidateReceipt>> { + self.candidates_pending_availability.get(&key).map(|v| &*v) + } + + pub(crate) fn cache_candidates_pending_availability( + &mut self, + key: (Hash, ParaId), + value: Vec<CommittedCandidateReceipt>, + ) { + self.candidates_pending_availability.insert(key, value); + } + pub(crate) fn candidate_events(&mut self, relay_parent: &Hash) -> Option<&Vec<CandidateEvent>> { self.candidate_events.get(relay_parent).map(|v| &*v) } @@ -591,4 +608,5 @@ pub(crate) enum RequestResult { AsyncBackingParams(Hash, async_backing::AsyncBackingParams), NodeFeatures(SessionIndex, NodeFeatures), ClaimQueue(Hash, BTreeMap<CoreIndex, VecDeque<ParaId>>), + CandidatesPendingAvailability(Hash, ParaId, Vec<CommittedCandidateReceipt>), } diff --git a/polkadot/node/core/runtime-api/src/lib.rs b/polkadot/node/core/runtime-api/src/lib.rs index 2b7f6fc2d609f4dfcdf4e94fb63bf499376f6d07..b7995aeeee761e489262504f017e1c79b677b875 100644 --- a/polkadot/node/core/runtime-api/src/lib.rs +++ b/polkadot/node/core/runtime-api/src/lib.rs @@ -133,6 +133,9 @@ where CandidatePendingAvailability(relay_parent, para_id, candidate) => self .requests_cache .cache_candidate_pending_availability((relay_parent, para_id), candidate), + CandidatesPendingAvailability(relay_parent, para_id, candidates) => self + .requests_cache + .cache_candidates_pending_availability((relay_parent, para_id), candidates), CandidateEvents(relay_parent, events) => self.requests_cache.cache_candidate_events(relay_parent, events), SessionExecutorParams(_relay_parent, session_index, index) => @@ -252,6 +255,9 @@ where Request::CandidatePendingAvailability(para, sender) => query!(candidate_pending_availability(para), sender) .map(|sender| Request::CandidatePendingAvailability(para, sender)), + Request::CandidatesPendingAvailability(para, sender) => + query!(candidates_pending_availability(para), sender) + .map(|sender| Request::CandidatesPendingAvailability(para, sender)), Request::CandidateEvents(sender) => query!(candidate_events(), sender).map(|sender| Request::CandidateEvents(sender)), Request::SessionExecutorParams(session_index, sender) => { @@ -531,6 +537,12 @@ where ver = 1, sender ), + Request::CandidatesPendingAvailability(para, sender) => query!( + CandidatesPendingAvailability, + candidates_pending_availability(para), + ver = Request::CANDIDATES_PENDING_AVAILABILITY_RUNTIME_REQUIREMENT, + sender + ), Request::CandidateEvents(sender) => { query!(CandidateEvents, candidate_events(), ver = 1, sender) }, diff --git a/polkadot/node/core/runtime-api/src/tests.rs b/polkadot/node/core/runtime-api/src/tests.rs index 73c661c40762effc2efbbfa66f7f46a47bbe2d16..0113de83c89ec56403fec0e42207bc5d4e0c552e 100644 --- a/polkadot/node/core/runtime-api/src/tests.rs +++ b/polkadot/node/core/runtime-api/src/tests.rs @@ -47,6 +47,7 @@ struct MockSubsystemClient { validation_outputs_results: HashMap<ParaId, bool>, session_index_for_child: SessionIndex, candidate_pending_availability: HashMap<ParaId, CommittedCandidateReceipt>, + candidates_pending_availability: HashMap<ParaId, Vec<CommittedCandidateReceipt>>, dmq_contents: HashMap<ParaId, Vec<InboundDownwardMessage>>, hrmp_channels: HashMap<ParaId, BTreeMap<ParaId, Vec<InboundHrmpMessage>>>, validation_code_by_hash: HashMap<ValidationCodeHash, ValidationCode>, @@ -140,6 +141,14 @@ impl RuntimeApiSubsystemClient for MockSubsystemClient { Ok(self.candidate_pending_availability.get(¶_id).cloned()) } + async fn candidates_pending_availability( + &self, + _: Hash, + para_id: ParaId, + ) -> Result<Vec<CommittedCandidateReceipt<Hash>>, ApiError> { + Ok(self.candidates_pending_availability.get(¶_id).cloned().unwrap_or_default()) + } + async fn candidate_events(&self, _: Hash) -> Result<Vec<CandidateEvent<Hash>>, ApiError> { Ok(self.candidate_events.clone()) } diff --git a/polkadot/node/service/chain-specs/paseo.json b/polkadot/node/service/chain-specs/paseo.json index 2e659716766ede8cf4ec2b183013b5e925dff998..19eefd328994ed67c269fafc06acefaf28c751bf 100644 --- a/polkadot/node/service/chain-specs/paseo.json +++ b/polkadot/node/service/chain-specs/paseo.json @@ -16,7 +16,9 @@ "/dns/boot.gatotech.network/tcp/33400/p2p/12D3KooWEvz5Ygv3MhCUNTVQbUTVhzhvf4KKcNoe5M5YbVLPBeeW", "/dns/boot.gatotech.network/tcp/35400/wss/p2p/12D3KooWEvz5Ygv3MhCUNTVQbUTVhzhvf4KKcNoe5M5YbVLPBeeW", "/dns/paseo-bootnode.turboflakes.io/tcp/30630/p2p/12D3KooWMjCN2CrnN71hAdehn6M2iYKeGdGbZ1A3SKhf4hxrgG9e", - "/dns/paseo-bootnode.turboflakes.io/tcp/30730/wss/p2p/12D3KooWMjCN2CrnN71hAdehn6M2iYKeGdGbZ1A3SKhf4hxrgG9e" + "/dns/paseo-bootnode.turboflakes.io/tcp/30730/wss/p2p/12D3KooWMjCN2CrnN71hAdehn6M2iYKeGdGbZ1A3SKhf4hxrgG9e", + "/dns/paseo-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWBLLFKDGBxCwq3QmU3YwWKXUx953WwprRshJQicYu4Cfr", + "/dns/paseo-boot-ng.dwellir.com/tcp/30354/p2p/12D3KooWBLLFKDGBxCwq3QmU3YwWKXUx953WwprRshJQicYu4Cfr" ], "telemetryEndpoints": null, "protocolId": "pas", @@ -419,4 +421,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 2ca6728af012166649ccfee136b098cff9b9fe4a..e75d80395c4ba0371b58ce1383db2a1f364eaad8 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -670,7 +670,7 @@ pub enum RuntimeApiRequest { /// Get validation code by its hash, either past, current or future code can be returned, as /// long as state is still available. ValidationCodeByHash(ValidationCodeHash, RuntimeApiSender<Option<ValidationCode>>), - /// Get a the candidate pending availability for a particular parachain by parachain / core + /// Get the candidate pending availability for a particular parachain by parachain / core /// index CandidatePendingAvailability(ParaId, RuntimeApiSender<Option<CommittedCandidateReceipt>>), /// Get all events concerning candidates (backing, inclusion, time-out) in the parent of @@ -739,6 +739,9 @@ pub enum RuntimeApiRequest { /// Fetch the `ClaimQueue` from scheduler pallet /// `V11` ClaimQueue(RuntimeApiSender<BTreeMap<CoreIndex, VecDeque<ParaId>>>), + /// Get the candidates pending availability for a particular parachain + /// `V11` + CandidatesPendingAvailability(ParaId, RuntimeApiSender<Vec<CommittedCandidateReceipt>>), } impl RuntimeApiRequest { @@ -776,6 +779,9 @@ impl RuntimeApiRequest { /// `ClaimQueue` pub const CLAIM_QUEUE_RUNTIME_REQUIREMENT: u32 = 11; + + /// `candidates_pending_availability` + pub const CANDIDATES_PENDING_AVAILABILITY_RUNTIME_REQUIREMENT: u32 = 11; } /// A message to the Runtime API subsystem. diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs index 664d10ed1af5fdb77c3105ab995ab5ff89df2220..e5e1e4d24ef96e8fa7689a41cd7897832b5aa271 100644 --- a/polkadot/node/subsystem-types/src/runtime_client.rs +++ b/polkadot/node/subsystem-types/src/runtime_client.rs @@ -333,6 +333,14 @@ pub trait RuntimeApiSubsystemClient { // == v11: Claim queue == /// Fetch the `ClaimQueue` from scheduler pallet async fn claim_queue(&self, at: Hash) -> Result<BTreeMap<CoreIndex, VecDeque<Id>>, ApiError>; + + // == v11: Elastic scaling support == + /// Get the receipts of all candidates pending availability for a `ParaId`. + async fn candidates_pending_availability( + &self, + at: Hash, + para_id: Id, + ) -> Result<Vec<CommittedCandidateReceipt<Hash>>, ApiError>; } /// Default implementation of [`RuntimeApiSubsystemClient`] using the client. @@ -428,6 +436,14 @@ where self.client.runtime_api().candidate_pending_availability(at, para_id) } + async fn candidates_pending_availability( + &self, + at: Hash, + para_id: Id, + ) -> Result<Vec<CommittedCandidateReceipt<Hash>>, ApiError> { + self.client.runtime_api().candidates_pending_availability(at, para_id) + } + async fn candidate_events(&self, at: Hash) -> Result<Vec<CandidateEvent<Hash>>, ApiError> { self.client.runtime_api().candidate_events(at) } diff --git a/polkadot/node/subsystem-util/src/lib.rs b/polkadot/node/subsystem-util/src/lib.rs index 83b046f0bf0ac54533958da36dc230e8c8ce2922..b93818070a183e43e71f327bcc1770a71fae1cb6 100644 --- a/polkadot/node/subsystem-util/src/lib.rs +++ b/polkadot/node/subsystem-util/src/lib.rs @@ -296,6 +296,7 @@ specialize_requests! { fn request_validation_code(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<ValidationCode>; ValidationCode; fn request_validation_code_by_hash(validation_code_hash: ValidationCodeHash) -> Option<ValidationCode>; ValidationCodeByHash; fn request_candidate_pending_availability(para_id: ParaId) -> Option<CommittedCandidateReceipt>; CandidatePendingAvailability; + fn request_candidates_pending_availability(para_id: ParaId) -> Vec<CommittedCandidateReceipt>; CandidatesPendingAvailability; fn request_candidate_events() -> Vec<CandidateEvent>; CandidateEvents; fn request_session_info(index: SessionIndex) -> Option<SessionInfo>; SessionInfo; fn request_validation_code_hash(para_id: ParaId, assumption: OccupiedCoreAssumption) diff --git a/polkadot/primitives/src/runtime_api.rs b/polkadot/primitives/src/runtime_api.rs index f611936f2701d367283503e761176e9b61e72cbe..7bd92be35c159db35b3db7b9dd91afcc73eebd81 100644 --- a/polkadot/primitives/src/runtime_api.rs +++ b/polkadot/primitives/src/runtime_api.rs @@ -288,5 +288,10 @@ sp_api::decl_runtime_apis! { /// Claim queue #[api_version(11)] fn claim_queue() -> BTreeMap<CoreIndex, VecDeque<ppp::Id>>; + + /***** Added in v11 *****/ + /// Elastic scaling support + #[api_version(11)] + fn candidates_pending_availability(para_id: ppp::Id) -> Vec<CommittedCandidateReceipt<Hash>>; } } diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-pending-availability.md b/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-pending-availability.md index e118757d83cede12468be2d8d444958ca847eadf..b9f03748d89bafaad448b7515f08eb73a44b0996 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-pending-availability.md +++ b/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-pending-availability.md @@ -4,5 +4,8 @@ Get the receipt of a candidate pending availability. This returns `Some` for any `availability_cores` and `None` otherwise. ```rust +// Deprectated. fn candidate_pending_availability(at: Block, ParaId) -> Option<CommittedCandidateReceipt>; +// Use this one +fn candidates_pending_availability(at: Block, ParaId) -> Vec<CommittedCandidateReceipt>; ``` diff --git a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md index fd74f33253b72bd629313b5763e2cbbed75a6314..0700a781d426324c2c35e72628caa65b577ef979 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md @@ -154,6 +154,8 @@ All failed checks should lead to an unrecoverable error making the block invalid where the changes to the state are expected to be discarded directly after. * `candidate_pending_availability(ParaId) -> Option<CommittedCandidateReceipt>`: returns the `CommittedCandidateReceipt` pending availability for the para provided, if any. +* `candidates_pending_availability(ParaId) -> Vec<CommittedCandidateReceipt>`: returns the `CommittedCandidateReceipt`s + pending availability for the para provided, if any. * `pending_availability(ParaId) -> Option<CandidatePendingAvailability>`: returns the metadata around the candidate pending availability for the para, if any. * `free_disputed(disputed: Vec<CandidateHash>) -> Vec<CoreIndex>`: Sweeps through all paras pending availability. If @@ -164,10 +166,10 @@ These functions were formerly part of the UMP pallet: * `check_upward_messages(P: ParaId, Vec<UpwardMessage>)`: 1. Checks that the parachain is not currently offboarding and error otherwise. - 1. Checks that there are at most `config.max_upward_message_num_per_candidate` messages to be enqueued. - 1. Checks that no message exceeds `config.max_upward_message_size`. - 1. Checks that the total resulting queue size would not exceed `co`. - 1. Verify that queuing up the messages could not result in exceeding the queue's footprint according to the config + 2. Checks that there are at most `config.max_upward_message_num_per_candidate` messages to be enqueued. + 3. Checks that no message exceeds `config.max_upward_message_size`. + 4. Checks that the total resulting queue size would not exceed `co`. + 5. Verify that queuing up the messages could not result in exceeding the queue's footprint according to the config items `config.max_upward_queue_count` and `config.max_upward_queue_size`. The queue's current footprint is provided in `well_known_keys` in order to facilitate oraclisation on to the para. diff --git a/polkadot/runtime/parachains/src/hrmp.rs b/polkadot/runtime/parachains/src/hrmp.rs index 05a540aef828b95b602439c89d6e44ad95c3174a..65652b38577b361353aca1920255e9788c838028 100644 --- a/polkadot/runtime/parachains/src/hrmp.rs +++ b/polkadot/runtime/parachains/src/hrmp.rs @@ -65,6 +65,7 @@ pub trait WeightInfo { fn force_open_hrmp_channel(c: u32) -> Weight; fn establish_system_channel() -> Weight; fn poke_channel_deposits() -> Weight; + fn establish_channel_with_system() -> Weight; } /// A weight info that is only suitable for testing. @@ -104,6 +105,9 @@ impl WeightInfo for TestWeightInfo { fn poke_channel_deposits() -> Weight { Weight::MAX } + fn establish_channel_with_system() -> Weight { + Weight::MAX + } } /// A description of a request to open an HRMP channel. @@ -270,6 +274,10 @@ pub mod pallet { /// implementation should be the same as `Balance` as used in the `Configuration`. type Currency: ReservableCurrency<Self::AccountId>; + /// The default channel size and capacity to use when opening a channel to a system + /// parachain. + type DefaultChannelSizeAndCapacityWithSystem: Get<(u32, u32)>; + /// Something that provides the weight of this pallet. type WeightInfo: WeightInfo; } @@ -297,7 +305,7 @@ pub mod pallet { proposed_max_capacity: u32, proposed_max_message_size: u32, }, - /// An HRMP channel was opened between two system chains. + /// An HRMP channel was opened with a system chain. HrmpSystemChannelOpened { sender: ParaId, recipient: ParaId, @@ -836,6 +844,50 @@ pub mod pallet { Ok(()) } + + /// Establish a bidirectional HRMP channel between a parachain and a system chain. + /// + /// Arguments: + /// + /// - `target_system_chain`: A system chain, `ParaId`. + /// + /// The origin needs to be the parachain origin. + #[pallet::call_index(10)] + #[pallet::weight(<T as Config>::WeightInfo::establish_channel_with_system())] + pub fn establish_channel_with_system( + origin: OriginFor<T>, + target_system_chain: ParaId, + ) -> DispatchResultWithPostInfo { + let sender = ensure_parachain(<T as Config>::RuntimeOrigin::from(origin))?; + + ensure!(target_system_chain.is_system(), Error::<T>::ChannelCreationNotAuthorized); + + let (max_message_size, max_capacity) = + T::DefaultChannelSizeAndCapacityWithSystem::get(); + + // create bidirectional channel + Self::init_open_channel(sender, target_system_chain, max_capacity, max_message_size)?; + Self::accept_open_channel(target_system_chain, sender)?; + + Self::init_open_channel(target_system_chain, sender, max_capacity, max_message_size)?; + Self::accept_open_channel(sender, target_system_chain)?; + + Self::deposit_event(Event::HrmpSystemChannelOpened { + sender, + recipient: target_system_chain, + proposed_max_capacity: max_capacity, + proposed_max_message_size: max_message_size, + }); + + Self::deposit_event(Event::HrmpSystemChannelOpened { + sender: target_system_chain, + recipient: sender, + proposed_max_capacity: max_capacity, + proposed_max_message_size: max_message_size, + }); + + Ok(Pays::No.into()) + } } } diff --git a/polkadot/runtime/parachains/src/hrmp/benchmarking.rs b/polkadot/runtime/parachains/src/hrmp/benchmarking.rs index 13f4cdfe3eeaf767f6c04bab6a627610db16939d..8bf444e647e2475cd9d24193c6bdb8d268c5228a 100644 --- a/polkadot/runtime/parachains/src/hrmp/benchmarking.rs +++ b/polkadot/runtime/parachains/src/hrmp/benchmarking.rs @@ -50,6 +50,13 @@ fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) { assert_eq!(event, &system_event); } +fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) { + let events = frame_system::Pallet::<T>::events(); + let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into(); + + assert!(events.iter().any(|record| record.event == system_event)); +} + /// Enumerates the phase in the setup process of a channel between two parachains. enum ParachainSetupStep { /// A channel open has been requested @@ -517,6 +524,43 @@ mod benchmarks { ); } + #[benchmark] + fn establish_channel_with_system() { + let sender_id = 1u32; + let recipient_id: ParaId = 2u32.into(); + + let sender_origin: crate::Origin = sender_id.into(); + + // make sure para is registered, and has zero balance. + register_parachain_with_balance::<T>(sender_id.into(), Zero::zero()); + register_parachain_with_balance::<T>(recipient_id, Zero::zero()); + + #[extrinsic_call] + _(sender_origin, recipient_id); + + let (max_message_size, max_capacity) = T::DefaultChannelSizeAndCapacityWithSystem::get(); + + assert_has_event::<T>( + Event::<T>::HrmpSystemChannelOpened { + sender: sender_id.into(), + recipient: recipient_id, + proposed_max_capacity: max_capacity, + proposed_max_message_size: max_message_size, + } + .into(), + ); + + assert_has_event::<T>( + Event::<T>::HrmpSystemChannelOpened { + sender: recipient_id, + recipient: sender_id.into(), + proposed_max_capacity: max_capacity, + proposed_max_message_size: max_message_size, + } + .into(), + ); + } + impl_benchmark_test_suite!( Hrmp, crate::mock::new_test_ext(crate::hrmp::tests::GenesisConfigBuilder::default().build()), diff --git a/polkadot/runtime/parachains/src/hrmp/tests.rs b/polkadot/runtime/parachains/src/hrmp/tests.rs index 8d43b866bc19767ad3308c4f9730c1ad894d4c0c..2f767ab7e1b19d311f53d194638bb23066c37d31 100644 --- a/polkadot/runtime/parachains/src/hrmp/tests.rs +++ b/polkadot/runtime/parachains/src/hrmp/tests.rs @@ -27,7 +27,7 @@ use crate::{ }, shared, }; -use frame_support::{assert_noop, assert_ok}; +use frame_support::{assert_noop, assert_ok, error::BadOrigin}; use primitives::BlockNumber; use std::collections::BTreeMap; @@ -935,3 +935,72 @@ fn watermark_maxed_out_at_relay_parent() { Hrmp::assert_storage_consistency_exhaustive(); }); } + +#[test] +fn establish_channel_with_system_works() { + let para_a = 2000.into(); + let para_a_origin: crate::Origin = 2000.into(); + let para_b = 3.into(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + // We need both A & B to be registered and live parachains. + register_parachain(para_a); + register_parachain(para_b); + + run_to_block(5, Some(vec![4, 5])); + Hrmp::establish_channel_with_system(para_a_origin.into(), para_b).unwrap(); + Hrmp::assert_storage_consistency_exhaustive(); + assert!(System::events().iter().any(|record| record.event == + MockEvent::Hrmp(Event::HrmpSystemChannelOpened { + sender: para_a, + recipient: para_b, + proposed_max_capacity: 1, + proposed_max_message_size: 4 + }))); + + assert!(System::events().iter().any(|record| record.event == + MockEvent::Hrmp(Event::HrmpSystemChannelOpened { + sender: para_b, + recipient: para_a, + proposed_max_capacity: 1, + proposed_max_message_size: 4 + }))); + + // Advance to a block 6, but without session change. That means that the channel has + // not been created yet. + run_to_block(6, None); + assert!(!channel_exists(para_a, para_b)); + assert!(!channel_exists(para_b, para_a)); + Hrmp::assert_storage_consistency_exhaustive(); + + // Now let the session change happen and thus open the channel. + run_to_block(8, Some(vec![8])); + assert!(channel_exists(para_a, para_b)); + assert!(channel_exists(para_b, para_a)); + Hrmp::assert_storage_consistency_exhaustive(); + }); +} + +#[test] +fn establish_channel_with_system_with_invalid_args() { + let para_a = 2001.into(); + let para_a_origin: crate::Origin = 2000.into(); + let para_b = 2003.into(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + // We need both A & B to be registered and live parachains. + register_parachain(para_a); + register_parachain(para_b); + + run_to_block(5, Some(vec![4, 5])); + assert_noop!( + Hrmp::establish_channel_with_system(RuntimeOrigin::signed(1), para_b), + BadOrigin + ); + assert_noop!( + Hrmp::establish_channel_with_system(para_a_origin.into(), para_b), + Error::<Test>::ChannelCreationNotAuthorized + ); + Hrmp::assert_storage_consistency_exhaustive(); + }); +} diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 9d60bbb23b6fae7f34b5b3152d01afe824c5936f..903d01aa5c9c9ff5ec9b2d5dd15ca2c2771fde00 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -1104,6 +1104,24 @@ impl<T: Config> Pallet<T> { }) } + /// Returns all the `CommittedCandidateReceipt` pending availability for the para provided, if + /// any. + pub(crate) fn candidates_pending_availability( + para: ParaId, + ) -> Vec<CommittedCandidateReceipt<T::Hash>> { + <PendingAvailability<T>>::get(¶) + .map(|candidates| { + candidates + .into_iter() + .map(|candidate| CommittedCandidateReceipt { + descriptor: candidate.descriptor.clone(), + commitments: candidate.commitments.clone(), + }) + .collect() + }) + .unwrap_or_default() + } + /// Returns the metadata around the first candidate pending availability for the /// para provided, if any. pub(crate) fn pending_availability( diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index 461b9f4b431acea91614b295d47e3d4c273adccf..c91f5be127cd4be06c5ca7f255804328bab7def7 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -248,6 +248,7 @@ impl crate::dmp::Config for Test {} parameter_types! { pub const FirstMessageFactorPercent: u64 = 100; + pub const DefaultChannelSizeAndCapacityWithSystem: (u32, u32) = (4, 1); } impl crate::hrmp::Config for Test { @@ -255,6 +256,7 @@ impl crate::hrmp::Config for Test { type RuntimeEvent = RuntimeEvent; type ChannelManager = frame_system::EnsureRoot<u64>; type Currency = pallet_balances::Pallet<Test>; + type DefaultChannelSizeAndCapacityWithSystem = DefaultChannelSizeAndCapacityWithSystem; type WeightInfo = crate::hrmp::TestWeightInfo; } diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs index 39d4f520994a9993e64f448b456086d4aca99a83..3dca38050a0ac8c43bbb44cdd605572fd6760a82 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs @@ -301,6 +301,10 @@ pub fn validation_code<T: initializer::Config>( } /// Implementation for the `candidate_pending_availability` function of the runtime API. +#[deprecated( + note = "`candidate_pending_availability` will be removed. Use `candidates_pending_availability` to query + all candidates pending availability" +)] pub fn candidate_pending_availability<T: initializer::Config>( para_id: ParaId, ) -> Option<CommittedCandidateReceipt<T::Hash>> { diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs index 9ea29a2d374007fa54337fcc8d0fd2f8092553be..32bbdca84a3cce4946ce39edb4e3a3f8e5211188 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs @@ -16,8 +16,8 @@ //! Put implementations of functions from staging APIs here. -use crate::scheduler; -use primitives::{CoreIndex, Id as ParaId}; +use crate::{inclusion, initializer, scheduler}; +use primitives::{CommittedCandidateReceipt, CoreIndex, Id as ParaId}; use sp_runtime::traits::One; use sp_std::{ collections::{btree_map::BTreeMap, vec_deque::VecDeque}, @@ -41,3 +41,11 @@ pub fn claim_queue<T: scheduler::Config>() -> BTreeMap<CoreIndex, VecDeque<ParaI }) .collect() } + +/// Returns all the candidates that are pending availability for a given `ParaId`. +/// Deprecates `candidate_pending_availability` in favor of supporting elastic scaling. +pub fn candidates_pending_availability<T: initializer::Config>( + para_id: ParaId, +) -> Vec<CommittedCandidateReceipt<T::Hash>> { + <inclusion::Pallet<T>>::candidates_pending_availability(para_id) +} diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 20a914fb8085fcc917b83fbf5f879e603609904b..bbe19310f970ade37e25f5c1e2c983758c617412 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -70,6 +70,7 @@ pallet-mmr = { path = "../../../substrate/frame/merkle-mountain-range", default- pallet-multisig = { path = "../../../substrate/frame/multisig", default-features = false } pallet-nis = { path = "../../../substrate/frame/nis", default-features = false } pallet-offences = { path = "../../../substrate/frame/offences", default-features = false } +pallet-parameters = { path = "../../../substrate/frame/parameters", default-features = false } pallet-preimage = { path = "../../../substrate/frame/preimage", default-features = false } pallet-proxy = { path = "../../../substrate/frame/proxy", default-features = false } pallet-ranked-collective = { path = "../../../substrate/frame/ranked-collective", default-features = false } @@ -164,6 +165,7 @@ std = [ "pallet-multisig/std", "pallet-nis/std", "pallet-offences/std", + "pallet-parameters/std", "pallet-preimage/std", "pallet-proxy/std", "pallet-ranked-collective/std", @@ -239,6 +241,7 @@ runtime-benchmarks = [ "pallet-multisig/runtime-benchmarks", "pallet-nis/runtime-benchmarks", "pallet-offences/runtime-benchmarks", + "pallet-parameters/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-ranked-collective/runtime-benchmarks", @@ -294,6 +297,7 @@ try-runtime = [ "pallet-multisig/try-runtime", "pallet-nis/try-runtime", "pallet-offences/try-runtime", + "pallet-parameters/try-runtime", "pallet-preimage/try-runtime", "pallet-proxy/try-runtime", "pallet-ranked-collective/try-runtime", diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 894d7fac2f0a4d0e07f1b97f19b3f6d73147dd23..740a6240d39526cec795ba082848a480c8a7e3a3 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -25,6 +25,7 @@ use beefy_primitives::{ ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}, mmr::{BeefyDataProvider, MmrLeafVersion}, }; +use frame_support::dynamic_params::{dynamic_pallet_params, dynamic_params}; use pallet_nis::WithMaximumOf; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use primitives::{ @@ -73,9 +74,10 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, parameter_types, traits::{ - fungible::HoldConsideration, Contains, EitherOf, EitherOfDiverse, EverythingBut, - InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage, - ProcessMessageError, StorageMapShim, WithdrawReasons, + fungible::HoldConsideration, Contains, EitherOf, EitherOfDiverse, EnsureOrigin, + EnsureOriginWithArg, EverythingBut, InstanceFilter, KeyOwnerProofSystem, + LinearStoragePrice, PrivilegeCmp, ProcessMessage, ProcessMessageError, StorageMapShim, + WithdrawReasons, }, weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, PalletId, @@ -234,6 +236,72 @@ impl PrivilegeCmp<OriginCaller> for OriginPrivilegeCmp { } } +/// Dynamic params that can be adjusted at runtime. +#[dynamic_params(RuntimeParameters, pallet_parameters::Parameters::<Runtime>)] +pub mod dynamic_params { + use super::*; + + #[dynamic_pallet_params] + #[codec(index = 0)] + pub mod nis { + use super::*; + + #[codec(index = 0)] + pub static Target: Perquintill = Perquintill::zero(); + + #[codec(index = 1)] + pub static MinBid: Balance = 100 * UNITS; + } + + #[dynamic_pallet_params] + #[codec(index = 1)] + pub mod preimage { + use super::*; + + #[codec(index = 0)] + pub static BaseDeposit: Balance = deposit(2, 64); + + #[codec(index = 1)] + pub static ByteDeposit: Balance = deposit(0, 1); + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl Default for RuntimeParameters { + fn default() -> Self { + RuntimeParameters::Preimage(dynamic_params::preimage::Parameters::BaseDeposit( + dynamic_params::preimage::BaseDeposit, + Some(1u32.into()), + )) + } +} + +/// Defines what origin can modify which dynamic parameters. +pub struct DynamicParameterOrigin; +impl EnsureOriginWithArg<RuntimeOrigin, RuntimeParametersKey> for DynamicParameterOrigin { + type Success = (); + + fn try_origin( + origin: RuntimeOrigin, + key: &RuntimeParametersKey, + ) -> Result<Self::Success, RuntimeOrigin> { + use crate::{dynamic_params::*, governance::*, RuntimeParametersKey::*}; + + match key { + Nis(nis::ParametersKey::MinBid(_)) => StakingAdmin::ensure_origin(origin.clone()), + Nis(nis::ParametersKey::Target(_)) => GeneralAdmin::ensure_origin(origin.clone()), + Preimage(_) => frame_system::ensure_root(origin.clone()), + } + .map_err(|_| origin) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(_key: &RuntimeParametersKey) -> Result<RuntimeOrigin, ()> { + // Provide the origin for the parameter returned by `Default`: + Ok(RuntimeOrigin::root()) + } +} + impl pallet_scheduler::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; @@ -250,8 +318,6 @@ impl pallet_scheduler::Config for Runtime { } parameter_types! { - pub const PreimageBaseDeposit: Balance = deposit(2, 64); - pub const PreimageByteDeposit: Balance = deposit(0, 1); pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); } @@ -264,7 +330,11 @@ impl pallet_preimage::Config for Runtime { AccountId, Balances, PreimageHoldReason, - LinearStoragePrice<PreimageBaseDeposit, PreimageByteDeposit, Balance>, + LinearStoragePrice< + dynamic_params::preimage::BaseDeposit, + dynamic_params::preimage::ByteDeposit, + Balance, + >, >; } @@ -950,11 +1020,16 @@ impl pallet_message_queue::Config for Runtime { impl parachains_dmp::Config for Runtime {} +parameter_types! { + pub const DefaultChannelSizeAndCapacityWithSystem: (u32, u32) = (51200, 500); +} + impl parachains_hrmp::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type ChannelManager = EnsureRoot<AccountId>; type Currency = Balances; + type DefaultChannelSizeAndCapacityWithSystem = DefaultChannelSizeAndCapacityWithSystem; type WeightInfo = weights::runtime_parachains_hrmp::WeightInfo<Runtime>; } @@ -1123,12 +1198,10 @@ impl pallet_balances::Config<NisCounterpartInstance> for Runtime { parameter_types! { pub const NisBasePeriod: BlockNumber = 30 * DAYS; - pub const MinBid: Balance = 100 * UNITS; pub MinReceipt: Perquintill = Perquintill::from_rational(1u64, 10_000_000u64); pub const IntakePeriod: BlockNumber = 5 * MINUTES; pub MaxIntakeWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 10; pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5); - pub storage NisTarget: Perquintill = Perquintill::zero(); pub const NisPalletId: PalletId = PalletId(*b"py/nis "); } @@ -1142,13 +1215,13 @@ impl pallet_nis::Config for Runtime { type CounterpartAmount = WithMaximumOf<ConstU128<21_000_000_000_000_000_000u128>>; type Deficit = (); // Mint type IgnoredIssuance = (); - type Target = NisTarget; + type Target = dynamic_params::nis::Target; type PalletId = NisPalletId; type QueueCount = ConstU32<300>; type MaxQueueLen = ConstU32<1000>; type FifoQueueLen = ConstU32<250>; type BasePeriod = NisBasePeriod; - type MinBid = MinBid; + type MinBid = dynamic_params::nis::MinBid; type MinReceipt = MinReceipt; type IntakePeriod = IntakePeriod; type MaxIntakeWeight = MaxIntakeWeight; @@ -1158,6 +1231,13 @@ impl pallet_nis::Config for Runtime { type BenchmarkSetup = (); } +impl pallet_parameters::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeParameters = RuntimeParameters; + type AdminOrigin = DynamicParameterOrigin; + type WeightInfo = weights::pallet_parameters::WeightInfo<Runtime>; +} + parameter_types! { pub BeefySetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get(); } @@ -1190,6 +1270,7 @@ impl pallet_mmr::Config for Runtime { type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest<Runtime>; type WeightInfo = (); type LeafData = pallet_beefy_mmr::Pallet<Runtime>; + type BlockHashProvider = pallet_mmr::DefaultBlockHashProvider<Runtime>; } parameter_types! { @@ -1285,6 +1366,7 @@ construct_runtime! { Timestamp: pallet_timestamp = 2, Indices: pallet_indices = 3, Balances: pallet_balances = 4, + Parameters: pallet_parameters = 6, TransactionPayment: pallet_transaction_payment = 33, // Consensus support. @@ -1625,6 +1707,7 @@ mod benches { [pallet_indices, Indices] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_parameters, Parameters] [pallet_preimage, Preimage] [pallet_proxy, Proxy] [pallet_ranked_collective, FellowshipCollective] @@ -1790,6 +1873,7 @@ sp_api::impl_runtime_apis! { } fn candidate_pending_availability(para_id: ParaId) -> Option<CommittedCandidateReceipt<Hash>> { + #[allow(deprecated)] parachains_runtime_api_impl::candidate_pending_availability::<Runtime>(para_id) } @@ -1903,6 +1987,10 @@ sp_api::impl_runtime_apis! { fn claim_queue() -> BTreeMap<CoreIndex, VecDeque<ParaId>> { vstaging_parachains_runtime_api_impl::claim_queue::<Runtime>() } + + fn candidates_pending_availability(para_id: ParaId) -> Vec<CommittedCandidateReceipt<Hash>> { + vstaging_parachains_runtime_api_impl::candidates_pending_availability::<Runtime>(para_id) + } } #[api_version(3)] diff --git a/polkadot/runtime/rococo/src/weights/mod.rs b/polkadot/runtime/rococo/src/weights/mod.rs index 7328dca9393693e335b49dc317d6754f9fd6e840..3c6845dfb43e65e85dc33b2e288f35a9a8742e1e 100644 --- a/polkadot/runtime/rococo/src/weights/mod.rs +++ b/polkadot/runtime/rococo/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod pallet_indices; pub mod pallet_message_queue; pub mod pallet_multisig; pub mod pallet_nis; +pub mod pallet_parameters; pub mod pallet_preimage; pub mod pallet_proxy; pub mod pallet_ranked_collective; diff --git a/polkadot/runtime/rococo/src/weights/pallet_parameters.rs b/polkadot/runtime/rococo/src/weights/pallet_parameters.rs new file mode 100644 index 0000000000000000000000000000000000000000..bd2bcf960e9baae96f156b6424852b02a1536b00 --- /dev/null +++ b/polkadot/runtime/rococo/src/weights/pallet_parameters.rs @@ -0,0 +1,63 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. + +//! Autogenerated weights for `pallet_parameters` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-04-05, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-anb7yjbi-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_parameters +// --chain=rococo-dev +// --header=./polkadot/file_header.txt +// --output=./polkadot/runtime/rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_parameters`. +pub struct WeightInfo<T>(PhantomData<T>); +impl<T: frame_system::Config> pallet_parameters::WeightInfo for WeightInfo<T> { + /// Storage: `Parameters::Parameters` (r:1 w:1) + /// Proof: `Parameters::Parameters` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + fn set_parameter() -> Weight { + // Proof Size summary in bytes: + // Measured: `4` + // Estimated: `3493` + // Minimum execution time: 6_937_000 picoseconds. + Weight::from_parts(7_242_000, 0) + .saturating_add(Weight::from_parts(0, 3493)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs b/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs index 417820e6627f6e39c4107df722f4b83f9cca6a4b..97b84155b36a6adb2258a714e65bf5c70bc9aa50 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_parachains_hrmp.rs @@ -331,4 +331,14 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + fn establish_channel_with_system() -> Weight { + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `6357` + // Minimum execution time: 629_674_000 picoseconds. + Weight::from_parts(640_174_000, 0) + .saturating_add(Weight::from_parts(0, 6357)) + .saturating_add(T::DbWeight::get().reads(12)) + .saturating_add(T::DbWeight::get().writes(8)) + } } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index f01382509501186bbe6efbdad67eea536ce59990..514643c0a20169291cde30fd9dfc259bc936853f 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -22,15 +22,19 @@ use pallet_transaction_payment::FungibleAdapter; use parity_scale_codec::Encode; -use sp_std::{collections::btree_map::BTreeMap, prelude::*}; +use sp_std::{ + collections::{btree_map::BTreeMap, vec_deque::VecDeque}, + prelude::*, +}; use polkadot_runtime_parachains::{ assigner_parachains as parachains_assigner_parachains, configuration as parachains_configuration, disputes as parachains_disputes, - disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, - inclusion as parachains_inclusion, initializer as parachains_initializer, - origin as parachains_origin, paras as parachains_paras, - paras_inherent as parachains_paras_inherent, runtime_api_impl::v10 as runtime_impl, + disputes::slashing as parachains_slashing, + dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, + initializer as parachains_initializer, origin as parachains_origin, paras as parachains_paras, + paras_inherent as parachains_paras_inherent, + runtime_api_impl::{v10 as runtime_impl, vstaging as vstaging_parachains_runtime_api_impl}, scheduler as parachains_scheduler, session_info as parachains_session_info, shared as parachains_shared, }; @@ -53,9 +57,9 @@ use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_runtime_parachains::reward_points::RewardValidatorsWithEraPoints; use primitives::{ slashing, AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, - Hash as HashT, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, Nonce, - OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, + CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, + GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, + Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, SessionInfo as SessionInfoData, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; @@ -554,6 +558,7 @@ impl parachains_dmp::Config for Runtime {} parameter_types! { pub const FirstMessageFactorPercent: u64 = 100; + pub const DefaultChannelSizeAndCapacityWithSystem: (u32, u32) = (51200, 500); } impl parachains_hrmp::Config for Runtime { @@ -561,6 +566,7 @@ impl parachains_hrmp::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ChannelManager = frame_system::EnsureRoot<AccountId>; type Currency = Balances; + type DefaultChannelSizeAndCapacityWithSystem = DefaultChannelSizeAndCapacityWithSystem; type WeightInfo = parachains_hrmp::TestWeightInfo; } @@ -829,7 +835,7 @@ sp_api::impl_runtime_apis! { } } - #[api_version(10)] + #[api_version(11)] impl primitives::runtime_api::ParachainHost<Block> for Runtime { fn validators() -> Vec<ValidatorId> { runtime_impl::validators::<Runtime>() @@ -877,6 +883,7 @@ sp_api::impl_runtime_apis! { } fn candidate_pending_availability(para_id: ParaId) -> Option<CommittedCandidateReceipt<Hash>> { + #[allow(deprecated)] runtime_impl::candidate_pending_availability::<Runtime>(para_id) } @@ -981,6 +988,14 @@ sp_api::impl_runtime_apis! { fn node_features() -> primitives::NodeFeatures { runtime_impl::node_features::<Runtime>() } + + fn claim_queue() -> BTreeMap<CoreIndex, VecDeque<ParaId>> { + vstaging_parachains_runtime_api_impl::claim_queue::<Runtime>() + } + + fn candidates_pending_availability(para_id: ParaId) -> Vec<CommittedCandidateReceipt<Hash>> { + vstaging_parachains_runtime_api_impl::candidates_pending_availability::<Runtime>(para_id) + } } impl beefy_primitives::BeefyApi<Block, BeefyId> for Runtime { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 4930610c1d80a3e632232b2f72c81ece97ae1955..a119d78b83ab8a76642d0bc1cf407ec4935ab5dc 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -333,6 +333,7 @@ impl pallet_mmr::Config for Runtime { type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest<Runtime>; type WeightInfo = (); type LeafData = pallet_beefy_mmr::Pallet<Runtime>; + type BlockHashProvider = pallet_mmr::DefaultBlockHashProvider<Runtime>; } /// MMR helper types. @@ -1153,11 +1154,16 @@ impl pallet_message_queue::Config for Runtime { impl parachains_dmp::Config for Runtime {} +parameter_types! { + pub const DefaultChannelSizeAndCapacityWithSystem: (u32, u32) = (4096, 4); +} + impl parachains_hrmp::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type ChannelManager = EnsureRoot<AccountId>; type Currency = Balances; + type DefaultChannelSizeAndCapacityWithSystem = DefaultChannelSizeAndCapacityWithSystem; type WeightInfo = weights::runtime_parachains_hrmp::WeightInfo<Self>; } @@ -1848,6 +1854,7 @@ sp_api::impl_runtime_apis! { } fn candidate_pending_availability(para_id: ParaId) -> Option<CommittedCandidateReceipt<Hash>> { + #[allow(deprecated)] parachains_runtime_api_impl::candidate_pending_availability::<Runtime>(para_id) } @@ -1961,6 +1968,10 @@ sp_api::impl_runtime_apis! { fn claim_queue() -> BTreeMap<CoreIndex, VecDeque<ParaId>> { vstaging_parachains_runtime_api_impl::claim_queue::<Runtime>() } + + fn candidates_pending_availability(para_id: ParaId) -> Vec<CommittedCandidateReceipt<Hash>> { + vstaging_parachains_runtime_api_impl::candidates_pending_availability::<Runtime>(para_id) + } } impl beefy_primitives::BeefyApi<Block, BeefyId> for Runtime { diff --git a/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs b/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs index 9beb15303d871cd4ae0b6b0ce3148291d745395b..3d2ab827b8fd125b74a2c892803038557998b8be 100644 --- a/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs +++ b/polkadot/runtime/westend/src/weights/runtime_parachains_hrmp.rs @@ -324,4 +324,14 @@ impl<T: frame_system::Config> runtime_parachains::hrmp::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + fn establish_channel_with_system() -> Weight { + // Proof Size summary in bytes: + // Measured: `417` + // Estimated: `6357` + // Minimum execution time: 629_674_000 picoseconds. + Weight::from_parts(640_174_000, 0) + .saturating_add(Weight::from_parts(0, 6357)) + .saturating_add(T::DbWeight::get().reads(12)) + .saturating_add(T::DbWeight::get().writes(8)) + } } diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index ef255068734aae29d879e16c575a8a5cea4c5e59..cf22b86cf82c9e4552beee53be5786e57db0f896 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -1299,64 +1299,20 @@ pub mod pallet { ); ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets); - let mut assets = assets.into_inner(); + let assets = assets.into_inner(); let fee_asset_item = fee_asset_item as usize; - let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone(); // Find transfer types for fee and non-fee assets. let (fees_transfer_type, assets_transfer_type) = Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?; - // local and remote XCM programs to potentially handle fees separately - let fees = if fees_transfer_type == assets_transfer_type { - // no need for custom fees instructions, fees are batched with assets - FeesHandling::Batched { fees } - } else { - // Disallow _remote reserves_ unless assets & fees have same remote reserve (covered - // by branch above). The reason for this is that we'd need to send XCMs to separate - // chains with no guarantee of delivery order on final destination; therefore we - // cannot guarantee to have fees in place on final destination chain to pay for - // assets transfer. - ensure!( - !matches!(assets_transfer_type, TransferType::RemoteReserve(_)), - Error::<T>::InvalidAssetUnsupportedReserve - ); - let weight_limit = weight_limit.clone(); - // remove `fees` from `assets` and build separate fees transfer instructions to be - // added to assets transfers XCM programs - let fees = assets.remove(fee_asset_item); - let (local_xcm, remote_xcm) = match fees_transfer_type { - TransferType::LocalReserve => Self::local_reserve_fees_instructions( - origin.clone(), - dest.clone(), - fees, - weight_limit, - )?, - TransferType::DestinationReserve => - Self::destination_reserve_fees_instructions( - origin.clone(), - dest.clone(), - fees, - weight_limit, - )?, - TransferType::Teleport => Self::teleport_fees_instructions( - origin.clone(), - dest.clone(), - fees, - weight_limit, - )?, - TransferType::RemoteReserve(_) => - return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()), - }; - FeesHandling::Separate { local_xcm, remote_xcm } - }; - - Self::build_and_execute_xcm_transfer_type( + Self::do_transfer_assets( origin, dest, beneficiary, assets, assets_transfer_type, - fees, + fee_asset_item, + fees_transfer_type, weight_limit, ) } @@ -1443,6 +1399,87 @@ pub mod pallet { <Self as SendController<_>>::send_blob(origin, dest, encoded_message)?; Ok(()) } + + /// Transfer assets from the local chain to the destination chain using explicit transfer + /// types for assets and fees. + /// + /// `assets` must have same reserve location or may be teleportable to `dest`. Caller must + /// provide the `assets_transfer_type` to be used for `assets`: + /// - `TransferType::LocalReserve`: transfer assets to sovereign account of destination + /// chain and forward a notification XCM to `dest` to mint and deposit reserve-based + /// assets to `beneficiary`. + /// - `TransferType::DestinationReserve`: burn local assets and forward a notification to + /// `dest` chain to withdraw the reserve assets from this chain's sovereign account and + /// deposit them to `beneficiary`. + /// - `TransferType::RemoteReserve(reserve)`: burn local assets, forward XCM to `reserve` + /// chain to move reserves from this chain's SA to `dest` chain's SA, and forward another + /// XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. Typically + /// the remote `reserve` is Asset Hub. + /// - `TransferType::Teleport`: burn local assets and forward XCM to `dest` chain to + /// mint/teleport assets and deposit them to `beneficiary`. + /// + /// Fee payment on the source, destination and all intermediary hops, is specified through + /// `fees_id`, but make sure enough of the specified `fees_id` asset is included in the + /// given list of `assets`. `fees_id` should be enough to pay for `weight_limit`. If more + /// weight is needed than `weight_limit`, then the operation will fail and the sent assets + /// may be at risk. + /// + /// `fees_id` may use different transfer type than rest of `assets` and can be specified + /// through `fees_transfer_type`. + /// + /// - `origin`: Must be capable of withdrawing the `assets` and executing XCM. + /// - `dest`: Destination context for the assets. Will typically be `[Parent, + /// Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from + /// relay to parachain, or `(parents: 2, (GlobalConsensus(..), ..))` to send from + /// parachain across a bridge to another ecosystem destination. + /// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will + /// generally be an `AccountId32` value. + /// - `assets`: The assets to be withdrawn. This should include the assets used to pay the + /// fee on the `dest` (and possibly reserve) chains. + /// - `assets_transfer_type`: The XCM `TransferType` used to transfer the `assets`. + /// - `fees_id`: One of the included `assets` to be be used to pay fees. + /// - `fees_transfer_type`: The XCM `TransferType` used to transfer the `fees` assets. + /// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase. + #[pallet::call_index(15)] + #[pallet::weight(T::WeightInfo::transfer_assets())] + pub fn transfer_assets_using_type( + origin: OriginFor<T>, + dest: Box<VersionedLocation>, + beneficiary: Box<VersionedLocation>, + assets: Box<VersionedAssets>, + assets_transfer_type: Box<TransferType>, + fees_id: Box<VersionedAssetId>, + fees_transfer_type: Box<TransferType>, + weight_limit: WeightLimit, + ) -> DispatchResult { + let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; + let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?; + let beneficiary: Location = + (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?; + let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?; + let fees_id: AssetId = (*fees_id).try_into().map_err(|()| Error::<T>::BadVersion)?; + log::debug!( + target: "xcm::pallet_xcm::transfer_assets_using_type", + "origin {:?}, dest {:?}, beneficiary {:?}, assets {:?} through {:?}, fees-id {:?} through {:?}", + origin_location, dest, beneficiary, assets, assets_transfer_type, fees_id, fees_transfer_type, + ); + + let assets = assets.into_inner(); + ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets); + + let fee_asset_index = + assets.iter().position(|a| a.id == fees_id).ok_or(Error::<T>::FeesNotMet)?; + Self::do_transfer_assets( + origin_location, + dest, + beneficiary, + assets, + *assets_transfer_type, + fee_asset_index, + *fees_transfer_type, + weight_limit, + ) + } } } @@ -1607,15 +1644,16 @@ impl<T: Config> Pallet<T> { // Ensure all assets (including fees) have same reserve location. ensure!(assets_transfer_type == fees_transfer_type, Error::<T>::TooManyReserves); - Self::build_and_execute_xcm_transfer_type( - origin, - dest, + let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type( + origin.clone(), + dest.clone(), beneficiary, assets, assets_transfer_type, FeesHandling::Batched { fees }, weight_limit, - ) + )?; + Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm) } fn do_teleport_assets( @@ -1648,18 +1686,85 @@ impl<T: Config> Pallet<T> { } let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone(); - Self::build_and_execute_xcm_transfer_type( - origin_location, - dest, + let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type( + origin_location.clone(), + dest.clone(), beneficiary, assets, TransferType::Teleport, FeesHandling::Batched { fees }, weight_limit, - ) + )?; + Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm) + } + + fn do_transfer_assets( + origin: Location, + dest: Location, + beneficiary: Location, + mut assets: Vec<Asset>, + assets_transfer_type: TransferType, + fee_asset_index: usize, + fees_transfer_type: TransferType, + weight_limit: WeightLimit, + ) -> DispatchResult { + // local and remote XCM programs to potentially handle fees separately + let fees = if fees_transfer_type == assets_transfer_type { + let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone(); + // no need for custom fees instructions, fees are batched with assets + FeesHandling::Batched { fees } + } else { + // Disallow _remote reserves_ unless assets & fees have same remote reserve (covered + // by branch above). The reason for this is that we'd need to send XCMs to separate + // chains with no guarantee of delivery order on final destination; therefore we + // cannot guarantee to have fees in place on final destination chain to pay for + // assets transfer. + ensure!( + !matches!(assets_transfer_type, TransferType::RemoteReserve(_)), + Error::<T>::InvalidAssetUnsupportedReserve + ); + let weight_limit = weight_limit.clone(); + // remove `fees` from `assets` and build separate fees transfer instructions to be + // added to assets transfers XCM programs + let fees = assets.remove(fee_asset_index); + let (local_xcm, remote_xcm) = match fees_transfer_type { + TransferType::LocalReserve => Self::local_reserve_fees_instructions( + origin.clone(), + dest.clone(), + fees, + weight_limit, + )?, + TransferType::DestinationReserve => Self::destination_reserve_fees_instructions( + origin.clone(), + dest.clone(), + fees, + weight_limit, + )?, + TransferType::Teleport => Self::teleport_fees_instructions( + origin.clone(), + dest.clone(), + fees, + weight_limit, + )?, + TransferType::RemoteReserve(_) => + return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()), + }; + FeesHandling::Separate { local_xcm, remote_xcm } + }; + + let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type( + origin.clone(), + dest.clone(), + beneficiary, + assets, + assets_transfer_type, + fees, + weight_limit, + )?; + Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm) } - fn build_and_execute_xcm_transfer_type( + fn build_xcm_transfer_type( origin: Location, dest: Location, beneficiary: Location, @@ -1667,14 +1772,14 @@ impl<T: Config> Pallet<T> { transfer_type: TransferType, fees: FeesHandling<T>, weight_limit: WeightLimit, - ) -> DispatchResult { + ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> { log::debug!( - target: "xcm::pallet_xcm::build_and_execute_xcm_transfer_type", + target: "xcm::pallet_xcm::build_xcm_transfer_type", "origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, transfer_type {:?}, \ fees_handling {:?}, weight_limit: {:?}", origin, dest, beneficiary, assets, transfer_type, fees, weight_limit, ); - let (mut local_xcm, remote_xcm) = match transfer_type { + Ok(match transfer_type { TransferType::LocalReserve => { let (local, remote) = Self::local_reserve_transfer_programs( origin.clone(), @@ -1704,7 +1809,7 @@ impl<T: Config> Pallet<T> { }; let local = Self::remote_reserve_transfer_program( origin.clone(), - reserve, + reserve.try_into().map_err(|()| Error::<T>::BadVersion)?, dest.clone(), beneficiary, assets, @@ -1724,7 +1829,21 @@ impl<T: Config> Pallet<T> { )?; (local, Some(remote)) }, - }; + }) + } + + fn execute_xcm_transfer( + origin: Location, + dest: Location, + mut local_xcm: Xcm<<T as Config>::RuntimeCall>, + remote_xcm: Option<Xcm<()>>, + ) -> DispatchResult { + log::debug!( + target: "xcm::pallet_xcm::execute_xcm_transfer", + "origin {:?}, dest {:?}, local_xcm {:?}, remote_xcm {:?}", + origin, dest, local_xcm, remote_xcm, + ); + let weight = T::Weigher::weight(&mut local_xcm).map_err(|()| Error::<T>::UnweighableMessage)?; let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256); @@ -1738,7 +1857,7 @@ impl<T: Config> Pallet<T> { Self::deposit_event(Event::Attempted { outcome: outcome.clone() }); outcome.ensure_complete().map_err(|error| { log::error!( - target: "xcm::pallet_xcm::build_and_execute_xcm_transfer_type", + target: "xcm::pallet_xcm::execute_xcm_transfer", "XCM execution failed with error {:?}", error ); Error::<T>::LocalExecutionIncomplete @@ -1750,7 +1869,7 @@ impl<T: Config> Pallet<T> { if origin != Here.into_location() { Self::charge_fees(origin.clone(), price).map_err(|error| { log::error!( - target: "xcm::pallet_xcm::build_and_execute_xcm_transfer_type", + target: "xcm::pallet_xcm::execute_xcm_transfer", "Unable to charge fee with error {:?}", error ); Error::<T>::FeesNotMet @@ -1935,6 +2054,7 @@ impl<T: Config> Pallet<T> { ]); // handle fees Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?; + // deposit all remaining assets in holding to `beneficiary` location xcm_on_dest .inner_mut() diff --git a/polkadot/xcm/xcm-builder/src/asset_conversion.rs b/polkadot/xcm/xcm-builder/src/asset_conversion.rs index e38af149be541f3f85ed47109e979351d6569f0c..520ce87448ea4f868fbb62130a7da45353d220b6 100644 --- a/polkadot/xcm/xcm-builder/src/asset_conversion.rs +++ b/polkadot/xcm/xcm-builder/src/asset_conversion.rs @@ -107,17 +107,6 @@ impl< #[deprecated = "Use `ConvertedConcreteId` instead"] pub type ConvertedConcreteAssetId<A, B, C, O> = ConvertedConcreteId<A, B, C, O>; -pub struct V4V3LocationConverter; -impl MaybeEquivalence<xcm::v4::Location, xcm::v3::Location> for V4V3LocationConverter { - fn convert(old: &xcm::v4::Location) -> Option<xcm::v3::Location> { - (*old).clone().try_into().ok() - } - - fn convert_back(new: &xcm::v3::Location) -> Option<xcm::v4::Location> { - (*new).try_into().ok() - } -} - pub struct MatchedConvertedConcreteId<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertOther>( PhantomData<(AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertOther)>, ); diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 46d0ad227bfdf8e5e77188165fc259b0c1aec585..c3400cc72b48e97cb2cdfd85a028b21b7c032963 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -30,7 +30,7 @@ mod asset_conversion; #[allow(deprecated)] pub use asset_conversion::ConvertedConcreteAssetId; pub use asset_conversion::{ - AsPrefixedGeneralIndex, ConvertedConcreteId, MatchedConvertedConcreteId, V4V3LocationConverter, + AsPrefixedGeneralIndex, ConvertedConcreteId, MatchedConvertedConcreteId, }; mod barriers; @@ -81,7 +81,9 @@ pub use location_conversion::{ }; mod matches_location; -pub use matches_location::{StartsWith, StartsWithExplicitGlobalConsensus}; +pub use matches_location::{ + StartsWith, StartsWithExplicitGlobalConsensus, WithLatestLocationConverter, +}; mod matches_token; pub use matches_token::IsConcrete; diff --git a/polkadot/xcm/xcm-builder/src/matches_location.rs b/polkadot/xcm/xcm-builder/src/matches_location.rs index 1664c24772909a8a287cf620da308b07158270bb..b6c2807e6b29db302e2d1182729c5acb4426b24b 100644 --- a/polkadot/xcm/xcm-builder/src/matches_location.rs +++ b/polkadot/xcm/xcm-builder/src/matches_location.rs @@ -18,6 +18,8 @@ //! `InteriorLocation` types. use frame_support::traits::{Contains, Get}; +use sp_runtime::traits::MaybeEquivalence; +use sp_std::marker::PhantomData; use xcm::latest::{InteriorLocation, Location, NetworkId}; /// An implementation of `Contains` that checks for `Location` or @@ -51,3 +53,18 @@ impl<T: Get<NetworkId>> Contains<InteriorLocation> for StartsWithExplicitGlobalC matches!(location.global_consensus(), Ok(requested_network) if requested_network.eq(&T::get())) } } + +/// An adapter implementation of `MaybeEquivalence` which can convert between the latest `Location` +/// and other versions that implement `TryInto<Location>` and `TryFrom<Location>`. +pub struct WithLatestLocationConverter<Target>(PhantomData<Target>); +impl<Target: TryInto<Location> + TryFrom<Location> + Clone> MaybeEquivalence<Location, Target> + for WithLatestLocationConverter<Target> +{ + fn convert(old: &Location) -> Option<Target> { + (*old).clone().try_into().ok() + } + + fn convert_back(new: &Target) -> Option<Location> { + new.clone().try_into().ok() + } +} diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 81b81fe6a17154c74e03ebbddf8bfd81e4f7181c..e673a46c4ac683cfcdbb0e8197cebc96e35c33d7 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -347,6 +347,9 @@ impl<Config: config::Config> XcmExecutor<Config> { msg: Xcm<()>, reason: FeeReason, ) -> Result<XcmHash, XcmError> { + log::trace!( + target: "xcm::send", "Sending msg: {msg:?}, to destination: {dest:?}, (reason: {reason:?})" + ); let (ticket, fee) = validate_send::<Config::XcmSender>(dest, msg)?; self.take_fee(fee, reason)?; Config::XcmSender::deliver(ticket).map_err(Into::into) diff --git a/polkadot/xcm/xcm-executor/src/traits/asset_transfer.rs b/polkadot/xcm/xcm-executor/src/traits/asset_transfer.rs index 5da3d1da37c8117d2f0f754c4cfea1b3e638cb06..6d72eaf680fda558984da1958219d539e8913707 100644 --- a/polkadot/xcm/xcm-executor/src/traits/asset_transfer.rs +++ b/polkadot/xcm/xcm-executor/src/traits/asset_transfer.rs @@ -30,7 +30,7 @@ pub enum Error { } /// Specify which type of asset transfer is required for a particular `(asset, dest)` combination. -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo)] pub enum TransferType { /// should teleport `asset` to `dest` Teleport, @@ -39,7 +39,7 @@ pub enum TransferType { /// should reserve-transfer `asset` to `dest`, using `dest` as reserve DestinationReserve, /// should reserve-transfer `asset` to `dest`, using remote chain `Location` as reserve - RemoteReserve(Location), + RemoteReserve(VersionedLocation), } /// A trait for identifying asset transfer type based on `IsTeleporter` and `IsReserve` @@ -77,7 +77,7 @@ pub trait XcmAssetTransfers { Ok(TransferType::LocalReserve) } else if Self::IsReserve::contains(asset, &asset_location) { // remote location that is recognized as reserve location for asset - Ok(TransferType::RemoteReserve(asset_location)) + Ok(TransferType::RemoteReserve(asset_location.into())) } else { // remote location that is not configured either as teleporter or reserve => cannot // determine asset reserve diff --git a/polkadot/zombienet_tests/elastic_scaling/0001-basic-3cores-6s-blocks.toml b/polkadot/zombienet_tests/elastic_scaling/0001-basic-3cores-6s-blocks.toml index db508e14dbaa5c443e30c6c50d9d835927055162..83f5434edddb19afefcba93a9ce7bb305909c07f 100644 --- a/polkadot/zombienet_tests/elastic_scaling/0001-basic-3cores-6s-blocks.toml +++ b/polkadot/zombienet_tests/elastic_scaling/0001-basic-3cores-6s-blocks.toml @@ -20,8 +20,8 @@ chain = "rococo-local" default_command = "polkadot" [relaychain.default_resources] - limits = { memory = "4G", cpu = "2" } - requests = { memory = "2G", cpu = "1" } + limits = { memory = "4G", cpu = "3" } + requests = { memory = "4G", cpu = "3" } [[relaychain.node_groups]] name = "elastic-validator" @@ -32,11 +32,20 @@ default_command = "polkadot" [[parachains]] id = {{id}} addToGenesis = true + [parachains.default_resources] + limits = { memory = "4G", cpu = "3" } + requests = { memory = "4G", cpu = "3" } [parachains.collator] name = "some-parachain" image = "{{COL_IMAGE}}" command = "adder-collator" args = ["-lparachain::collation-generation=trace,parachain::collator-protocol=trace,parachain=debug"] + {% endfor %} +# This represents the layout of the adder collator block header. +[types.Header] +number = "u64" +parent_hash = "Hash" +post_state = "Hash" \ No newline at end of file diff --git a/polkadot/zombienet_tests/elastic_scaling/0001-basic-3cores-6s-blocks.zndsl b/polkadot/zombienet_tests/elastic_scaling/0001-basic-3cores-6s-blocks.zndsl index b9c002457549788c04673f291f54599757cd67d7..d624cbaf9df6a62448db2cef637e6d29a0d419b5 100644 --- a/polkadot/zombienet_tests/elastic_scaling/0001-basic-3cores-6s-blocks.zndsl +++ b/polkadot/zombienet_tests/elastic_scaling/0001-basic-3cores-6s-blocks.zndsl @@ -18,11 +18,11 @@ elastic-validator-0: js-script ./assign-core.js with "2000,1" return is 0 within elastic-validator-0: reports substrate_block_height{status="best"} is at least 20 within 600 seconds # Non elastic parachain should progress normally -some-parachain-1: count of log lines containing "Parachain velocity: 1" is at least 9 within 20 seconds +some-parachain-1: count of log lines containing "Parachain velocity: 1" is at least 5 within 20 seconds # Sanity -some-parachain-1: count of log lines containing "Parachain velocity: 2" is 0 within 20 seconds +some-parachain-1: count of log lines containing "Parachain velocity: 2" is 0 -# Parachain should progress 3 blocks per relay chain block ideally, however this measurement does -# `ceil()` on the actual velocity to account for CI overload. -some-parachain: count of log lines containing "Parachain velocity: 3" is at least 9 within 20 seconds +# Parachain should progress 3 blocks per relay chain block ideally, however CI might not be +# the most performant environment so we'd just use a lower bound of 2 blocks per RCB +elastic-validator-0: parachain 2000 block height is at least 20 within 200 seconds diff --git a/polkadot/zombienet_tests/elastic_scaling/assign-core.js b/polkadot/zombienet_tests/elastic_scaling/assign-core.js index 2e5f9d8cfa58ae26ef058f334ad782f1fbd9eb9f..add63b6d30859d2c50a38a73e33ec75ed3669433 100644 --- a/polkadot/zombienet_tests/elastic_scaling/assign-core.js +++ b/polkadot/zombienet_tests/elastic_scaling/assign-core.js @@ -1,6 +1,6 @@ async function run(nodeName, networkInfo, args) { - const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); + const wsUri = networkInfo.nodesByName[nodeName].wsUri; + const api = await zombie.connect(wsUri); let para = Number(args[0]); let core = Number(args[1]); @@ -33,8 +33,6 @@ async function run(nodeName, networkInfo, args) { }); }); - - return 0; } diff --git a/prdoc/pr_3695.prdoc b/prdoc/pr_3695.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2c2c2b2e6917819f5f4c3b2c0381e3f7e4890a17 --- /dev/null +++ b/prdoc/pr_3695.prdoc @@ -0,0 +1,38 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "pallet-xcm: add new extrinsic for asset transfers using explicit reserve" + +doc: + - audience: Runtime User + description: | + pallet-xcm has a new extrinsic `transfer_assets_using_type` for transferring + assets from local chain to destination chain using an explicit XCM transfer + types for transferring the assets and the fees: + - `TransferType::LocalReserve`: transfer assets to sovereign account of destination + chain and forward a notification XCM to `dest` to mint and deposit reserve-based + assets to `beneficiary`. + - `TransferType::DestinationReserve`: burn local assets and forward a notification to + `dest` chain to withdraw the reserve assets from this chain's sovereign account and + deposit them to `beneficiary`. + - `TransferType::RemoteReserve(reserve)`: burn local assets, forward XCM to `reserve` + chain to move reserves from this chain's SA to `dest` chain's SA, and forward another + XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. Typically + the remote `reserve` is Asset Hub. + - `TransferType::Teleport`: burn local assets and forward XCM to `dest` chain to + mint/teleport assets and deposit them to `beneficiary`. + By default, an asset's reserve is its origin chain. But sometimes we may want to + explicitly use another chain as reserve (as long as allowed by runtime IsReserve + filter). + This is very helpful for transferring assets with multiple configured reserves + (such as Asset Hub ForeignAssets), when the transfer strictly depends on the used + reserve location. + + E.g. For transferring a bridged Foreign Assets between local parachains, Asset Hub + or the parachain that bridged the asset over must be used as the reserve location. + Same when transferring bridged assets back across the bridge, the local bridging + parachain must be used as the explicit reserve location. + +crates: +- name: pallet-xcm + bump: minor diff --git a/prdoc/pr_3721.prdoc b/prdoc/pr_3721.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..be36103c474286cea613696e05dbba6ea5fc5782 --- /dev/null +++ b/prdoc/pr_3721.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: New call `hrmp.establish_channel_with_system` to allow parachains to establish a channel with a system parachain + +doc: + - audience: Runtime Dev + description: | + This PR adds a new call `hrmp.establish_channel_with_system` that allows a parachain origin to open a bidirectional channel with a system parachain. + +crates: +- name: polkadot-runtime-parachains + bump: minor diff --git a/prdoc/pr_4006.prdoc b/prdoc/pr_4006.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e6c339c406ac743f149ef14b3777a3214cfc10d2 --- /dev/null +++ b/prdoc/pr_4006.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Deploy pallet-parameters to rococo and fix dynamic_params name expand" + +doc: + - audience: Runtime Dev + description: | + Fix the expanded names of `dynamic_params` to not remove suffix "s". + + Also deploy the parameters pallet to the rococo-runtime. + +crates: + - name: frame-support-procedural + bump: major + - name: rococo-runtime + bump: major + - name: pallet-parameters + bump: patch diff --git a/prdoc/pr_4027.prdoc b/prdoc/pr_4027.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c85fd196a6c4a4af81e2d6882a424fb7a2d8b28d --- /dev/null +++ b/prdoc/pr_4027.prdoc @@ -0,0 +1,25 @@ +title: Add `candidates_pending_availability` Runtime API + +doc: + - audience: "Node Dev" + description: | + This new API retrieves all `CommittedCandidateReceipts` of all candidates pending availability + for a parachain at a given relay chain block number. It is required by collators that make use + of elastic scaling capability in the context of PoV recovery and block import. The old API + `candidate_pending_availability` is now deprectated and will be removed in the future. + +crates: + - name: polkadot-node-core-runtime-api + bump: minor + - name: polkadot-node-subsystem-types + bump: minor + - name: polkadot-node-subsystem-util + bump: minor + - name: polkadot-primitives + bump: minor + - name: polkadot-runtime-parachains + bump: minor + - name: cumulus-relay-chain-rpc-interface + bump: minor + - name: cumulus-relay-chain-minimal-node + bump: minor diff --git a/prdoc/pr_4037.prdoc b/prdoc/pr_4037.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7071875a7e370fdf11a0e88df1f6c0c5bb0a35a7 --- /dev/null +++ b/prdoc/pr_4037.prdoc @@ -0,0 +1,26 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Remove `xcm::v3` from `assets-common` nits" + +doc: + - audience: Runtime Dev + description: | + Remove `xcm::v3` imports from `assets-common` to make it more generic and facilitate the transition to newer XCM versions. + The implementations `AssetIdForTrustBackedAssetsConvert`, `ForeignAssetsConvertedConcreteId`, or `TrustBackedAssetsAsLocation` + used hard-coded `xcm::v3::Location`, which has been changed to use `xcm::latest::Location` by default. + Alternatively, the providing runtime can configure them according to its needs, such as with a lower XCM version. + + Example: + ```patch + - AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocationV3>, + + AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocationV3, xcm::v3::Location>, + ``` + + Another change is that the removed `xcm_builder::V4V3LocationConverter` can be replaced with `WithLatestLocationConverter`. + +crates: +- name: assets-common + bump: patch +- name: staging-xcm-builder + bump: patch diff --git a/prdoc/pr_4059.prdoc b/prdoc/pr_4059.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..92753328a433a65f327572c255375a9b5f8e45fd --- /dev/null +++ b/prdoc/pr_4059.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Remove redundent logging code + +doc: + - audience: Node Dev + description: | + Simplified logging code, now does slightly less work while logging. + +crates: +- name: sc-tracing + bump: minor diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index ef533b3cc80d2bb96c4be4e50e5f51ce9803662d..096b43a3739abb785bdd0a73b026c1eff5019cd9 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1602,6 +1602,7 @@ impl pallet_mmr::Config for Runtime { type Hashing = Keccak256; type LeafData = pallet_mmr::ParentNumberAndHash<Self>; type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest<Runtime>; + type BlockHashProvider = pallet_mmr::DefaultBlockHashProvider<Runtime>; type WeightInfo = (); } @@ -2257,7 +2258,7 @@ impl EnsureOriginWithArg<RuntimeOrigin, RuntimeParametersKey> for DynamicParamet frame_system::ensure_root(origin.clone()).map_err(|_| origin)?; return Ok(()) }, - RuntimeParametersKey::Contract(_) => { + RuntimeParametersKey::Contracts(_) => { frame_system::ensure_root(origin.clone()).map_err(|_| origin)?; return Ok(()) }, diff --git a/substrate/client/network/test/src/lib.rs b/substrate/client/network/test/src/lib.rs index 1dfe7d4454e9949f9ba9be08998363d77b5993a6..48a4b3d6e6e16a4281c6e504d1c0d9511d2eb677 100644 --- a/substrate/client/network/test/src/lib.rs +++ b/substrate/client/network/test/src/lib.rs @@ -393,13 +393,14 @@ where futures::executor::block_on(self.block_import.import_block(import_block)) .expect("block_import failed"); - if announce_block { - self.sync_service.announce_block(hash, None); - } hashes.push(hash); at = hash; } + if announce_block { + self.sync_service.announce_block(at, None); + } + if inform_sync_about_new_best_block { self.sync_service.new_best_block_imported( at, diff --git a/substrate/client/tracing/src/logging/event_format.rs b/substrate/client/tracing/src/logging/event_format.rs index 235d66cadc78a7a25358f24a40ffcfc1cb18e8ea..9589c1dfee28d11fe0dd7d409f9cec6e3563b812 100644 --- a/substrate/client/tracing/src/logging/event_format.rs +++ b/substrate/client/tracing/src/logging/event_format.rs @@ -21,12 +21,9 @@ use ansi_term::Colour; use regex::Regex; use std::fmt::{self, Write}; use tracing::{Event, Level, Subscriber}; -use tracing_log::NormalizeEvent; use tracing_subscriber::{ - field::RecordFields, fmt::{format, time::FormatTime, FmtContext, FormatEvent, FormatFields}, - layer::Context, - registry::{LookupSpan, SpanRef}, + registry::LookupSpan, }; /// A pre-configured event formatter. @@ -54,7 +51,7 @@ where // https://github.com/tokio-rs/tracing/blob/2f59b32/tracing-subscriber/src/fmt/format/mod.rs#L449 pub(crate) fn format_event_custom<'b, 'w, S, N>( &self, - ctx: CustomFmtContext<'b, S, N>, + ctx: &FmtContext<'b, S, N>, writer: format::Writer<'w>, event: &Event, ) -> fmt::Result @@ -63,12 +60,10 @@ where N: for<'a> FormatFields<'a> + 'static, { let mut writer = &mut ControlCodeSanitizer::new(!self.enable_color, writer); - let normalized_meta = event.normalized_metadata(); - let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); time::write(&self.timer, &mut format::Writer::new(&mut writer), self.enable_color)?; if self.display_level { - let fmt_level = { FmtLevel::new(meta.level(), self.enable_color) }; + let fmt_level = FmtLevel::new(event.metadata().level(), self.enable_color); write!(writer, "{} ", fmt_level)?; } @@ -86,7 +81,7 @@ where } if self.display_target { - write!(writer, "{}: ", meta.target())?; + write!(writer, "{}: ", event.metadata().target())?; } // Custom code to display node name @@ -137,12 +132,12 @@ where { let mut out = String::new(); let buf_writer = format::Writer::new(&mut out); - self.format_event_custom(CustomFmtContext::FmtContext(ctx), buf_writer, event)?; + self.format_event_custom(ctx, buf_writer, event)?; writer.write_str(&out)?; print!("{}", out); Ok(()) } else { - self.format_event_custom(CustomFmtContext::FmtContext(ctx), writer, event) + self.format_event_custom(ctx, writer, event) } } } @@ -261,48 +256,6 @@ mod time { } } -// NOTE: `FmtContext`'s fields are private. This enum allows us to make a `format_event` function -// that works with `FmtContext` or `Context` with `FormatFields` -#[allow(dead_code)] -pub(crate) enum CustomFmtContext<'a, S, N> { - FmtContext(&'a FmtContext<'a, S, N>), - ContextWithFormatFields(&'a Context<'a, S>, &'a N), -} - -impl<'a, S, N> FormatFields<'a> for CustomFmtContext<'a, S, N> -where - S: Subscriber + for<'lookup> LookupSpan<'lookup>, - N: for<'writer> FormatFields<'writer> + 'static, -{ - fn format_fields<R: RecordFields>(&self, writer: format::Writer<'_>, fields: R) -> fmt::Result { - match self { - CustomFmtContext::FmtContext(fmt_ctx) => fmt_ctx.format_fields(writer, fields), - CustomFmtContext::ContextWithFormatFields(_ctx, fmt_fields) => - fmt_fields.format_fields(writer, fields), - } - } -} - -// NOTE: the following code has been duplicated from tracing-subscriber -// -// https://github.com/tokio-rs/tracing/blob/2f59b32/tracing-subscriber/src/fmt/fmt_layer.rs#L788 -impl<'a, S, N> CustomFmtContext<'a, S, N> -where - S: Subscriber + for<'lookup> LookupSpan<'lookup>, - N: for<'writer> FormatFields<'writer> + 'static, -{ - #[inline] - pub fn lookup_current(&self) -> Option<SpanRef<'_, S>> - where - S: for<'lookup> LookupSpan<'lookup>, - { - match self { - CustomFmtContext::FmtContext(fmt_ctx) => fmt_ctx.lookup_current(), - CustomFmtContext::ContextWithFormatFields(ctx, _) => ctx.lookup_current(), - } - } -} - /// A writer which (optionally) strips out terminal control codes from the logs. /// /// This is used by [`EventFormat`] to sanitize the log messages. diff --git a/substrate/frame/beefy-mmr/src/mock.rs b/substrate/frame/beefy-mmr/src/mock.rs index 9d1ece7a1d8ecf433f6d244e55fb1f6e6211e77e..d59c219d3e71eae1c38975fc8bec137cc27b9076 100644 --- a/substrate/frame/beefy-mmr/src/mock.rs +++ b/substrate/frame/beefy-mmr/src/mock.rs @@ -90,6 +90,8 @@ impl pallet_mmr::Config for Test { type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest<Test>; + type BlockHashProvider = pallet_mmr::DefaultBlockHashProvider<Test>; + type WeightInfo = (); } diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index ef20bc8fb80b19eaddef8fa6011dac17ffa22609..c2e731462ca58bb042ffe08c3b2c59af2cf3954c 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -81,7 +81,8 @@ impl<T: Config> Pallet<T> { last_timeslice: Self::current_timeslice(), }; let now = frame_system::Pallet::<T>::block_number(); - let new_sale = SaleInfoRecord { + // Imaginary old sale for bootstrapping the first actual sale: + let old_sale = SaleInfoRecord { sale_start: now, leadin_length: Zero::zero(), price, @@ -94,7 +95,7 @@ impl<T: Config> Pallet<T> { cores_sold: 0, }; Self::deposit_event(Event::<T>::SalesStarted { price, core_count }); - Self::rotate_sale(new_sale, &config, &status); + Self::rotate_sale(old_sale, &config, &status); Status::<T>::put(&status); Ok(()) } diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index a39576b09013f7fef9893ff87ae093eae467af5d..330491eb2087f032435bb07636694bd27da470ef 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -552,16 +552,27 @@ pub mod pallet { /// /// - `origin`: Must be Root or pass `AdminOrigin`. /// - `initial_price`: The price of Bulk Coretime in the first sale. - /// - `core_count`: The number of cores which can be allocated. + /// - `total_core_count`: This is the total number of cores the relay chain should have + /// after the sale concludes. + /// + /// NOTE: This function does not actually request that new core count from the relay chain. + /// You need to make sure to call `request_core_count` afterwards to bring the relay chain + /// in sync. + /// + /// When to call the function depends on the new core count. If it is larger than what it + /// was before, you can call it immediately or even before `start_sales` as non allocated + /// cores will just be `Idle`. If you are actually reducing the number of cores, you should + /// call `request_core_count`, right before the next sale, to avoid shutting down tasks too + /// early. #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::start_sales((*core_count).into()))] + #[pallet::weight(T::WeightInfo::start_sales((*total_core_count).into()))] pub fn start_sales( origin: OriginFor<T>, initial_price: BalanceOf<T>, - core_count: CoreIndex, + total_core_count: CoreIndex, ) -> DispatchResultWithPostInfo { T::AdminOrigin::ensure_origin_or_root(origin)?; - Self::do_start_sales(initial_price, core_count)?; + Self::do_start_sales(initial_price, total_core_count)?; Ok(Pays::No.into()) } diff --git a/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs b/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs index 3c06131dd6088a2b044b68cfbf3b4a918eb37e3e..bf3c00b3ff1f5f3e78c164875d92742ea74a0cf0 100644 --- a/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs +++ b/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs @@ -15,87 +15,19 @@ // along with Polkadot. If not, see <http://www.gnu.org/licenses/>. use super::{Balances, Runtime, RuntimeCall, RuntimeEvent}; -use crate::{ - parachain, - parachain::RuntimeHoldReason, - primitives::{Balance, CENTS}, -}; -use frame_support::{ - parameter_types, - traits::{ConstBool, ConstU32, Contains, Randomness}, - weights::Weight, -}; -use frame_system::{pallet_prelude::BlockNumberFor, EnsureSigned}; -use pallet_xcm::BalanceOf; -use sp_runtime::{traits::Convert, Perbill}; - -pub const fn deposit(items: u32, bytes: u32) -> Balance { - items as Balance * 1 * CENTS + (bytes as Balance) * 1 * CENTS -} +use crate::parachain::RuntimeHoldReason; +use frame_support::{derive_impl, parameter_types}; parameter_types! { - pub const DepositPerItem: Balance = deposit(1, 0); - pub const DepositPerByte: Balance = deposit(0, 1); - pub const DefaultDepositLimit: Balance = deposit(1024, 1024 * 1024); pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default(); - pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); - pub const MaxDelegateDependencies: u32 = 32; -} - -pub struct DummyRandomness<T: pallet_contracts::Config>(sp_std::marker::PhantomData<T>); - -impl<T: pallet_contracts::Config> Randomness<T::Hash, BlockNumberFor<T>> for DummyRandomness<T> { - fn random(_subject: &[u8]) -> (T::Hash, BlockNumberFor<T>) { - (Default::default(), Default::default()) - } -} - -impl Convert<Weight, BalanceOf<Self>> for Runtime { - fn convert(w: Weight) -> BalanceOf<Self> { - w.ref_time().into() - } -} - -#[derive(Clone, Default)] -pub struct Filters; - -impl Contains<RuntimeCall> for Filters { - fn contains(call: &RuntimeCall) -> bool { - match call { - parachain::RuntimeCall::Contracts(_) => true, - _ => false, - } - } } +#[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] impl pallet_contracts::Config for Runtime { type AddressGenerator = pallet_contracts::DefaultAddressGenerator; - type CallFilter = Filters; type CallStack = [pallet_contracts::Frame<Self>; 5]; - type ChainExtension = (); - type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type Currency = Balances; - type DefaultDepositLimit = DefaultDepositLimit; - type DepositPerByte = DepositPerByte; - type DepositPerItem = DepositPerItem; - type MaxCodeLen = ConstU32<{ 123 * 1024 }>; - type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; - type MaxDelegateDependencies = MaxDelegateDependencies; - type MaxStorageKeyLen = ConstU32<128>; - type Migrations = (); - type Randomness = DummyRandomness<Self>; - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type RuntimeHoldReason = RuntimeHoldReason; type Schedule = Schedule; type Time = super::Timestamp; - type UnsafeUnstableInterface = ConstBool<true>; - type UploadOrigin = EnsureSigned<Self::AccountId>; - type InstantiateOrigin = EnsureSigned<Self::AccountId>; - type WeightInfo = (); - type WeightPrice = Self; - type Debug = (); - type Environment = (); - type ApiVersion = (); type Xcm = pallet_xcm::Pallet<Self>; } diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index 73c70a7704e202845a6ce4edc361527b6c125fbe..edc4c872bfce14e21d84ca14fc8877d88c362ff0 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -250,7 +250,7 @@ pub mod pallet { #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet<T>(_); - #[pallet::config] + #[pallet::config(with_default)] pub trait Config: frame_system::Config { /// The time implementation used to supply timestamps to contracts through `seal_now`. type Time: Time; @@ -263,22 +263,30 @@ pub mod pallet { /// be instantiated from existing codes that use this deprecated functionality. It will /// be removed eventually. Hence for new `pallet-contracts` deployments it is okay /// to supply a dummy implementation for this type (because it is never used). + #[pallet::no_default_bounds] type Randomness: Randomness<Self::Hash, BlockNumberFor<Self>>; /// The fungible in which fees are paid and contract balances are held. + #[pallet::no_default] type Currency: Inspect<Self::AccountId> + Mutate<Self::AccountId> + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>; /// The overarching event type. + #[pallet::no_default_bounds] type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// The overarching call type. + #[pallet::no_default_bounds] type RuntimeCall: Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo> + GetDispatchInfo + codec::Decode + IsType<<Self as frame_system::Config>::RuntimeCall>; + /// Overarching hold reason. + #[pallet::no_default_bounds] + type RuntimeHoldReason: From<HoldReason>; + /// Filter that is applied to calls dispatched by contracts. /// /// Use this filter to control which dispatchables are callable by contracts. @@ -301,10 +309,12 @@ pub mod pallet { /// /// This filter does not apply to XCM transact calls. To impose restrictions on XCM transact /// calls, you must configure them separately within the XCM pallet itself. + #[pallet::no_default_bounds] type CallFilter: Contains<<Self as frame_system::Config>::RuntimeCall>; /// Used to answer contracts' queries regarding the current weight price. This is **not** /// used to calculate the actual fee and is only for informational purposes. + #[pallet::no_default_bounds] type WeightPrice: Convert<Weight, BalanceOf<Self>>; /// Describes the weights of the dispatchables of this module and is also used to @@ -312,10 +322,12 @@ pub mod pallet { type WeightInfo: WeightInfo; /// Type that allows the runtime authors to add new host functions for a contract to call. + #[pallet::no_default_bounds] type ChainExtension: chain_extension::ChainExtension<Self> + Default; /// Cost schedule and limits. #[pallet::constant] + #[pallet::no_default] type Schedule: Get<Schedule<Self>>; /// The type of the call stack determines the maximum nesting depth of contract calls. @@ -326,6 +338,7 @@ pub mod pallet { /// /// This setting along with [`MaxCodeLen`](#associatedtype.MaxCodeLen) directly affects /// memory usage of your runtime. + #[pallet::no_default] type CallStack: Array<Item = Frame<Self>>; /// The amount of balance a caller has to pay for each byte of storage. @@ -334,10 +347,12 @@ pub mod pallet { /// /// Changing this value for an existing chain might need a storage migration. #[pallet::constant] + #[pallet::no_default_bounds] type DepositPerByte: Get<BalanceOf<Self>>; /// Fallback value to limit the storage deposit if it's not being set by the caller. #[pallet::constant] + #[pallet::no_default_bounds] type DefaultDepositLimit: Get<BalanceOf<Self>>; /// The amount of balance a caller has to pay for each storage item. @@ -346,6 +361,7 @@ pub mod pallet { /// /// Changing this value for an existing chain might need a storage migration. #[pallet::constant] + #[pallet::no_default_bounds] type DepositPerItem: Get<BalanceOf<Self>>; /// The percentage of the storage deposit that should be held for using a code hash. @@ -356,6 +372,7 @@ pub mod pallet { type CodeHashLockupDepositPercent: Get<Perbill>; /// The address generator used to generate the addresses of contracts. + #[pallet::no_default_bounds] type AddressGenerator: AddressGenerator<Self>; /// The maximum length of a contract code in bytes. @@ -395,6 +412,7 @@ pub mod pallet { /// /// By default, it is safe to set this to `EnsureSigned`, allowing anyone to upload contract /// code. + #[pallet::no_default_bounds] type UploadOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>; /// Origin allowed to instantiate code. @@ -407,11 +425,9 @@ pub mod pallet { /// /// By default, it is safe to set this to `EnsureSigned`, allowing anyone to instantiate /// contract code. + #[pallet::no_default_bounds] type InstantiateOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>; - /// Overarching hold reason. - type RuntimeHoldReason: From<HoldReason>; - /// The sequence of migration steps that will be applied during a migration. /// /// # Examples @@ -435,6 +451,7 @@ pub mod pallet { /// For most production chains, it's recommended to use the `()` implementation of this /// trait. This implementation offers additional logging when the log target /// "runtime::contracts" is set to trace. + #[pallet::no_default_bounds] type Debug: Debugger<Self>; /// Type that bundles together all the runtime configurable interface types. @@ -442,16 +459,19 @@ pub mod pallet { /// This is not a real config. We just mention the type here as constant so that /// its type appears in the metadata. Only valid value is `()`. #[pallet::constant] + #[pallet::no_default_bounds] type Environment: Get<Environment<Self>>; /// The version of the HostFn APIs that are available in the runtime. /// /// Only valid value is `()`. #[pallet::constant] + #[pallet::no_default_bounds] type ApiVersion: Get<ApiVersion>; /// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and /// execute XCM programs. + #[pallet::no_default_bounds] type Xcm: xcm_builder::Controller< OriginFor<Self>, <Self as frame_system::Config>::RuntimeCall, @@ -459,6 +479,95 @@ pub mod pallet { >; } + /// Container for different types that implement [`DefaultConfig`]` of this pallet. + pub mod config_preludes { + use super::*; + use frame_support::{ + derive_impl, + traits::{ConstBool, ConstU32}, + }; + use frame_system::EnsureSigned; + use sp_core::parameter_types; + + type AccountId = sp_runtime::AccountId32; + type Balance = u64; + const UNITS: Balance = 10_000_000_000; + const CENTS: Balance = UNITS / 100; + + const fn deposit(items: u32, bytes: u32) -> Balance { + items as Balance * 1 * CENTS + (bytes as Balance) * 1 * CENTS + } + + parameter_types! { + pub const DepositPerItem: Balance = deposit(1, 0); + pub const DepositPerByte: Balance = deposit(0, 1); + pub const DefaultDepositLimit: Balance = deposit(1024, 1024 * 1024); + pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); + pub const MaxDelegateDependencies: u32 = 32; + } + + /// A type providing default configurations for this pallet in testing environment. + pub struct TestDefaultConfig; + + impl<Output, BlockNumber> Randomness<Output, BlockNumber> for TestDefaultConfig { + fn random(_subject: &[u8]) -> (Output, BlockNumber) { + unimplemented!("No default `random` implementation in `TestDefaultConfig`, provide a custom `T::Randomness` type.") + } + } + + impl Time for TestDefaultConfig { + type Moment = u64; + fn now() -> Self::Moment { + unimplemented!("No default `now` implementation in `TestDefaultConfig` provide a custom `T::Time` type.") + } + } + + impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig { + fn convert(w: Weight) -> T { + w.ref_time().into() + } + } + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)] + impl frame_system::DefaultConfig for TestDefaultConfig {} + + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + #[inject_runtime_type] + type RuntimeEvent = (); + + #[inject_runtime_type] + type RuntimeHoldReason = (); + + #[inject_runtime_type] + type RuntimeCall = (); + + type AddressGenerator = DefaultAddressGenerator; + type CallFilter = (); + type ChainExtension = (); + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type DefaultDepositLimit = DefaultDepositLimit; + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; + type MaxDelegateDependencies = MaxDelegateDependencies; + type MaxStorageKeyLen = ConstU32<128>; + type Migrations = (); + type Time = Self; + type Randomness = Self; + type UnsafeUnstableInterface = ConstBool<true>; + type UploadOrigin = EnsureSigned<AccountId>; + type InstantiateOrigin = EnsureSigned<AccountId>; + type WeightInfo = (); + type WeightPrice = Self; + type Debug = (); + type Environment = (); + type ApiVersion = (); + type Xcm = (); + } + } + #[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight { diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs index 0b83358a7f56a134a938076aab5f20b5915f0826..57b804a51e4175a9c92683e9431a95de8c2cb397 100644 --- a/substrate/frame/contracts/src/tests.rs +++ b/substrate/frame/contracts/src/tests.rs @@ -334,34 +334,24 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { + type Block = Block; type AccountId = AccountId32; type Lookup = IdentityLookup<Self::AccountId>; - type Block = Block; type AccountData = pallet_balances::AccountData<u64>; } + impl pallet_insecure_randomness_collective_flip::Config for Test {} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Test { - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type Balance = u64; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; + type ReserveIdentifier = [u8; 8]; type AccountStore = System; - type WeightInfo = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; } -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<1>; - type WeightInfo = (); -} +#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] +impl pallet_timestamp::Config for Test {} + impl pallet_utility::Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -467,16 +457,13 @@ parameter_types! { pub static UnstableInterface: bool = true; } +#[derive_impl(crate::config_preludes::TestDefaultConfig)] impl Config for Test { type Time = Timestamp; type Randomness = Randomness; type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; type CallFilter = TestFilter; type CallStack = [Frame<Self>; 5]; - type WeightPrice = Self; - type WeightInfo = (); type ChainExtension = (TestExtension, DisabledExtension, RevertingExtension, TempStorageExtension); type Schedule = MySchedule; @@ -484,20 +471,13 @@ impl Config for Test { type DepositPerItem = DepositPerItem; type DefaultDepositLimit = DefaultDepositLimit; type AddressGenerator = DefaultAddressGenerator; - type MaxCodeLen = ConstU32<{ 123 * 1024 }>; - type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = UnstableInterface; type UploadOrigin = EnsureAccount<Self, UploadAccount>; type InstantiateOrigin = EnsureAccount<Self, InstantiateAccount>; - type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; - type RuntimeHoldReason = RuntimeHoldReason; type Migrations = crate::migration::codegen::BenchMigrations; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type MaxDelegateDependencies = MaxDelegateDependencies; type Debug = TestDebug; - type Environment = (); - type ApiVersion = (); - type Xcm = (); } pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); diff --git a/substrate/frame/merkle-mountain-range/src/lib.rs b/substrate/frame/merkle-mountain-range/src/lib.rs index 7b6edb37b7f7bc8608a417aa9eff6f0713edf9df..e2b40974579e8376723f466198d74755603285d0 100644 --- a/substrate/frame/merkle-mountain-range/src/lib.rs +++ b/substrate/frame/merkle-mountain-range/src/lib.rs @@ -103,6 +103,24 @@ impl<T: frame_system::Config> LeafDataProvider for ParentNumberAndHash<T> { } } +/// Block hash provider for a given block number. +pub trait BlockHashProvider<BlockNumber, BlockHash> { + fn block_hash(block_number: BlockNumber) -> BlockHash; +} + +/// Default implementation of BlockHashProvider using frame_system. +pub struct DefaultBlockHashProvider<T: frame_system::Config> { + _phantom: sp_std::marker::PhantomData<T>, +} + +impl<T: frame_system::Config> BlockHashProvider<BlockNumberFor<T>, T::Hash> + for DefaultBlockHashProvider<T> +{ + fn block_hash(block_number: BlockNumberFor<T>) -> T::Hash { + frame_system::Pallet::<T>::block_hash(block_number) + } +} + pub trait WeightInfo { fn on_initialize(peaks: NodeIndex) -> Weight; } @@ -177,6 +195,12 @@ pub mod pallet { /// Clients. Hook complexity should be `O(1)`. type OnNewRoot: primitives::OnNewRoot<HashOf<Self, I>>; + /// Block hash provider for a given block number. + type BlockHashProvider: BlockHashProvider< + BlockNumberFor<Self>, + <Self as frame_system::Config>::Hash, + >; + /// Weights for this pallet. type WeightInfo: WeightInfo; } diff --git a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs index 96a20c3445eedea0bfa3d02e99ed07022d30da80..f2acc35a137ffb46a39396762eafe30d7296e017 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs @@ -29,7 +29,7 @@ use sp_std::prelude::*; use crate::{ mmr::{Node, NodeOf}, primitives::{self, NodeIndex}, - Config, Nodes, NumberOfLeaves, Pallet, + BlockHashProvider, Config, Nodes, NumberOfLeaves, Pallet, }; /// A marker type for runtime-specific storage implementation. @@ -87,7 +87,7 @@ where // Fall through to searching node using fork-specific key. let ancestor_parent_block_num = Pallet::<T, I>::leaf_index_to_parent_block_num(ancestor_leaf_idx, leaves); - let ancestor_parent_hash = <frame_system::Pallet<T>>::block_hash(ancestor_parent_block_num); + let ancestor_parent_hash = T::BlockHashProvider::block_hash(ancestor_parent_block_num); let temp_key = Pallet::<T, I>::node_temp_offchain_key(pos, ancestor_parent_hash); debug!( target: "runtime::mmr::offchain", diff --git a/substrate/frame/merkle-mountain-range/src/mock.rs b/substrate/frame/merkle-mountain-range/src/mock.rs index 212012a052a027cd0582b90e3a29a409980c4e66..8318b20e83074cd3bcb540b854fd7abae998438e 100644 --- a/substrate/frame/merkle-mountain-range/src/mock.rs +++ b/substrate/frame/merkle-mountain-range/src/mock.rs @@ -44,6 +44,7 @@ impl Config for Test { type Hashing = Keccak256; type LeafData = Compact<Keccak256, (ParentNumberAndHash<Test>, LeafData)>; type OnNewRoot = (); + type BlockHashProvider = DefaultBlockHashProvider<Test>; type WeightInfo = (); } diff --git a/substrate/frame/parameters/src/tests/mock.rs b/substrate/frame/parameters/src/tests/mock.rs index 4c7dda639a9ae762c94b355174e7e4ca59869afe..6cfd7c8f30b8119ff1e1f0a63165c98afba1577b 100644 --- a/substrate/frame/parameters/src/tests/mock.rs +++ b/substrate/frame/parameters/src/tests/mock.rs @@ -16,6 +16,7 @@ // limitations under the License. #![cfg(any(test, feature = "runtime-benchmarks"))] +#![allow(non_snake_case)] //! Mock runtime that configures the `pallet_example_basic` to use dynamic params for testing. @@ -66,6 +67,20 @@ pub mod dynamic_params { #[codec(index = 0)] pub static Key3: u128 = 4; } + + #[dynamic_pallet_params] + #[codec(index = 2)] + pub mod nis { + #[codec(index = 0)] + pub static Target: u64 = 0; + } + + #[dynamic_pallet_params] + #[codec(index = 3)] + pub mod somE_weird_SPElLInG_s { + #[codec(index = 0)] + pub static V: u64 = 0; + } } #[docify::export(benchmarking_default)] @@ -98,6 +113,8 @@ mod custom_origin { } match key { + RuntimeParametersKey::SomEWeirdSPElLInGS(_) | + RuntimeParametersKey::Nis(_) | RuntimeParametersKey::Pallet1(_) => ensure_root(origin.clone()), RuntimeParametersKey::Pallet2(_) => ensure_signed(origin.clone()).map(|_| ()), } diff --git a/substrate/frame/support/procedural/src/dynamic_params.rs b/substrate/frame/support/procedural/src/dynamic_params.rs index 29399a885bc671ddfeefe3d8eefba3d235ad4899..ad62f59e6b0a04ea50c02375d6e1f421fc5689aa 100644 --- a/substrate/frame/support/procedural/src/dynamic_params.rs +++ b/substrate/frame/support/procedural/src/dynamic_params.rs @@ -91,7 +91,7 @@ impl ToTokens for DynamicParamModAttr { let mut quoted_enum = quote! {}; for m in self.inner_mods() { let aggregate_name = - syn::Ident::new(&m.ident.to_string().to_class_case(), m.ident.span()); + syn::Ident::new(&m.ident.to_string().to_pascal_case(), m.ident.span()); let mod_name = &m.ident; let mut attrs = m.attrs.clone(); @@ -222,8 +222,10 @@ impl ToTokens for DynamicPalletParamAttr { let (params_mod, parameter_pallet, runtime_params) = (&self.inner_mod, &self.meta.parameter_pallet, &self.meta.runtime_params); - let aggregate_name = - syn::Ident::new(¶ms_mod.ident.to_string().to_class_case(), params_mod.ident.span()); + let aggregate_name = syn::Ident::new( + ¶ms_mod.ident.to_string().to_pascal_case(), + params_mod.ident.span(), + ); let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis); let statics = self.statics();