From 43faca7ce49c784e599eded8e2cb6898c2057e1d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 27 Mar 2024 22:45:47 +0100 Subject: [PATCH 01/61] feat(xcm-fee-payment-runtime-api): add XcmTransfersApi and tests --- Cargo.lock | 6 + polkadot/xcm/pallet-xcm/src/lib.rs | 18 +- .../xcm-fee-payment-runtime-api/Cargo.toml | 14 ++ .../xcm-fee-payment-runtime-api/src/fees.rs | 97 +++++++ .../xcm-fee-payment-runtime-api/src/lib.rs | 94 ++----- .../src/transfers.rs | 39 +++ .../tests/fee_estimation.rs | 80 ++++++ .../xcm-fee-payment-runtime-api/tests/mock.rs | 236 ++++++++++++++++++ 8 files changed, 496 insertions(+), 88 deletions(-) create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/src/transfers.rs create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs create mode 100644 polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs diff --git a/Cargo.lock b/Cargo.lock index 2382dc8d1624..4d0d4d7bfac4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22454,13 +22454,19 @@ name = "xcm-fee-payment-runtime-api" version = "0.1.0" dependencies = [ "frame-support", + "frame-system", + "pallet-balances", + "pallet-xcm", "parity-scale-codec", "scale-info", "sp-api", + "sp-io", "sp-runtime", "sp-std 14.0.0", "sp-weights", "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", ] [[package]] diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 29b61988f73c..a7940c7ddaa3 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -61,7 +61,7 @@ use xcm_executor::{ }, AssetsInHolding, }; -use xcm_fee_payment_runtime_api::Error as FeePaymentError; +use xcm_fee_payment_runtime_api::XcmPaymentApiError; #[cfg(any(feature = "try-runtime", test))] use sp_runtime::TryRuntimeError; @@ -2458,35 +2458,35 @@ impl Pallet { AccountIdConversion::::into_account_truncating(&ID) } - pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result { let message = - Xcm::<()>::try_from(message).map_err(|_| FeePaymentError::VersionedConversionFailed)?; + Xcm::<()>::try_from(message).map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; T::Weigher::weight(&mut message.into()).map_err(|()| { log::error!(target: "xcm::pallet_xcm::query_xcm_weight", "Error when querying XCM weight"); - FeePaymentError::WeightNotComputable + XcmPaymentApiError::WeightNotComputable }) } pub fn query_delivery_fees( destination: VersionedLocation, message: VersionedXcm<()>, - ) -> Result { + ) -> Result { let result_version = destination.identify_version().max(message.identify_version()); let destination = - destination.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?; + destination.try_into().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; - let message = message.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?; + let message = message.try_into().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; let (_, fees) = validate_send::(destination, message).map_err(|error| { log::error!(target: "xcm::pallet_xcm::query_delivery_fees", "Error when querying delivery fees: {:?}", error); - FeePaymentError::Unroutable + XcmPaymentApiError::Unroutable })?; VersionedAssets::from(fees) .into_version(result_version) - .map_err(|_| FeePaymentError::VersionedConversionFailed) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed) } /// Create a new expectation of a query response with the querier being here. diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index 682642d13c3a..24d95c1c31ee 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -26,6 +26,14 @@ sp-weights = { path = "../../../substrate/primitives/weights", default-features xcm = { package = "staging-xcm", path = "../", default-features = false } frame-support = { path = "../../../substrate/frame/support", default-features = false } +[dev-dependencies] +frame-system = { path = "../../../substrate/frame/system", default-features = false } +pallet-xcm = { path = "../pallet-xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false } +sp-io = { path = "../../../substrate/primitives/io", default-features = false } +pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } + [features] default = ["std"] std = [ @@ -37,4 +45,10 @@ std = [ "sp-std/std", "sp-weights/std", "xcm/std", + "frame-system/std", + "pallet-xcm/std", + "xcm-builder/std", + "sp-io/std", + "pallet-balances/std", + "xcm-executor/std", ] diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs new file mode 100644 index 000000000000..796bfda880bd --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs @@ -0,0 +1,97 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate 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. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Runtime API definition for getting xcm fees. + +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::TypeInfo; +use sp_std::vec::Vec; +use sp_weights::Weight; +use xcm::{Version, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; + +sp_api::decl_runtime_apis! { + /// A trait of XCM payment API. + /// + /// API provides functionality for obtaining: + /// + /// * the weight required to execute an XCM message, + /// * a list of acceptable `AssetId`s for message execution payment, + /// * the cost of the weight in the specified acceptable `AssetId`. + /// * the fees for an XCM message delivery. + /// + /// To determine the execution weight of the calls required for + /// [`xcm::latest::Instruction::Transact`] instruction, `TransactionPaymentCallApi` can be used. + pub trait XcmPaymentApi { + /// Returns a list of acceptable payment assets. + /// + /// # Arguments + /// + /// * `xcm_version`: Version. + fn query_acceptable_payment_assets(xcm_version: Version) -> Result, Error>; + + /// Returns a weight needed to execute a XCM. + /// + /// # Arguments + /// + /// * `message`: `VersionedXcm`. + fn query_xcm_weight(message: VersionedXcm<()>) -> Result; + + /// Converts a weight into a fee for the specified `AssetId`. + /// + /// # Arguments + /// + /// * `weight`: convertible `Weight`. + /// * `asset`: `VersionedAssetId`. + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result; + + /// Get delivery fees for sending a specific `message` to a `destination`. + /// These always come in a specific asset, defined by the chain. + /// + /// # Arguments + /// * `message`: The message that'll be sent, necessary because most delivery fees are based on the + /// size of the message. + /// * `destination`: The destination to send the message to. Different destinations may use + /// different senders that charge different fees. + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result; + } +} + +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +pub enum Error { + /// An API part is unsupported. + #[codec(index = 0)] + Unimplemented, + + /// Converting a versioned data structure from one version to another failed. + #[codec(index = 1)] + VersionedConversionFailed, + + /// XCM message weight calculation failed. + #[codec(index = 2)] + WeightNotComputable, + + /// XCM version not able to be handled. + #[codec(index = 3)] + UnhandledXcmVersion, + + /// The given asset is not handled as a fee asset. + #[codec(index = 4)] + AssetNotFound, + + /// Destination is known to be unroutable. + #[codec(index = 5)] + Unroutable, +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs index 20bf9236f1fb..e7a629ca5495 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs @@ -14,86 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Runtime API definition for xcm transaction payment. +//! Runtime APIs for estimating xcm fee payment. +//! This crate offers two APIs, one for estimating fees, +//! which can be used for any type of message, and another one +//! for returning the specific messages used for transfers, a common +//! feature. +//! Users of these APIs should call the transfers API and pass the result to the +//! fees API. #![cfg_attr(not(feature = "std"), no_std)] -use codec::{Decode, Encode}; -use frame_support::pallet_prelude::TypeInfo; -use sp_std::vec::Vec; -use sp_weights::Weight; -use xcm::{Version, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}; +/// Main API. +/// Estimates fees. +mod fees; +/// Transfers API. +/// Returns the messages that need to be passed to the fees API. +mod transfers; -sp_api::decl_runtime_apis! { - /// A trait of XCM payment API. - /// - /// API provides functionality for obtaining: - /// - /// * the weight required to execute an XCM message, - /// * a list of acceptable `AssetId`s for message execution payment, - /// * the cost of the weight in the specified acceptable `AssetId`. - /// * the fees for an XCM message delivery. - /// - /// To determine the execution weight of the calls required for - /// [`xcm::latest::Instruction::Transact`] instruction, `TransactionPaymentCallApi` can be used. - pub trait XcmPaymentApi { - /// Returns a list of acceptable payment assets. - /// - /// # Arguments - /// - /// * `xcm_version`: Version. - fn query_acceptable_payment_assets(xcm_version: Version) -> Result, Error>; - - /// Returns a weight needed to execute a XCM. - /// - /// # Arguments - /// - /// * `message`: `VersionedXcm`. - fn query_xcm_weight(message: VersionedXcm<()>) -> Result; - - /// Converts a weight into a fee for the specified `AssetId`. - /// - /// # Arguments - /// - /// * `weight`: convertible `Weight`. - /// * `asset`: `VersionedAssetId`. - fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result; - - /// Get delivery fees for sending a specific `message` to a `destination`. - /// These always come in a specific asset, defined by the chain. - /// - /// # Arguments - /// * `message`: The message that'll be sent, necessary because most delivery fees are based on the - /// size of the message. - /// * `destination`: The destination to send the message to. Different destinations may use - /// different senders that charge different fees. - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result; - } -} - -#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] -pub enum Error { - /// An API part is unsupported. - #[codec(index = 0)] - Unimplemented, - - /// Converting a versioned data structure from one version to another failed. - #[codec(index = 1)] - VersionedConversionFailed, - - /// XCM message weight calculation failed. - #[codec(index = 2)] - WeightNotComputable, - - /// XCM version not able to be handled. - #[codec(index = 3)] - UnhandledXcmVersion, - - /// The given asset is not handled as a fee asset. - #[codec(index = 4)] - AssetNotFound, - - /// Destination is known to be unroutable. - #[codec(index = 5)] - Unroutable, -} +pub use fees::{XcmPaymentApi, Error as XcmPaymentApiError}; +pub use transfers::XcmTransfersApi; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/transfers.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/transfers.rs new file mode 100644 index 000000000000..5ea1421c2489 --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/transfers.rs @@ -0,0 +1,39 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate 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. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Runtime API definition for getting xcm transfer messages. +//! These messages can be used to get the fees that need to be paid. + +use sp_std::vec::Vec; +use xcm::prelude::*; + +sp_api::decl_runtime_apis! { + /// API for obtaining the messages for different types of cross-chain transfers. + /// + /// All calls return a vector of tuples (location, xcm) where each "xcm" is executed in "location". + /// If there's local execution, the location will be "Here". + /// This vector can be used to calculate both execution and delivery fees. + pub trait XcmTransfersApi { + /// Generic transfer, will figure out if it's a teleport or a reserve transfer. + fn transfer_assets() -> Vec<(Location, Xcm<()>)>; + + /// Returns messages for a teleport. + fn teleport_assets(dest: Location, beneficiary: Location, assets: Assets) -> Vec<(VersionedLocation, VersionedXcm<()>)>; + + /// Returns messages for a reserve transfer. + fn reserve_transfer_assets() -> Vec<(Location, Xcm<()>)>; + } +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs new file mode 100644 index 000000000000..29be8fbc7193 --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -0,0 +1,80 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate 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. + +// Substrate 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 . + +//! Tests for using both the XCM fee payment API and the transfers API. + +use sp_api::ProvideRuntimeApi; +use sp_runtime::testing::H256; +use xcm_fee_payment_runtime_api::{XcmPaymentApi, XcmTransfersApi}; +use xcm::prelude::*; + +mod mock; +use mock::{TestClient, HereLocation}; + +#[test] +fn can_get_both_execution_and_delivery_fees_for_a_transfer() { + let client = TestClient; + let runtime_api = client.runtime_api(); + let messages = runtime_api.teleport_assets( + H256::zero(), + (Parent, Parachain(1000)).into(), + AccountId32 { id: [0u8; 32], network: None }.into(), + (Here, 100u128).into(), + ).unwrap(); + // assert_eq!(messages, [...]); + + let mut messages_iter = messages.iter(); + + let (_, local_message) = messages_iter.next().unwrap(); + + // We get a double result since the actual call returns a result and the runtime api returns results. + let weight = runtime_api.query_xcm_weight( + H256::zero(), + local_message.clone(), + ).unwrap().unwrap(); + assert_eq!(weight, Weight::from_parts(2_000_000_000_000, 2 * 1024 * 1024)); + let execution_fees = runtime_api.query_weight_to_asset_fee( + H256::zero(), + weight, + VersionedAssetId::V4(HereLocation::get().into()) + ).unwrap().unwrap(); + assert_eq!(execution_fees, 2_000_002_097_152); + + let (destination, remote_message) = messages_iter.next().unwrap(); + + let delivery_fees = runtime_api.query_delivery_fees( + H256::zero(), + destination.clone(), + remote_message.clone(), + ).unwrap().unwrap(); + + // This would have to be the runtime API of the destination, + // which we have the location for. + let remote_execution_weight = runtime_api.query_xcm_weight( + H256::zero(), + remote_message.clone(), + ).unwrap().unwrap(); + let remote_execution_fees = runtime_api.query_weight_to_asset_fee( + H256::zero(), + remote_execution_weight, + VersionedAssetId::V4(HereLocation::get().into()), + ); + + // Now we know that locally we need to use `execution_fees` and + // `delivery_fees`. + // On the message we forward to the destination, we need to + // put `remote_execution_fees` in `BuyExecution`. +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs new file mode 100644 index 000000000000..499284831aef --- /dev/null +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -0,0 +1,236 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate 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. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Mock runtime for tests. +//! Implements both runtime APIs for fee estimation and getting the messages for transfers. + +use codec::Encode; +use frame_system::EnsureRoot; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{Nothing, ConstU32, ConstU128}, + weights::WeightToFee as WeightToFeeT, +}; +use pallet_xcm::TestWeightInfo; +use sp_runtime::{AccountId32, traits::IdentityLookup, SaturatedConversion}; +use xcm::{prelude::*, Version as XcmVersion}; +use xcm_builder::{EnsureXcmOrigin, FixedWeightBounds, IsConcrete}; +use xcm_executor::XcmExecutor; + +use xcm_fee_payment_runtime_api::{XcmTransfersApi, XcmPaymentApi, XcmPaymentApiError}; + +construct_runtime! { + pub enum Test { + System: frame_system, + Balances: pallet_balances, + XcmPallet: pallet_xcm, + } +} + +type Block = frame_system::mocking::MockBlock; +type Balance = u128; +type AccountId = AccountId32; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; + type AccountId = AccountId; + type AccountData = pallet_balances::AccountData; + type Lookup = IdentityLookup; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type AccountStore = System; + type Balance = Balance; + type ExistentialDeposit = ConstU128<1>; +} + +pub struct TestSendXcm; +impl SendXcm for TestSendXcm { + type Ticket = (Location, Xcm<()>); + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult { + let ticket = (dest.take().unwrap(), msg.take().unwrap()); + let fees: Assets = (HereLocation::get(), 1_000_000_000_000u128).into(); + Ok((ticket, fees)) + } + fn deliver(ticket: Self::Ticket) -> Result { + let hash = fake_message_hash(&ticket.1); + Ok(hash) + } +} + +fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(sp_io::hashing::blake2_256) +} + +pub type XcmRouter = TestSendXcm; + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000_000, 1024 * 1024); + pub const MaxInstructions: u32 = 100; + pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Westend)].into(); + pub static AdvertisedXcmVersion: XcmVersion = 4; + pub const HereLocation: Location = Location::here(); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +/// Simple `WeightToFee` implementation that adds the ref_time by the proof_size. +pub struct WeightToFee; +impl WeightToFeeT for WeightToFee { + type Balance = Balance; + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_add(Self::Balance::saturated_from(weight.proof_size())) + } +} + +type Weigher = FixedWeightBounds; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = (); + type OriginConverter = (); + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = (); + type Weigher = Weigher; + type Trader = (); + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = (); + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Nothing; + type Aliasers = Nothing; + type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); +} + +impl pallet_xcm::Config for Test { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Nothing; + type Weigher = Weigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = AdvertisedXcmVersion; + type AdminOrigin = EnsureRoot; + type TrustedLockers = (); + type SovereignAccountOf = (); + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type MaxLockers = ConstU32<0>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type WeightInfo = TestWeightInfo; +} + +#[derive(Clone)] +pub(crate) struct TestClient; + +pub(crate) struct RuntimeApi { + _inner: TestClient, +} + +impl sp_api::ProvideRuntimeApi for TestClient { + type Api = RuntimeApi; + fn runtime_api(&self) -> sp_api::ApiRef { + RuntimeApi { _inner: self.clone() }.into() + } +} + +sp_api::mock_impl_runtime_apis! { + impl XcmPaymentApi for RuntimeApi { + fn query_acceptable_payment_assets(xcm_version: XcmVersion) -> Result, XcmPaymentApiError> { + todo!() + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + XcmPallet::query_xcm_weight(message) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(HereLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + XcmPallet::query_delivery_fees(destination, message) + } + } + + impl XcmTransfersApi for RuntimeApi { + fn transfer_assets() -> Vec<(Location, Xcm<()>)> { + todo!() + } + fn teleport_assets( + dest: Location, + beneficiary: Location, + assets: Assets, + ) -> Vec<(VersionedLocation, VersionedXcm<()>)> { + vec![ + ( + VersionedLocation::V4(Here.into()), + VersionedXcm::V4(Xcm(vec![ + WithdrawAsset(assets.clone()), + BurnAsset(assets.clone()), + ])), + ), + ( + VersionedLocation::V4(dest), + VersionedXcm::V4(Xcm(vec![ + ReceiveTeleportedAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: (Here, 100u128).into(), weight_limit: Unlimited }, + DepositAsset { assets: Wild(All), beneficiary }, + ])), + ), + ] + } + fn reserve_transfer_assets() -> Vec<(Location, Xcm<()>)> { + todo!() + } + } +} From dd0376da998e250d0241e0ec646a6338b5df3d06 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 28 Mar 2024 21:09:41 +0100 Subject: [PATCH 02/61] feat(xcm-fee-payment-runtime-api): changed XcmTransfersApi to XcmDryRunApi --- Cargo.lock | 1 + .../xcm-fee-payment-runtime-api/Cargo.toml | 2 + .../src/{transfers.rs => dry_run.rs} | 23 +++--- .../xcm-fee-payment-runtime-api/src/lib.rs | 6 +- .../tests/fee_estimation.rs | 14 ++-- .../xcm-fee-payment-runtime-api/tests/mock.rs | 80 +++++++++++-------- 6 files changed, 75 insertions(+), 51 deletions(-) rename polkadot/xcm/xcm-fee-payment-runtime-api/src/{transfers.rs => dry_run.rs} (66%) diff --git a/Cargo.lock b/Cargo.lock index 4d0d4d7bfac4..d5288cafb08d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22453,6 +22453,7 @@ dependencies = [ name = "xcm-fee-payment-runtime-api" version = "0.1.0" dependencies = [ + "frame-executive", "frame-support", "frame-system", "pallet-balances", diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index 24d95c1c31ee..9215dd1d584a 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -33,6 +33,7 @@ xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", defaul sp-io = { path = "../../../substrate/primitives/io", default-features = false } pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } +frame-executive = { path = "../../../substrate/frame/executive", default-features = false } [features] default = ["std"] @@ -51,4 +52,5 @@ std = [ "sp-io/std", "pallet-balances/std", "xcm-executor/std", + "frame-executive/std", ] diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/transfers.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs similarity index 66% rename from polkadot/xcm/xcm-fee-payment-runtime-api/src/transfers.rs rename to polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index 5ea1421c2489..ab105b067a5a 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/transfers.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -17,23 +17,28 @@ //! Runtime API definition for getting xcm transfer messages. //! These messages can be used to get the fees that need to be paid. +use codec::{Encode, Decode}; +use frame_support::pallet_prelude::TypeInfo; use sp_std::vec::Vec; +use sp_runtime::traits::Block as BlockT; use xcm::prelude::*; +#[derive(Encode, Decode, Debug, TypeInfo)] +pub struct XcmDryRunEffects { + pub local_program: Xcm<()>, +} + sp_api::decl_runtime_apis! { - /// API for obtaining the messages for different types of cross-chain transfers. + /// API for dry-running extrinsics and XCM programs to get the programs that need to be passed to the fees API. /// /// All calls return a vector of tuples (location, xcm) where each "xcm" is executed in "location". /// If there's local execution, the location will be "Here". /// This vector can be used to calculate both execution and delivery fees. - pub trait XcmTransfersApi { - /// Generic transfer, will figure out if it's a teleport or a reserve transfer. - fn transfer_assets() -> Vec<(Location, Xcm<()>)>; - - /// Returns messages for a teleport. - fn teleport_assets(dest: Location, beneficiary: Location, assets: Assets) -> Vec<(VersionedLocation, VersionedXcm<()>)>; + pub trait XcmDryRunApi { + /// Dry run extrinsic. + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result; - /// Returns messages for a reserve transfer. - fn reserve_transfer_assets() -> Vec<(Location, Xcm<()>)>; + /// Dry run XCM program + fn dry_run_xcm(xcm: Xcm<()>) -> Result; } } diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs index e7a629ca5495..697bc55f15c6 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs @@ -27,9 +27,9 @@ /// Main API. /// Estimates fees. mod fees; -/// Transfers API. +/// Dry-run API. /// Returns the messages that need to be passed to the fees API. -mod transfers; +mod dry_run; pub use fees::{XcmPaymentApi, Error as XcmPaymentApiError}; -pub use transfers::XcmTransfersApi; +pub use dry_run::{XcmDryRunApi, XcmDryRunEffects}; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 29be8fbc7193..220046fcc4b8 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -18,7 +18,7 @@ use sp_api::ProvideRuntimeApi; use sp_runtime::testing::H256; -use xcm_fee_payment_runtime_api::{XcmPaymentApi, XcmTransfersApi}; +use xcm_fee_payment_runtime_api::{XcmPaymentApi, XcmDryRunApi}; use xcm::prelude::*; mod mock; @@ -28,12 +28,14 @@ use mock::{TestClient, HereLocation}; fn can_get_both_execution_and_delivery_fees_for_a_transfer() { let client = TestClient; let runtime_api = client.runtime_api(); - let messages = runtime_api.teleport_assets( + // TODO: Build extrinsic + // (Parent, Parachain(1000)).into(), + // AccountId32 { id: [0u8; 32], network: None }.into(), + // (Here, 100u128).into(), + let messages = runtime_api.dry_run_extrinsic( H256::zero(), - (Parent, Parachain(1000)).into(), - AccountId32 { id: [0u8; 32], network: None }.into(), - (Here, 100u128).into(), - ).unwrap(); + extrinsic, + ).unwrap().unwrap(); // assert_eq!(messages, [...]); let mut messages_iter = messages.iter(); diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 499284831aef..93f50ca78f8b 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -25,12 +25,13 @@ use frame_support::{ weights::WeightToFee as WeightToFeeT, }; use pallet_xcm::TestWeightInfo; -use sp_runtime::{AccountId32, traits::IdentityLookup, SaturatedConversion}; +use sp_runtime::{AccountId32, traits::{IdentityLookup, Block as BlockT}, SaturatedConversion}; +use sp_std::cell::RefCell; use xcm::{prelude::*, Version as XcmVersion}; use xcm_builder::{EnsureXcmOrigin, FixedWeightBounds, IsConcrete}; use xcm_executor::XcmExecutor; -use xcm_fee_payment_runtime_api::{XcmTransfersApi, XcmPaymentApi, XcmPaymentApiError}; +use xcm_fee_payment_runtime_api::{XcmDryRunApi, XcmPaymentApi, XcmPaymentApiError, XcmDryRunEffects}; construct_runtime! { pub enum Test { @@ -44,6 +45,15 @@ type Block = frame_system::mocking::MockBlock; type Balance = u128; type AccountId = AccountId32; +type Executive = frame_executive::Executive< + Test, + Block, + frame_system::ChainContext, + Test, + AllPalletsWithSystem, + (), +>; + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; @@ -59,8 +69,16 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ConstU128<1>; } -pub struct TestSendXcm; -impl SendXcm for TestSendXcm { +thread_local! { + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); +} + +pub(crate) fn sent_xcm() -> Vec<(Location, Xcm<()>)> { + SENT_XCM.with(|q| (*q.borrow()).clone()) +} + +pub struct TestXcmSender; +impl SendXcm for TestXcmSender { type Ticket = (Location, Xcm<()>); fn validate( dest: &mut Option, @@ -72,6 +90,7 @@ impl SendXcm for TestSendXcm { } fn deliver(ticket: Self::Ticket) -> Result { let hash = fake_message_hash(&ticket.1); + SENT_XCM.with(|q| q.borrow_mut().push(ticket)); Ok(hash) } } @@ -80,7 +99,7 @@ fn fake_message_hash(message: &Xcm) -> XcmHash { message.using_encoded(sp_io::hashing::blake2_256) } -pub type XcmRouter = TestSendXcm; +pub type XcmRouter = TestXcmSender; parameter_types! { pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000_000, 1024 * 1024); @@ -201,35 +220,30 @@ sp_api::mock_impl_runtime_apis! { } } - impl XcmTransfersApi for RuntimeApi { - fn transfer_assets() -> Vec<(Location, Xcm<()>)> { - todo!() + impl XcmDryRunApi for RuntimeApi { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result { + match extrinsic.function { + RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest, + beneficiary, + assets, + fee_asset_item, + weight_limit, + }) => { + let assets: Assets = (*assets).try_into().map_err(|()| ())?; + Executive::apply_extrinsic(extrinsic); + Ok(XcmDryRunEffects { + local_program: Xcm::builder_unsafe() + .withdraw_asset(assets.clone()) + .burn_asset(assets) + .build(), + }) + }, + _ => Err(()), + } } - fn teleport_assets( - dest: Location, - beneficiary: Location, - assets: Assets, - ) -> Vec<(VersionedLocation, VersionedXcm<()>)> { - vec![ - ( - VersionedLocation::V4(Here.into()), - VersionedXcm::V4(Xcm(vec![ - WithdrawAsset(assets.clone()), - BurnAsset(assets.clone()), - ])), - ), - ( - VersionedLocation::V4(dest), - VersionedXcm::V4(Xcm(vec![ - ReceiveTeleportedAsset(assets.clone()), - ClearOrigin, - BuyExecution { fees: (Here, 100u128).into(), weight_limit: Unlimited }, - DepositAsset { assets: Wild(All), beneficiary }, - ])), - ), - ] - } - fn reserve_transfer_assets() -> Vec<(Location, Xcm<()>)> { + + fn dry_run_xcm(xcm: Xcm<()>) -> Result { todo!() } } From cd1838a51ff2923cc2347f3e9400dc477d711c91 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Sat, 30 Mar 2024 00:00:20 +0100 Subject: [PATCH 03/61] feat(xcm-dry-run-api): get forwarded messages from the queues --- Cargo.lock | 1 + .../xcm-fee-payment-runtime-api/Cargo.toml | 1 + .../src/dry_run.rs | 3 +- .../tests/fee_estimation.rs | 144 +++++++++---- .../xcm-fee-payment-runtime-api/tests/mock.rs | 202 ++++++++++++++---- 5 files changed, 266 insertions(+), 85 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5288cafb08d..f3aa1a8366a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22453,6 +22453,7 @@ dependencies = [ name = "xcm-fee-payment-runtime-api" version = "0.1.0" dependencies = [ + "env_logger 0.9.3", "frame-executive", "frame-support", "frame-system", diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index 9215dd1d584a..daf8a3a1ac11 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -34,6 +34,7 @@ sp-io = { path = "../../../substrate/primitives/io", default-features = false } pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } frame-executive = { path = "../../../substrate/frame/executive", default-features = false } +env_logger = "0.9.0" [features] default = ["std"] diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index ab105b067a5a..bd2b705a8670 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -25,7 +25,8 @@ use xcm::prelude::*; #[derive(Encode, Decode, Debug, TypeInfo)] pub struct XcmDryRunEffects { - pub local_program: Xcm<()>, + pub local_program: VersionedXcm<()>, + pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, } sp_api::decl_runtime_apis! { diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 220046fcc4b8..b915632c57d5 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -22,61 +22,111 @@ use xcm_fee_payment_runtime_api::{XcmPaymentApi, XcmDryRunApi}; use xcm::prelude::*; mod mock; -use mock::{TestClient, HereLocation}; +use mock::{TestClient, HereLocation, TestXt, RuntimeCall, new_test_ext_with_balances, extra, DeliveryFees, ExistentialDeposit}; +// Scenario: User `1` in the local chain wants to transfer assets to account `[0u8; 32]` on "AssetHub". +// He wants to make sure he has enough for fees, so before he calls the `transfer_asset` extrinsic to do the transfer, +// he decides to use the `XcmDryRunApi` and `XcmPaymentApi` runtime APIs to estimate fees. #[test] fn can_get_both_execution_and_delivery_fees_for_a_transfer() { - let client = TestClient; - let runtime_api = client.runtime_api(); - // TODO: Build extrinsic - // (Parent, Parachain(1000)).into(), - // AccountId32 { id: [0u8; 32], network: None }.into(), - // (Here, 100u128).into(), - let messages = runtime_api.dry_run_extrinsic( - H256::zero(), - extrinsic, - ).unwrap().unwrap(); - // assert_eq!(messages, [...]); + let _ = env_logger::builder() + .is_test(true) + .try_init(); + let balances = vec![(1, 100 + DeliveryFees::get() + ExistentialDeposit::get())]; + new_test_ext_with_balances(balances).execute_with(|| { + let client = TestClient; + let runtime_api = client.runtime_api(); + let who = 1; // AccountId = u64. + let extrinsic = TestXt::new( + RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4((Parent, Parachain(1000)).into())), + beneficiary: Box::new(VersionedLocation::V4(AccountId32 { id: [0u8; 32], network: None }.into())), + assets: Box::new(VersionedAssets::V4((Here, 100u128).into())), + fee_asset_item: 0, + weight_limit: WeightLimit::Unlimited, + }), + Some((who, extra())), + ); + let dry_run_effects = runtime_api.dry_run_extrinsic( + H256::zero(), + extrinsic, + ).unwrap().unwrap(); - let mut messages_iter = messages.iter(); + assert_eq!( + dry_run_effects.local_program, + VersionedXcm::V4( + Xcm::builder_unsafe() + .withdraw_asset((Here, 100u128).into()) + .burn_asset((Here, 100u128).into()) + .build() + ) + ); + assert_eq!( + dry_run_effects.forwarded_messages, + vec![ + ( + VersionedLocation::V4(Location::new(1, [Parachain(1000)])), + VersionedXcm::V4( + Xcm::<()>::builder_unsafe() + .receive_teleported_asset(([Parent, Parachain(2000)], 100u128).into()) + .clear_origin() + .buy_execution((Parent, Parachain(2000), 100u128).into()) + .deposit_asset(AllCounted(1).into(), AccountId32 { id: [0u8; 32], network: None }.into()) + .build() + ) + ), + ], + ); - let (_, local_message) = messages_iter.next().unwrap(); + // TODO: Weighing the local program is not relevant for extrinsics that already + // take this weight into account. + // In this case, we really only care about delivery fees. + let local_program = dry_run_effects.local_program; - // We get a double result since the actual call returns a result and the runtime api returns results. - let weight = runtime_api.query_xcm_weight( - H256::zero(), - local_message.clone(), - ).unwrap().unwrap(); - assert_eq!(weight, Weight::from_parts(2_000_000_000_000, 2 * 1024 * 1024)); - let execution_fees = runtime_api.query_weight_to_asset_fee( - H256::zero(), - weight, - VersionedAssetId::V4(HereLocation::get().into()) - ).unwrap().unwrap(); - assert_eq!(execution_fees, 2_000_002_097_152); + // We get a double result since the actual call returns a result and the runtime api returns results. + let weight = runtime_api.query_xcm_weight( + H256::zero(), + local_program.clone(), + ).unwrap().unwrap(); + assert_eq!(weight, Weight::from_parts(200, 20)); + let execution_fees = runtime_api.query_weight_to_asset_fee( + H256::zero(), + weight, + VersionedAssetId::V4(HereLocation::get().into()) + ).unwrap().unwrap(); + assert_eq!(execution_fees, 2_000_002_097_152); - let (destination, remote_message) = messages_iter.next().unwrap(); + let mut forwarded_messages_iter = dry_run_effects.forwarded_messages.into_iter(); - let delivery_fees = runtime_api.query_delivery_fees( - H256::zero(), - destination.clone(), - remote_message.clone(), - ).unwrap().unwrap(); + let (destination, remote_message) = forwarded_messages_iter.next().unwrap(); - // This would have to be the runtime API of the destination, - // which we have the location for. - let remote_execution_weight = runtime_api.query_xcm_weight( - H256::zero(), - remote_message.clone(), - ).unwrap().unwrap(); - let remote_execution_fees = runtime_api.query_weight_to_asset_fee( - H256::zero(), - remote_execution_weight, - VersionedAssetId::V4(HereLocation::get().into()), - ); + let delivery_fees = runtime_api.query_delivery_fees( + H256::zero(), + destination.clone(), + remote_message.clone(), + ).unwrap().unwrap(); + assert_eq!(delivery_fees, VersionedAssets::V4((Here, 100u128).into())); - // Now we know that locally we need to use `execution_fees` and - // `delivery_fees`. - // On the message we forward to the destination, we need to - // put `remote_execution_fees` in `BuyExecution`. + // TODO: This would have to be the runtime API of the destination, + // which we have the location for. + // If I had a mock runtime configured for "AssetHub" then I would use the + // runtime APIs from that. + let remote_execution_weight = runtime_api.query_xcm_weight( + H256::zero(), + remote_message.clone(), + ).unwrap().unwrap(); + let remote_execution_fees = runtime_api.query_weight_to_asset_fee( + H256::zero(), + remote_execution_weight, + VersionedAssetId::V4(HereLocation::get().into()), + ).unwrap().unwrap(); + assert_eq!(remote_execution_fees, 100u128); + + // Now we know that locally we need to use `execution_fees` and + // `delivery_fees`. + // On the message we forward to the destination, we need to + // put `remote_execution_fees` in `BuyExecution`. + // For the `transfer_assets` extrinsic, it just means passing the correct amount + // of fees in the parameters. + }); } diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 93f50ca78f8b..21973354a7a6 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -18,44 +18,59 @@ //! Implements both runtime APIs for fee estimation and getting the messages for transfers. use codec::Encode; -use frame_system::EnsureRoot; +use frame_system::{EnsureRoot, RawOrigin as SystemRawOrigin}; use frame_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{Nothing, ConstU32, ConstU128}, + construct_runtime, derive_impl, parameter_types, assert_ok, + traits::{Nothing, ConstU32, ConstU128, OriginTrait, ContainsPair, Everything, Equals}, weights::WeightToFee as WeightToFeeT, }; use pallet_xcm::TestWeightInfo; -use sp_runtime::{AccountId32, traits::{IdentityLookup, Block as BlockT}, SaturatedConversion}; -use sp_std::cell::RefCell; +use sp_runtime::{traits::{IdentityLookup, Block as BlockT, TryConvert, Get}, SaturatedConversion, BuildStorage}; +use sp_std::{cell::RefCell, marker::PhantomData}; use xcm::{prelude::*, Version as XcmVersion}; -use xcm_builder::{EnsureXcmOrigin, FixedWeightBounds, IsConcrete}; -use xcm_executor::XcmExecutor; +use xcm_builder::{ + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, FungibleAdapter, MintLocation, + AllowTopLevelPaidExecutionFrom, TakeWeightCredit, +}; +use xcm_executor::{XcmExecutor, traits::ConvertLocation}; use xcm_fee_payment_runtime_api::{XcmDryRunApi, XcmPaymentApi, XcmPaymentApiError, XcmDryRunEffects}; construct_runtime! { - pub enum Test { + pub enum TestRuntime { System: frame_system, Balances: pallet_balances, XcmPallet: pallet_xcm, } } -type Block = frame_system::mocking::MockBlock; +pub type SignedExtra = ( + // frame_system::CheckEra, + // frame_system::CheckNonce, + frame_system::CheckWeight, +); +pub type TestXt = sp_runtime::testing::TestXt; +type Block = sp_runtime::testing::Block; type Balance = u128; -type AccountId = AccountId32; +type AccountId = u64; + +pub(crate) fn extra() -> SignedExtra { + ( + frame_system::CheckWeight::new(), + ) +} type Executive = frame_executive::Executive< - Test, + TestRuntime, Block, - frame_system::ChainContext, - Test, + frame_system::ChainContext, + TestRuntime, AllPalletsWithSystem, (), >; #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { +impl frame_system::Config for TestRuntime { type Block = Block; type AccountId = AccountId; type AccountData = pallet_balances::AccountData; @@ -63,10 +78,10 @@ impl frame_system::Config for Test { } #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for Test { +impl pallet_balances::Config for TestRuntime { type AccountStore = System; type Balance = Balance; - type ExistentialDeposit = ConstU128<1>; + type ExistentialDeposit = ExistentialDeposit; } thread_local! { @@ -85,7 +100,7 @@ impl SendXcm for TestXcmSender { msg: &mut Option>, ) -> SendResult { let ticket = (dest.take().unwrap(), msg.take().unwrap()); - let fees: Assets = (HereLocation::get(), 1_000_000_000_000u128).into(); + let fees: Assets = (HereLocation::get(), DeliveryFees::get()).into(); Ok((ticket, fees)) } fn deliver(ticket: Self::Ticket) -> Result { @@ -102,12 +117,18 @@ fn fake_message_hash(message: &Xcm) -> XcmHash { pub type XcmRouter = TestXcmSender; parameter_types! { - pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000_000, 1024 * 1024); + pub const DeliveryFees: u128 = 20; // Random value. + pub const ExistentialDeposit: u128 = 1; // Random value. + pub const BaseXcmWeight: Weight = Weight::from_parts(100, 10); // Random value. pub const MaxInstructions: u32 = 100; - pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Westend)].into(); + pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(2000)].into(); pub static AdvertisedXcmVersion: XcmVersion = 4; pub const HereLocation: Location = Location::here(); + pub const RelayLocation: Location = Location::parent(); pub const MaxAssetsIntoHolding: u32 = 64; + pub CheckAccount: AccountId = XcmPallet::check_account(); + pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); + pub const AnyNetwork: Option = None; } /// Simple `WeightToFee` implementation that adds the ref_time by the proof_size. @@ -122,16 +143,79 @@ impl WeightToFeeT for WeightToFee { type Weigher = FixedWeightBounds; +/// Matches the pair (NativeToken, AssetHub). +/// This is used in the `IsTeleporter` configuration item, meaning we accept our native token +/// coming from AssetHub as a teleport. +pub struct NativeTokenToAssetHub; +impl ContainsPair for NativeTokenToAssetHub { + fn contains(asset: &Asset, origin: &Location) -> bool { + matches!(asset.id.0.unpack(), (0, [])) + && matches!(origin.unpack(), (1, [Parachain(1000)])) + } +} + +/// Matches the pair (RelayToken, AssetHub). +/// This is used in the `IsReserve` configuration item, meaning we accept the relay token +/// coming from AssetHub as a reserve asset transfer. +pub struct RelayTokenToAssetHub; +impl ContainsPair for RelayTokenToAssetHub { + fn contains(asset: &Asset, origin: &Location) -> bool { + matches!(asset.id.0.unpack(), (1, [])) + && matches!(origin.unpack(), (1, [Parachain(1000)])) + } +} + +/// Converts locations that are only the `AccountIndex64` junction into local u64 accounts. +pub struct AccountIndex64Aliases(PhantomData<(Network, AccountId)>); +impl>, AccountId: From> + ConvertLocation for AccountIndex64Aliases +{ + fn convert_location(location: &Location) -> Option { + let index = match location.unpack() { + (0, [AccountIndex64 { index, network: None }]) => index, + (0, [AccountIndex64 { index, network }]) if *network == Network::get() => index, + _ => return None, + }; + Some((*index).into()) + } +} + +/// We only alias local account locations to actual local accounts. +/// We don't allow sovereign accounts for locations outside our chain. +pub type LocationToAccountId = AccountIndex64Aliases; + +pub type FungibleTransactor = FungibleAdapter< + // We use pallet-balances for handling this fungible asset. + Balances, + // The fungible asset handled by this transactor is the native token of the chain. + IsConcrete, + // How we convert locations to accounts. + LocationToAccountId, + // We need to specify the AccountId type. + AccountId, + // We mint the native tokens locally, so we track how many we've sent away via teleports. + LocalCheckAccount, +>; + +// TODO: Handle the relay chain asset so we can also test with +// reserve asset transfers. +pub type AssetTransactor = FungibleTransactor; + +pub type Barrier = ( + TakeWeightCredit, // We need this for pallet-xcm's extrinsics to work. + AllowTopLevelPaidExecutionFrom>, // TODO: Technically, we should allow messages from "AssetHub". +); + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = (); + type AssetTransactor = AssetTransactor; type OriginConverter = (); type IsReserve = (); - type IsTeleporter = (); + type IsTeleporter = NativeTokenToAssetHub; type UniversalLocation = UniversalLocation; - type Barrier = (); + type Barrier = Barrier; type Weigher = Weigher; type Trader = (); type ResponseHandler = (); @@ -154,15 +238,37 @@ impl xcm_executor::Config for XcmConfig { type HrmpChannelClosingHandler = (); } -impl pallet_xcm::Config for Test { +/// Converts a signed origin of a u64 account into a location with only the `AccountIndex64` junction. +pub struct SignedToAccountIndex64(PhantomData<(RuntimeOrigin, AccountId)>); +impl< + RuntimeOrigin: OriginTrait + Clone, + AccountId: Into, +> TryConvert for SignedToAccountIndex64 +where + RuntimeOrigin::PalletsOrigin: From> + + TryInto, Error = RuntimeOrigin::PalletsOrigin>, +{ + fn try_convert(origin: RuntimeOrigin) -> Result { + origin.try_with_caller(|caller| match caller.try_into() { + Ok(SystemRawOrigin::Signed(who)) => + Ok(Junction::AccountIndex64 { network: None, index: who.into() }.into()), + Ok(other) => Err(other.into()), + Err(other) => Err(other), + }) + } +} + +pub type LocalOriginToLocation = SignedToAccountIndex64; + +impl pallet_xcm::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = EnsureXcmOrigin; type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; + type ExecuteXcmOrigin = EnsureXcmOrigin; type XcmExecuteFilter = Nothing; type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Nothing; - type XcmReserveTransferFilter = Nothing; + type XcmTeleportFilter = Everything; // Put everything instead of something more restricted. + type XcmReserveTransferFilter = Everything; // Same. type Weigher = Weigher; type UniversalLocation = UniversalLocation; type RuntimeOrigin = RuntimeOrigin; @@ -180,6 +286,18 @@ impl pallet_xcm::Config for Test { type WeightInfo = TestWeightInfo; } +pub fn new_test_ext_with_balances(balances: Vec<(AccountId, Balance)>) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + #[derive(Clone)] pub(crate) struct TestClient; @@ -222,21 +340,31 @@ sp_api::mock_impl_runtime_apis! { impl XcmDryRunApi for RuntimeApi { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result { - match extrinsic.function { + // First we execute the extrinsic to check the queue. + match &extrinsic.call { RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { - dest, - beneficiary, + dest: _dest, + beneficiary: _beneficiary, assets, - fee_asset_item, - weight_limit, + fee_asset_item: _fee_asset_item, + weight_limit: _weight_limit, }) => { - let assets: Assets = (*assets).try_into().map_err(|()| ())?; - Executive::apply_extrinsic(extrinsic); + let assets: Assets = (**assets).clone().try_into()?; + assert_ok!(Executive::apply_extrinsic(extrinsic)); // Asserting just because it's for tests. + let forwarded_messages = sent_xcm() + .into_iter() + .map(|(location, message)| ( + VersionedLocation::V4(location), + VersionedXcm::V4(message) + )).collect(); Ok(XcmDryRunEffects { - local_program: Xcm::builder_unsafe() - .withdraw_asset(assets.clone()) - .burn_asset(assets) - .build(), + local_program: VersionedXcm::V4( + Xcm::builder_unsafe() + .withdraw_asset(assets.clone()) + .burn_asset(assets) + .build() + ), + forwarded_messages, }) }, _ => Err(()), From 7e04d5073731163d57387f29b6b737a18a86af9d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Sun, 31 Mar 2024 13:20:33 +0200 Subject: [PATCH 04/61] feat(xcm-dry-run-api): updated test works --- Cargo.lock | 1 + polkadot/xcm/pallet-xcm/src/lib.rs | 169 +++++++++++------- .../xcm-fee-payment-runtime-api/Cargo.toml | 1 + .../tests/fee_estimation.rs | 10 +- .../xcm-fee-payment-runtime-api/tests/mock.rs | 63 +++++-- 5 files changed, 161 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3aa1a8366a9..413859d00428 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22457,6 +22457,7 @@ dependencies = [ "frame-executive", "frame-support", "frame-system", + "log", "pallet-balances", "pallet-xcm", "parity-scale-codec", diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index a7940c7ddaa3..909e42c7d239 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -1383,7 +1383,7 @@ pub mod pallet { ); ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::::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::::Empty)?.clone(); // Find transfer types for fee and non-fee assets. @@ -1391,58 +1391,27 @@ pub mod pallet { 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::::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::::InvalidAssetUnsupportedReserve.into()), - }; - FeesHandling::Separate { local_xcm, remote_xcm } - }; + let fees_handling = Self::find_fees_handling( + origin.clone(), + dest.clone(), + &fees_transfer_type, + &assets_transfer_type, + assets.clone(), + fees, + &weight_limit, + fee_asset_item, + )?; - 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, - fees, + fees_handling, weight_limit, - ) + )?; + Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm) } /// Claims assets trapped on this pallet because of leftover assets during XCM execution. @@ -1552,7 +1521,7 @@ const MAX_ASSETS_FOR_TRANSFER: usize = 2; /// Specify how assets used for fees are handled during asset transfers. #[derive(Clone, PartialEq)] -enum FeesHandling { +pub enum FeesHandling { /// `fees` asset can be batch-transferred with rest of assets using same XCM instructions. Batched { fees: Asset }, /// fees cannot be batched, they are handled separately using XCM programs here. @@ -1636,7 +1605,7 @@ impl Pallet { /// transferring to `dest`. /// /// Validate `assets` to all have same `TransferType`. - fn find_fee_and_assets_transfer_types( + pub fn find_fee_and_assets_transfer_types( assets: &[Asset], fee_asset_item: usize, dest: &Location, @@ -1673,6 +1642,60 @@ impl Pallet { )) } + pub fn find_fees_handling( + origin: Location, + dest: Location, + fees_transfer_type: &TransferType, + assets_transfer_type: &TransferType, + mut assets: Vec, + fees: Asset, + weight_limit: &WeightLimit, + fee_asset_item: usize, + ) -> Result, Error> { + if fees_transfer_type == assets_transfer_type { + // no need for custom fees instructions, fees are batched with assets + Ok(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::::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::::InvalidAssetUnsupportedReserve.into()), + }; + Ok(FeesHandling::Separate { local_xcm, remote_xcm }) + } + } + fn do_reserve_transfer_assets( origin: OriginFor, dest: Box, @@ -1708,15 +1731,16 @@ impl Pallet { // Ensure all assets (including fees) have same reserve location. ensure!(assets_transfer_type == fees_transfer_type, Error::::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( @@ -1749,18 +1773,19 @@ impl Pallet { } let fees = assets.get(fee_asset_item as usize).ok_or(Error::::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 build_and_execute_xcm_transfer_type( + pub fn build_xcm_transfer_type( origin: Location, dest: Location, beneficiary: Location, @@ -1768,14 +1793,14 @@ impl Pallet { transfer_type: TransferType, fees: FeesHandling, weight_limit: WeightLimit, - ) -> DispatchResult { + ) -> Result<(Xcm<::RuntimeCall>, Option>), Error> { 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(), @@ -1825,7 +1850,21 @@ impl Pallet { )?; (local, Some(remote)) }, - }; + }) + } + + fn execute_xcm_transfer( + origin: Location, + dest: Location, + mut local_xcm: Xcm<::RuntimeCall>, + remote_xcm: Option>, + ) -> 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::::UnweighableMessage)?; let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256); @@ -1839,7 +1878,7 @@ impl Pallet { 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::::LocalExecutionIncomplete @@ -1851,7 +1890,7 @@ impl Pallet { 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::::FeesNotMet diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index daf8a3a1ac11..fdfd4d36bb79 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -34,6 +34,7 @@ sp-io = { path = "../../../substrate/primitives/io", default-features = false } pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } frame-executive = { path = "../../../substrate/frame/executive", default-features = false } +log = { workspace = true } env_logger = "0.9.0" [features] diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index b915632c57d5..e99da8bd5762 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -68,9 +68,9 @@ fn can_get_both_execution_and_delivery_fees_for_a_transfer() { VersionedLocation::V4(Location::new(1, [Parachain(1000)])), VersionedXcm::V4( Xcm::<()>::builder_unsafe() - .receive_teleported_asset(([Parent, Parachain(2000)], 100u128).into()) + .receive_teleported_asset(((Parent, Parachain(2000)), 100u128).into()) .clear_origin() - .buy_execution((Parent, Parachain(2000), 100u128).into()) + .buy_execution(((Parent, Parachain(2000)), 100u128).into(), Unlimited) .deposit_asset(AllCounted(1).into(), AccountId32 { id: [0u8; 32], network: None }.into()) .build() ) @@ -94,7 +94,7 @@ fn can_get_both_execution_and_delivery_fees_for_a_transfer() { weight, VersionedAssetId::V4(HereLocation::get().into()) ).unwrap().unwrap(); - assert_eq!(execution_fees, 2_000_002_097_152); + assert_eq!(execution_fees, 220); let mut forwarded_messages_iter = dry_run_effects.forwarded_messages.into_iter(); @@ -105,7 +105,7 @@ fn can_get_both_execution_and_delivery_fees_for_a_transfer() { destination.clone(), remote_message.clone(), ).unwrap().unwrap(); - assert_eq!(delivery_fees, VersionedAssets::V4((Here, 100u128).into())); + assert_eq!(delivery_fees, VersionedAssets::V4((Here, 20u128).into())); // TODO: This would have to be the runtime API of the destination, // which we have the location for. @@ -120,7 +120,7 @@ fn can_get_both_execution_and_delivery_fees_for_a_transfer() { remote_execution_weight, VersionedAssetId::V4(HereLocation::get().into()), ).unwrap().unwrap(); - assert_eq!(remote_execution_fees, 100u128); + assert_eq!(remote_execution_fees, 440u128); // Now we know that locally we need to use `execution_fees` and // `delivery_fees`. diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 21973354a7a6..c94def7e088e 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -21,7 +21,7 @@ use codec::Encode; use frame_system::{EnsureRoot, RawOrigin as SystemRawOrigin}; use frame_support::{ construct_runtime, derive_impl, parameter_types, assert_ok, - traits::{Nothing, ConstU32, ConstU128, OriginTrait, ContainsPair, Everything, Equals}, + traits::{Nothing, ConstU32, OriginTrait, ContainsPair, Everything, Equals}, weights::WeightToFee as WeightToFeeT, }; use pallet_xcm::TestWeightInfo; @@ -54,7 +54,7 @@ type Block = sp_runtime::testing::Block; type Balance = u128; type AccountId = u64; -pub(crate) fn extra() -> SignedExtra { +pub fn extra() -> SignedExtra { ( frame_system::CheckWeight::new(), ) @@ -315,7 +315,8 @@ impl sp_api::ProvideRuntimeApi for TestClient { sp_api::mock_impl_runtime_apis! { impl XcmPaymentApi for RuntimeApi { fn query_acceptable_payment_assets(xcm_version: XcmVersion) -> Result, XcmPaymentApiError> { - todo!() + if xcm_version != 4 { return Err(XcmPaymentApiError::UnhandledXcmVersion) }; + Ok(vec![VersionedAssetId::V4(HereLocation::get().into())]) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { @@ -341,15 +342,22 @@ sp_api::mock_impl_runtime_apis! { impl XcmDryRunApi for RuntimeApi { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result { // First we execute the extrinsic to check the queue. + // TODO: Not the best rust code out there. More functions could take references instead of ownership. + // Should fix on another PR. match &extrinsic.call { RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { - dest: _dest, - beneficiary: _beneficiary, + dest, + beneficiary, assets, - fee_asset_item: _fee_asset_item, - weight_limit: _weight_limit, + fee_asset_item, + weight_limit, }) => { let assets: Assets = (**assets).clone().try_into()?; + let dest: Location = (**dest).clone().try_into()?; + let beneficiary: Location = (**beneficiary).clone().try_into()?; + let fee_asset_item = *fee_asset_item as usize; + let weight_limit = weight_limit.clone(); + let who = extrinsic.signature.as_ref().ok_or(())?.0; // TODO: Handle errors assert_ok!(Executive::apply_extrinsic(extrinsic)); // Asserting just because it's for tests. let forwarded_messages = sent_xcm() .into_iter() @@ -357,13 +365,42 @@ sp_api::mock_impl_runtime_apis! { VersionedLocation::V4(location), VersionedXcm::V4(message) )).collect(); + let (fees_transfer_type, assets_transfer_type) = + XcmPallet::find_fee_and_assets_transfer_types(assets.inner(), fee_asset_item, &dest) + .map_err(|error| { + log::error!("Couldn't determinte the transfer type. Error: {:?}", error); + () + })?; + let origin_location: Location = AccountIndex64 { index: who.clone(), network: None }.into(); + let fees = assets.get(fee_asset_item).ok_or(())?; // TODO: Handle errors + let fees_handling = XcmPallet::find_fees_handling( + origin_location.clone(), + dest.clone(), + &fees_transfer_type, + &assets_transfer_type, + assets.inner().to_vec(), + fees.clone(), + &weight_limit, + fee_asset_item, + ).map_err(|error| { + log::error!("Unable to handle fees. Error: {:?}", error); + () // TODO: Handle errors. + })?; + let (local_xcm, _) = XcmPallet::build_xcm_transfer_type( + origin_location, + dest, + beneficiary, + assets.into_inner(), + assets_transfer_type, + fees_handling, + weight_limit, + ).map_err(|error| { + log::error!("Unable to construct local XCM program. Error: {:?}", error); + () // TODO: Handle errors. + })?; + let local_xcm: Xcm<()> = local_xcm.into(); Ok(XcmDryRunEffects { - local_program: VersionedXcm::V4( - Xcm::builder_unsafe() - .withdraw_asset(assets.clone()) - .burn_asset(assets) - .build() - ), + local_program: VersionedXcm::<()>::V4(local_xcm), forwarded_messages, }) }, From ec04b8052b2ec23124a2d16e20e0577871136994 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Sun, 31 Mar 2024 18:39:12 +0200 Subject: [PATCH 05/61] feat(xcm-dry-run-api): add test for a reserve asset transfer --- Cargo.lock | 1 + .../xcm-fee-payment-runtime-api/Cargo.toml | 2 + .../tests/fee_estimation.rs | 71 +++++++++++- .../xcm-fee-payment-runtime-api/tests/mock.rs | 109 +++++++++++++++--- 4 files changed, 165 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 413859d00428..3c9f6e598c57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22458,6 +22458,7 @@ dependencies = [ "frame-support", "frame-system", "log", + "pallet-assets", "pallet-balances", "pallet-xcm", "parity-scale-codec", diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index fdfd4d36bb79..e32502351906 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -32,6 +32,7 @@ pallet-xcm = { path = "../pallet-xcm", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false } sp-io = { path = "../../../substrate/primitives/io", default-features = false } pallet-balances = { path = "../../../substrate/frame/balances", default-features = false } +pallet-assets = { path = "../../../substrate/frame/assets", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } frame-executive = { path = "../../../substrate/frame/executive", default-features = false } log = { workspace = true } @@ -53,6 +54,7 @@ std = [ "xcm-builder/std", "sp-io/std", "pallet-balances/std", + "pallet-assets/std", "xcm-executor/std", "frame-executive/std", ] diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index e99da8bd5762..1e155de066a6 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -22,13 +22,18 @@ use xcm_fee_payment_runtime_api::{XcmPaymentApi, XcmDryRunApi}; use xcm::prelude::*; mod mock; -use mock::{TestClient, HereLocation, TestXt, RuntimeCall, new_test_ext_with_balances, extra, DeliveryFees, ExistentialDeposit}; +use mock::{ + TestClient, HereLocation, TestXt, RuntimeCall, + new_test_ext_with_balances, extra, DeliveryFees, ExistentialDeposit, + new_test_ext_with_balances_and_assets, +}; // Scenario: User `1` in the local chain wants to transfer assets to account `[0u8; 32]` on "AssetHub". // He wants to make sure he has enough for fees, so before he calls the `transfer_asset` extrinsic to do the transfer, // he decides to use the `XcmDryRunApi` and `XcmPaymentApi` runtime APIs to estimate fees. +// This uses a teleport because we're dealing with the native token of the chain, which is registered on "AssetHub". #[test] -fn can_get_both_execution_and_delivery_fees_for_a_transfer() { +fn fee_estimation_for_teleport() { let _ = env_logger::builder() .is_test(true) .try_init(); @@ -43,7 +48,7 @@ fn can_get_both_execution_and_delivery_fees_for_a_transfer() { beneficiary: Box::new(VersionedLocation::V4(AccountId32 { id: [0u8; 32], network: None }.into())), assets: Box::new(VersionedAssets::V4((Here, 100u128).into())), fee_asset_item: 0, - weight_limit: WeightLimit::Unlimited, + weight_limit: Unlimited, }), Some((who, extra())), ); @@ -59,7 +64,7 @@ fn can_get_both_execution_and_delivery_fees_for_a_transfer() { .withdraw_asset((Here, 100u128).into()) .burn_asset((Here, 100u128).into()) .build() - ) + ), ); assert_eq!( dry_run_effects.forwarded_messages, @@ -130,3 +135,61 @@ fn can_get_both_execution_and_delivery_fees_for_a_transfer() { // of fees in the parameters. }); } + +#[test] +fn dry_run_reserve_asset_transfer() { + let _ = env_logger::builder() + .is_test(true) + .try_init(); + let who = 1; // AccountId = u64. + // Native token used for fees. + let balances = vec![(who, DeliveryFees::get() + ExistentialDeposit::get())]; + // Relay token is the one we want to transfer. + let assets = vec![(1, who, 100)]; // id, account_id, balance. + new_test_ext_with_balances_and_assets(balances, assets).execute_with(|| { + let client = TestClient; + let runtime_api = client.runtime_api(); + let extrinsic = TestXt::new( + RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4((Parent, Parachain(1000)).into())), + beneficiary: Box::new(VersionedLocation::V4(AccountId32 { id: [0u8; 32], network: None }.into())), + assets: Box::new(VersionedAssets::V4((Parent, 100u128).into())), + fee_asset_item: 0, + weight_limit: Unlimited, + }), + Some((who, extra())), + ); + let dry_run_effects = runtime_api.dry_run_extrinsic( + H256::zero(), + extrinsic, + ).unwrap().unwrap(); + + assert_eq!( + dry_run_effects.local_program, + VersionedXcm::V4( + Xcm::builder_unsafe() + .withdraw_asset((Parent, 100u128).into()) + .burn_asset((Parent, 100u128).into()) + .build() + ), + ); + + // In this case, the transfer type is `DestinationReserve`, so the remote xcm just withdraws the assets. + assert_eq!( + dry_run_effects.forwarded_messages, + vec![ + ( + VersionedLocation::V4(Location::new(1, Parachain(1000))), + VersionedXcm::V4( + Xcm::<()>::builder_unsafe() + .withdraw_asset((Parent, 100u128).into()) + .clear_origin() + .buy_execution((Parent, 100u128).into(), Unlimited) + .deposit_asset(AllCounted(1).into(), AccountId32 { id: [0u8; 32], network: None }.into()) + .build() + ), + ), + ], + ); + }); +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index c94def7e088e..556c3a25422b 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -21,18 +21,19 @@ use codec::Encode; use frame_system::{EnsureRoot, RawOrigin as SystemRawOrigin}; use frame_support::{ construct_runtime, derive_impl, parameter_types, assert_ok, - traits::{Nothing, ConstU32, OriginTrait, ContainsPair, Everything, Equals}, + traits::{Nothing, ConstU32, ConstU128, OriginTrait, ContainsPair, Everything, Equals, AsEnsureOriginWithArg}, weights::WeightToFee as WeightToFeeT, }; use pallet_xcm::TestWeightInfo; -use sp_runtime::{traits::{IdentityLookup, Block as BlockT, TryConvert, Get}, SaturatedConversion, BuildStorage}; +use sp_runtime::{traits::{IdentityLookup, Block as BlockT, TryConvert, Get, MaybeEquivalence}, SaturatedConversion, BuildStorage}; use sp_std::{cell::RefCell, marker::PhantomData}; use xcm::{prelude::*, Version as XcmVersion}; use xcm_builder::{ EnsureXcmOrigin, FixedWeightBounds, IsConcrete, FungibleAdapter, MintLocation, - AllowTopLevelPaidExecutionFrom, TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, TakeWeightCredit, FungiblesAdapter, ConvertedConcreteId, + NoChecking, }; -use xcm_executor::{XcmExecutor, traits::ConvertLocation}; +use xcm_executor::{XcmExecutor, traits::{ConvertLocation, JustTry}}; use xcm_fee_payment_runtime_api::{XcmDryRunApi, XcmPaymentApi, XcmPaymentApiError, XcmDryRunEffects}; @@ -40,6 +41,7 @@ construct_runtime! { pub enum TestRuntime { System: frame_system, Balances: pallet_balances, + AssetsPallet: pallet_assets, XcmPallet: pallet_xcm, } } @@ -52,6 +54,7 @@ pub type SignedExtra = ( pub type TestXt = sp_runtime::testing::TestXt; type Block = sp_runtime::testing::Block; type Balance = u128; +type AssetIdForAssetsPallet = u32; type AccountId = u64; pub fn extra() -> SignedExtra { @@ -84,6 +87,21 @@ impl pallet_balances::Config for TestRuntime { type ExistentialDeposit = ExistentialDeposit; } +#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)] +impl pallet_assets::Config for TestRuntime { + type AssetId = AssetIdForAssetsPallet; + type Balance = Balance; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Freezer = (); + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; +} + thread_local! { pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); } @@ -184,7 +202,7 @@ impl>, AccountId: From> /// We don't allow sovereign accounts for locations outside our chain. pub type LocationToAccountId = AccountIndex64Aliases; -pub type FungibleTransactor = FungibleAdapter< +pub type NativeTokenTransactor = FungibleAdapter< // We use pallet-balances for handling this fungible asset. Balances, // The fungible asset handled by this transactor is the native token of the chain. @@ -197,9 +215,44 @@ pub type FungibleTransactor = FungibleAdapter< LocalCheckAccount, >; -// TODO: Handle the relay chain asset so we can also test with -// reserve asset transfers. -pub type AssetTransactor = FungibleTransactor; +pub struct LocationToAssetIdForAssetsPallet; +impl MaybeEquivalence for LocationToAssetIdForAssetsPallet { + fn convert(location: &Location) -> Option { + match location.unpack() { + (1, []) => Some(1 as AssetIdForAssetsPallet), + _ => None, + } + } + + fn convert_back(id: &AssetIdForAssetsPallet) -> Option { + match id { + 1 => Some(Location::new(1, [])), + _ => None, + } + } +} + +/// AssetTransactor for handling the relay chain token. +pub type RelayTokenTransactor = FungiblesAdapter< + // We use pallet-assets for handling the relay token. + AssetsPallet, + // Matches the relay token. + ConvertedConcreteId< + AssetIdForAssetsPallet, + Balance, + LocationToAssetIdForAssetsPallet, + JustTry, + >, + // How we convert locations to accounts. + LocationToAccountId, + // We need to specify the AccountId type. + AccountId, + // We don't track teleports. + NoChecking, + (), +>; + +pub type AssetTransactors = (NativeTokenTransactor, RelayTokenTransactor); pub type Barrier = ( TakeWeightCredit, // We need this for pallet-xcm's extrinsics to work. @@ -210,9 +263,9 @@ pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; - type AssetTransactor = AssetTransactor; + type AssetTransactor = AssetTransactors; type OriginConverter = (); - type IsReserve = (); + type IsReserve = RelayTokenToAssetHub; type IsTeleporter = NativeTokenToAssetHub; type UniversalLocation = UniversalLocation; type Barrier = Barrier; @@ -298,6 +351,31 @@ pub fn new_test_ext_with_balances(balances: Vec<(AccountId, Balance)>) -> sp_io: ext } +pub fn new_test_ext_with_balances_and_assets(balances: Vec<(AccountId, Balance)>, assets: Vec<(AssetIdForAssetsPallet, AccountId, Balance)>) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_assets::GenesisConfig:: { + assets: vec![ + // id, owner, is_sufficient, min_balance. + // We don't actually need this to be sufficient, since we use the native assets in tests for the existential deposit. + (1, 0, true, 1), + ], + metadata: vec![ + // id, name, symbol, decimals. + (1, "Relay Token".into(), "RLY".into(), 12), + ], + accounts: assets, + }.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + #[derive(Clone)] pub(crate) struct TestClient; @@ -368,11 +446,14 @@ sp_api::mock_impl_runtime_apis! { let (fees_transfer_type, assets_transfer_type) = XcmPallet::find_fee_and_assets_transfer_types(assets.inner(), fee_asset_item, &dest) .map_err(|error| { - log::error!("Couldn't determinte the transfer type. Error: {:?}", error); + log::error!(target: "xcm", "Couldn't determinte the transfer type. Error: {:?}", error); () })?; let origin_location: Location = AccountIndex64 { index: who.clone(), network: None }.into(); - let fees = assets.get(fee_asset_item).ok_or(())?; // TODO: Handle errors + let fees = assets.get(fee_asset_item).ok_or_else(|| { + log::error!(target: "xcm", "Couldn't get fee asset."); + () + })?; // TODO: Handle errors let fees_handling = XcmPallet::find_fees_handling( origin_location.clone(), dest.clone(), @@ -383,7 +464,7 @@ sp_api::mock_impl_runtime_apis! { &weight_limit, fee_asset_item, ).map_err(|error| { - log::error!("Unable to handle fees. Error: {:?}", error); + log::error!(target: "xcm", "Unable to handle fees. Error: {:?}", error); () // TODO: Handle errors. })?; let (local_xcm, _) = XcmPallet::build_xcm_transfer_type( @@ -395,7 +476,7 @@ sp_api::mock_impl_runtime_apis! { fees_handling, weight_limit, ).map_err(|error| { - log::error!("Unable to construct local XCM program. Error: {:?}", error); + log::error!(target: "xcm", "Unable to construct local XCM program. Error: {:?}", error); () // TODO: Handle errors. })?; let local_xcm: Xcm<()> = local_xcm.into(); From c5c48cacdb0dedf95a39e3d0b67857ff91280a9c Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Sun, 31 Mar 2024 23:30:04 +0200 Subject: [PATCH 06/61] feat(xcm-dry-run-api): add dry_run_xcm call --- .../src/dry_run.rs | 4 +- .../tests/fee_estimation.rs | 45 +++++++++++++ .../xcm-fee-payment-runtime-api/tests/mock.rs | 66 +++++++++++++++---- 3 files changed, 102 insertions(+), 13 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index bd2b705a8670..fdfacf5839f7 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -35,11 +35,11 @@ sp_api::decl_runtime_apis! { /// All calls return a vector of tuples (location, xcm) where each "xcm" is executed in "location". /// If there's local execution, the location will be "Here". /// This vector can be used to calculate both execution and delivery fees. - pub trait XcmDryRunApi { + pub trait XcmDryRunApi { /// Dry run extrinsic. fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result; /// Dry run XCM program - fn dry_run_xcm(xcm: Xcm<()>) -> Result; + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result; } } diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 1e155de066a6..d234e18188d7 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -193,3 +193,48 @@ fn dry_run_reserve_asset_transfer() { ); }); } + +#[test] +fn dry_run_xcm() { + let _ = env_logger::builder() + .is_test(true) + .try_init(); + let who = 1; // AccountId = u64. + let transfer_amount = 100u128; + // TODO: We need to build the XCM to weigh it and then build the real XCM + // that can pay for fees. + // There might be a better way. + let xcm_to_weigh = Xcm::::builder_unsafe() + .withdraw_asset((Here, transfer_amount).into()) + .clear_origin() + .buy_execution((Here, transfer_amount).into(), Unlimited) + .deposit_reserve_asset(AllCounted(1).into(), (Parent, Parachain(2100)).into(), Xcm(vec![])) + .build(); + let client = TestClient; + let runtime_api = client.runtime_api(); + let xcm_weight = runtime_api.query_xcm_weight( + H256::zero(), + VersionedXcm::V4(xcm_to_weigh.clone().into()), + ).unwrap().unwrap(); + let execution_fees = runtime_api.query_weight_to_asset_fee( + H256::zero(), + xcm_weight, + VersionedAssetId::V4(Here.into()) + ).unwrap().unwrap(); + let xcm = Xcm::::builder_unsafe() + .withdraw_asset((Here, transfer_amount + execution_fees).into()) + .clear_origin() + .buy_execution((Here, execution_fees).into(), Unlimited) + .deposit_reserve_asset(AllCounted(1).into(), (Parent, Parachain(2100)).into(), Xcm(vec![])) + .build(); + let balances = vec![(who, transfer_amount + execution_fees + DeliveryFees::get() + ExistentialDeposit::get())]; + new_test_ext_with_balances(balances).execute_with(|| { + let dry_run_effects = runtime_api.dry_run_xcm( + H256::zero(), + VersionedLocation::V4(AccountIndex64 { index: 1, network: None }.into()), + VersionedXcm::V4(xcm), + xcm_weight, + ).unwrap().unwrap(); + dbg!(&dry_run_effects); + }); +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 556c3a25422b..74bb571dcfb6 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -21,7 +21,7 @@ use codec::Encode; use frame_system::{EnsureRoot, RawOrigin as SystemRawOrigin}; use frame_support::{ construct_runtime, derive_impl, parameter_types, assert_ok, - traits::{Nothing, ConstU32, ConstU128, OriginTrait, ContainsPair, Everything, Equals, AsEnsureOriginWithArg}, + traits::{Nothing, Contains, ConstU32, ConstU128, OriginTrait, ContainsPair, Everything, AsEnsureOriginWithArg}, weights::WeightToFee as WeightToFeeT, }; use pallet_xcm::TestWeightInfo; @@ -31,7 +31,7 @@ use xcm::{prelude::*, Version as XcmVersion}; use xcm_builder::{ EnsureXcmOrigin, FixedWeightBounds, IsConcrete, FungibleAdapter, MintLocation, AllowTopLevelPaidExecutionFrom, TakeWeightCredit, FungiblesAdapter, ConvertedConcreteId, - NoChecking, + NoChecking, FixedRateOfFungible, }; use xcm_executor::{XcmExecutor, traits::{ConvertLocation, JustTry}}; @@ -139,6 +139,7 @@ parameter_types! { pub const ExistentialDeposit: u128 = 1; // Random value. pub const BaseXcmWeight: Weight = Weight::from_parts(100, 10); // Random value. pub const MaxInstructions: u32 = 100; + pub const NativeTokenPerSecondPerByte: (AssetId, u128, u128) = (AssetId(HereLocation::get()), 1, 1); pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(2000)].into(); pub static AdvertisedXcmVersion: XcmVersion = 4; pub const HereLocation: Location = Location::here(); @@ -198,9 +199,24 @@ impl>, AccountId: From> } } -/// We only alias local account locations to actual local accounts. -/// We don't allow sovereign accounts for locations outside our chain. -pub type LocationToAccountId = AccountIndex64Aliases; +/// Custom location converter to turn sibling chains into u64 accounts. +pub struct SiblingChainToIndex64; +impl ConvertLocation for SiblingChainToIndex64 { + fn convert_location(location: &Location) -> Option { + let index = match location.unpack() { + (1, [Parachain(id)]) => id, + _ => return None, + }; + Some((*index).into()) + } +} + +/// We alias local account locations to actual local accounts. +/// We also allow sovereign accounts for other sibling chains. +pub type LocationToAccountId = ( + AccountIndex64Aliases, + SiblingChainToIndex64, +); pub type NativeTokenTransactor = FungibleAdapter< // We use pallet-balances for handling this fungible asset. @@ -254,11 +270,20 @@ pub type RelayTokenTransactor = FungiblesAdapter< pub type AssetTransactors = (NativeTokenTransactor, RelayTokenTransactor); +pub struct HereAndInnerLocations; +impl Contains for HereAndInnerLocations { + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (0, []) | (0, _)) + } +} + pub type Barrier = ( TakeWeightCredit, // We need this for pallet-xcm's extrinsics to work. - AllowTopLevelPaidExecutionFrom>, // TODO: Technically, we should allow messages from "AssetHub". + AllowTopLevelPaidExecutionFrom, // TODO: Technically, we should allow messages from "AssetHub". ); +pub type Trader = FixedRateOfFungible; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -270,7 +295,7 @@ impl xcm_executor::Config for XcmConfig { type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = Weigher; - type Trader = (); + type Trader = Trader; type ResponseHandler = (); type AssetTrap = (); type AssetLocker = (); @@ -417,7 +442,7 @@ sp_api::mock_impl_runtime_apis! { } } - impl XcmDryRunApi for RuntimeApi { + impl XcmDryRunApi for RuntimeApi { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result { // First we execute the extrinsic to check the queue. // TODO: Not the best rust code out there. More functions could take references instead of ownership. @@ -436,7 +461,7 @@ sp_api::mock_impl_runtime_apis! { let fee_asset_item = *fee_asset_item as usize; let weight_limit = weight_limit.clone(); let who = extrinsic.signature.as_ref().ok_or(())?.0; // TODO: Handle errors - assert_ok!(Executive::apply_extrinsic(extrinsic)); // Asserting just because it's for tests. + assert_ok!(Executive::apply_extrinsic(extrinsic)); // Asserting only because it's a test. let forwarded_messages = sent_xcm() .into_iter() .map(|(location, message)| ( @@ -489,8 +514,27 @@ sp_api::mock_impl_runtime_apis! { } } - fn dry_run_xcm(xcm: Xcm<()>) -> Result { - todo!() + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, max_weight: Weight) -> Result { + let origin_location: Location = origin_location.try_into()?; + let xcm: Xcm = xcm.try_into()?; + let mut hash = fake_message_hash(&xcm); + assert_ok!(XcmExecutor::::prepare_and_execute( + origin_location, + xcm, + &mut hash, + max_weight, + Weight::zero(), + )); // Asserting only because it's a test. + let forwarded_messages = sent_xcm() + .into_iter() + .map(|(location, message)| ( + VersionedLocation::V4(location), + VersionedXcm::V4(message), + )).collect(); + Ok(XcmDryRunEffects { + local_program: VersionedXcm::V4(Xcm(Vec::new())), + forwarded_messages, + }) } } } From df90feb2f170c28c39e406dfdaa44aadf7683e3b Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 9 Apr 2024 17:37:54 +0200 Subject: [PATCH 07/61] feat(xcm-dry-run-api): grab XCM that's about to be executed --- polkadot/xcm/pallet-xcm/src/lib.rs | 53 +- polkadot/xcm/pallet-xcm/src/mock.rs | 1 + polkadot/xcm/pallet-xcm/src/tests/mod.rs | 40 +- polkadot/xcm/xcm-executor/src/config.rs | 6 +- polkadot/xcm/xcm-executor/src/lib.rs | 11 +- polkadot/xcm/xcm-executor/src/traits/mod.rs | 2 + .../xcm/xcm-executor/src/traits/record_xcm.rs | 35 + .../src/dry_run.rs | 26 +- .../xcm-fee-payment-runtime-api/src/lib.rs | 8 +- .../tests/fee_estimation.rs | 448 ++++++----- .../xcm-fee-payment-runtime-api/tests/mock.rs | 699 ++++++++---------- 11 files changed, 720 insertions(+), 609 deletions(-) create mode 100644 polkadot/xcm/xcm-executor/src/traits/record_xcm.rs diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 909e42c7d239..ff7eb9e3c2b8 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -751,6 +751,24 @@ pub mod pallet { #[pallet::storage] pub(super) type XcmExecutionSuspended = StorageValue<_, bool, ValueQuery>; + /// Whether or not incoming XCMs (both executed locally and received) should be recorded. + /// Only one XCM will be recorded at a time. + /// This is meant to be used in runtime APIs, and it's advised it stays false + /// for all other use cases, so as to not degrade regular performance. + /// + /// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] + /// implementation in the XCM executor configuration. + #[pallet::storage] + pub type ShouldRecordXcm = StorageValue<_, bool, ValueQuery>; + + /// If [`ShouldRecordXcm`] is set to true, then the recorded XCM will be stored here. + /// Runtime APIs can fetch the XCM that was executed by accessing this value. + /// + /// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] + /// implementation in the XCM executor configuration. + #[pallet::storage] + pub type RecordedXcm = StorageValue<_, Xcm<()>>; + #[pallet::genesis_config] pub struct GenesisConfig { #[serde(skip)] @@ -1676,13 +1694,12 @@ impl Pallet { fees, weight_limit, )?, - TransferType::DestinationReserve => - Self::destination_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(), @@ -2498,8 +2515,8 @@ impl Pallet { } pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result { - let message = - Xcm::<()>::try_from(message).map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + let message = Xcm::<()>::try_from(message) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; T::Weigher::weight(&mut message.into()).map_err(|()| { log::error!(target: "xcm::pallet_xcm::query_xcm_weight", "Error when querying XCM weight"); @@ -2513,10 +2530,12 @@ impl Pallet { ) -> Result { let result_version = destination.identify_version().max(message.identify_version()); - let destination = - destination.try_into().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + let destination = destination + .try_into() + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; - let message = message.try_into().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + let message = + message.try_into().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; let (_, fees) = validate_send::(destination, message).map_err(|error| { log::error!(target: "xcm::pallet_xcm::query_delivery_fees", "Error when querying delivery fees: {:?}", error); @@ -3189,6 +3208,16 @@ impl CheckSuspension for Pallet { } } +impl xcm_executor::traits::RecordXcm for Pallet { + fn should_record() -> bool { + ShouldRecordXcm::::get() + } + + fn record(xcm: Xcm<()>) { + RecordedXcm::::put(xcm); + } +} + /// Ensure that the origin `o` represents an XCM (`Transact`) origin. /// /// Returns `Ok` with the location of the XCM sender or an `Err` otherwise. diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index 2cc228476ba8..d28ed47eb49e 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -529,6 +529,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index 763d768e154a..862ea018c36c 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -20,8 +20,9 @@ pub(crate) mod assets_transfer; use crate::{ mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error, - LatestVersionedLocation, Pallet, Queries, QueryStatus, VersionDiscoveryQueue, - VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, WeightInfo, + LatestVersionedLocation, Pallet, Queries, QueryStatus, RecordedXcm, ShouldRecordXcm, + VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, + WeightInfo, }; use codec::Encode; use frame_support::{ @@ -1266,3 +1267,38 @@ fn multistage_migration_works() { assert!(Pallet::::do_try_state().is_ok()); }) } + +#[test] +fn record_xcm_works() { + let balances = vec![(ALICE, INITIAL_BALANCE)]; + new_test_ext_with_balances(balances).execute_with(|| { + let message = Xcm::::builder() + .withdraw_asset((Here, SEND_AMOUNT).into()) + .buy_execution((Here, SEND_AMOUNT).into(), Unlimited) + .deposit_asset( + AllCounted(1).into(), + Junction::AccountId32 { network: None, id: BOB.into() }.into(), + ) + .build(); + // Test default values. + assert_eq!(ShouldRecordXcm::::get(), false); + assert_eq!(RecordedXcm::::get(), None); + + // By default the message won't be recorded. + assert_ok!(XcmPallet::execute_blob( + RuntimeOrigin::signed(ALICE), + VersionedXcm::from(message.clone()).encode().try_into().unwrap(), + BaseXcmWeight::get() * 3, + )); + assert_eq!(RecordedXcm::::get(), None); + + // We explicitly set the record flag to true so we record the XCM. + ShouldRecordXcm::::put(true); + assert_ok!(XcmPallet::execute_blob( + RuntimeOrigin::signed(ALICE), + VersionedXcm::from(message.clone()).encode().try_into().unwrap(), + BaseXcmWeight::get() * 3, + )); + assert_eq!(RecordedXcm::::get(), Some(message.into())); + }); +} diff --git a/polkadot/xcm/xcm-executor/src/config.rs b/polkadot/xcm/xcm-executor/src/config.rs index b296d32ca2ad..e87caec50163 100644 --- a/polkadot/xcm/xcm-executor/src/config.rs +++ b/polkadot/xcm/xcm-executor/src/config.rs @@ -17,8 +17,8 @@ use crate::traits::{ AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FeeManager, HandleHrmpChannelAccepted, HandleHrmpChannelClosing, - HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, ShouldExecute, TransactAsset, - VersionChangeNotifier, WeightBounds, WeightTrader, + HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, RecordXcm, ShouldExecute, + TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, }; use frame_support::{ dispatch::{GetDispatchInfo, Parameter, PostDispatchInfo}, @@ -122,4 +122,6 @@ pub trait Config { type HrmpChannelAcceptedHandler: HandleHrmpChannelAccepted; /// Allows optional logic execution for the `HrmpChannelClosing` XCM notification. type HrmpChannelClosingHandler: HandleHrmpChannelClosing; + /// Allows recording XCMs. + type XcmRecorder: RecordXcm; } diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 81b81fe6a171..8e6ce1871587 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -33,8 +33,8 @@ use traits::{ validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, Enact, ExportXcm, FeeManager, FeeReason, HandleHrmpChannelAccepted, HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, - Properties, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, - XcmAssetTransfers, + Properties, RecordXcm, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, + WeightTrader, XcmAssetTransfers, }; mod assets; @@ -204,6 +204,13 @@ impl ExecuteXcm for XcmExecutor. + +//! Trait for recording XCMs and a dummy implementation. + +use xcm::latest::Xcm; + +/// Trait for recording XCMs. +pub trait RecordXcm { + /// Whether or not we should record incoming XCMs. + fn should_record() -> bool; + /// Record `xcm`. + fn record(xcm: Xcm<()>); +} + +impl RecordXcm for () { + fn should_record() -> bool { + false + } + + fn record(_: Xcm<()>) {} +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index fdfacf5839f7..ea9bad64cb41 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -17,29 +17,29 @@ //! Runtime API definition for getting xcm transfer messages. //! These messages can be used to get the fees that need to be paid. -use codec::{Encode, Decode}; +use codec::{Decode, Encode}; use frame_support::pallet_prelude::TypeInfo; -use sp_std::vec::Vec; use sp_runtime::traits::Block as BlockT; +use sp_std::vec::Vec; use xcm::prelude::*; #[derive(Encode, Decode, Debug, TypeInfo)] pub struct XcmDryRunEffects { - pub local_program: VersionedXcm<()>, - pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, + pub local_program: VersionedXcm<()>, + pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, } sp_api::decl_runtime_apis! { - /// API for dry-running extrinsics and XCM programs to get the programs that need to be passed to the fees API. - /// - /// All calls return a vector of tuples (location, xcm) where each "xcm" is executed in "location". - /// If there's local execution, the location will be "Here". - /// This vector can be used to calculate both execution and delivery fees. + /// API for dry-running extrinsics and XCM programs to get the programs that need to be passed to the fees API. + /// + /// All calls return a vector of tuples (location, xcm) where each "xcm" is executed in "location". + /// If there's local execution, the location will be "Here". + /// This vector can be used to calculate both execution and delivery fees. pub trait XcmDryRunApi { - /// Dry run extrinsic. - fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result; + /// Dry run extrinsic. + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result; - /// Dry run XCM program - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result; + /// Dry run XCM program + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result; } } diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs index 697bc55f15c6..52ee65381cdd 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs @@ -24,12 +24,12 @@ #![cfg_attr(not(feature = "std"), no_std)] -/// Main API. -/// Estimates fees. -mod fees; /// Dry-run API. /// Returns the messages that need to be passed to the fees API. mod dry_run; +/// Main API. +/// Estimates fees. +mod fees; -pub use fees::{XcmPaymentApi, Error as XcmPaymentApiError}; pub use dry_run::{XcmDryRunApi, XcmDryRunEffects}; +pub use fees::{Error as XcmPaymentApiError, XcmPaymentApi}; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index d234e18188d7..297e61913d99 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -18,223 +18,265 @@ use sp_api::ProvideRuntimeApi; use sp_runtime::testing::H256; -use xcm_fee_payment_runtime_api::{XcmPaymentApi, XcmDryRunApi}; use xcm::prelude::*; +use xcm_fee_payment_runtime_api::{XcmDryRunApi, XcmPaymentApi}; mod mock; use mock::{ - TestClient, HereLocation, TestXt, RuntimeCall, - new_test_ext_with_balances, extra, DeliveryFees, ExistentialDeposit, - new_test_ext_with_balances_and_assets, + extra, new_test_ext_with_balances, new_test_ext_with_balances_and_assets, DeliveryFees, + ExistentialDeposit, HereLocation, RuntimeCall, TestClient, TestXt, }; -// Scenario: User `1` in the local chain wants to transfer assets to account `[0u8; 32]` on "AssetHub". -// He wants to make sure he has enough for fees, so before he calls the `transfer_asset` extrinsic to do the transfer, -// he decides to use the `XcmDryRunApi` and `XcmPaymentApi` runtime APIs to estimate fees. -// This uses a teleport because we're dealing with the native token of the chain, which is registered on "AssetHub". +// Scenario: User `1` in the local chain wants to transfer assets to account `[0u8; 32]` on +// "AssetHub". He wants to make sure he has enough for fees, so before he calls the `transfer_asset` +// extrinsic to do the transfer, he decides to use the `XcmDryRunApi` and `XcmPaymentApi` runtime +// APIs to estimate fees. This uses a teleport because we're dealing with the native token of the +// chain, which is registered on "AssetHub". #[test] fn fee_estimation_for_teleport() { - let _ = env_logger::builder() - .is_test(true) - .try_init(); - let balances = vec![(1, 100 + DeliveryFees::get() + ExistentialDeposit::get())]; - new_test_ext_with_balances(balances).execute_with(|| { - let client = TestClient; - let runtime_api = client.runtime_api(); - let who = 1; // AccountId = u64. - let extrinsic = TestXt::new( - RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { - dest: Box::new(VersionedLocation::V4((Parent, Parachain(1000)).into())), - beneficiary: Box::new(VersionedLocation::V4(AccountId32 { id: [0u8; 32], network: None }.into())), - assets: Box::new(VersionedAssets::V4((Here, 100u128).into())), - fee_asset_item: 0, - weight_limit: Unlimited, - }), - Some((who, extra())), - ); - let dry_run_effects = runtime_api.dry_run_extrinsic( - H256::zero(), - extrinsic, - ).unwrap().unwrap(); - - assert_eq!( - dry_run_effects.local_program, - VersionedXcm::V4( - Xcm::builder_unsafe() - .withdraw_asset((Here, 100u128).into()) - .burn_asset((Here, 100u128).into()) - .build() - ), - ); - assert_eq!( - dry_run_effects.forwarded_messages, - vec![ - ( - VersionedLocation::V4(Location::new(1, [Parachain(1000)])), - VersionedXcm::V4( - Xcm::<()>::builder_unsafe() - .receive_teleported_asset(((Parent, Parachain(2000)), 100u128).into()) - .clear_origin() - .buy_execution(((Parent, Parachain(2000)), 100u128).into(), Unlimited) - .deposit_asset(AllCounted(1).into(), AccountId32 { id: [0u8; 32], network: None }.into()) - .build() - ) - ), - ], - ); - - // TODO: Weighing the local program is not relevant for extrinsics that already - // take this weight into account. - // In this case, we really only care about delivery fees. - let local_program = dry_run_effects.local_program; - - // We get a double result since the actual call returns a result and the runtime api returns results. - let weight = runtime_api.query_xcm_weight( - H256::zero(), - local_program.clone(), - ).unwrap().unwrap(); - assert_eq!(weight, Weight::from_parts(200, 20)); - let execution_fees = runtime_api.query_weight_to_asset_fee( - H256::zero(), - weight, - VersionedAssetId::V4(HereLocation::get().into()) - ).unwrap().unwrap(); - assert_eq!(execution_fees, 220); - - let mut forwarded_messages_iter = dry_run_effects.forwarded_messages.into_iter(); - - let (destination, remote_message) = forwarded_messages_iter.next().unwrap(); - - let delivery_fees = runtime_api.query_delivery_fees( - H256::zero(), - destination.clone(), - remote_message.clone(), - ).unwrap().unwrap(); - assert_eq!(delivery_fees, VersionedAssets::V4((Here, 20u128).into())); - - // TODO: This would have to be the runtime API of the destination, - // which we have the location for. - // If I had a mock runtime configured for "AssetHub" then I would use the - // runtime APIs from that. - let remote_execution_weight = runtime_api.query_xcm_weight( - H256::zero(), - remote_message.clone(), - ).unwrap().unwrap(); - let remote_execution_fees = runtime_api.query_weight_to_asset_fee( - H256::zero(), - remote_execution_weight, - VersionedAssetId::V4(HereLocation::get().into()), - ).unwrap().unwrap(); - assert_eq!(remote_execution_fees, 440u128); - - // Now we know that locally we need to use `execution_fees` and - // `delivery_fees`. - // On the message we forward to the destination, we need to - // put `remote_execution_fees` in `BuyExecution`. - // For the `transfer_assets` extrinsic, it just means passing the correct amount - // of fees in the parameters. - }); + let _ = env_logger::builder().is_test(true).try_init(); + let balances = vec![(1, 100 + DeliveryFees::get() + ExistentialDeposit::get())]; + new_test_ext_with_balances(balances).execute_with(|| { + let client = TestClient; + let runtime_api = client.runtime_api(); + let who = 1; // AccountId = u64. + let extrinsic = TestXt::new( + RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4((Parent, Parachain(1000)).into())), + beneficiary: Box::new(VersionedLocation::V4( + AccountId32 { id: [0u8; 32], network: None }.into(), + )), + assets: Box::new(VersionedAssets::V4((Here, 100u128).into())), + fee_asset_item: 0, + weight_limit: Unlimited, + }), + Some((who, extra())), + ); + let dry_run_effects = + runtime_api.dry_run_extrinsic(H256::zero(), extrinsic).unwrap().unwrap(); + + assert_eq!( + dry_run_effects.local_program, + VersionedXcm::V4( + Xcm::builder_unsafe() + .withdraw_asset((Here, 100u128).into()) + .burn_asset((Here, 100u128).into()) + .build() + ), + ); + assert_eq!( + dry_run_effects.forwarded_messages, + vec![( + VersionedLocation::V4(Location::new(1, [Parachain(1000)])), + VersionedXcm::V4( + Xcm::<()>::builder_unsafe() + .receive_teleported_asset(((Parent, Parachain(2000)), 100u128).into()) + .clear_origin() + .buy_execution(((Parent, Parachain(2000)), 100u128).into(), Unlimited) + .deposit_asset( + AllCounted(1).into(), + AccountId32 { id: [0u8; 32], network: None }.into() + ) + .build() + ) + ),], + ); + + // TODO: Weighing the local program is not relevant for extrinsics that already + // take this weight into account. + // In this case, we really only care about delivery fees. + let local_program = dry_run_effects.local_program; + + // We get a double result since the actual call returns a result and the runtime api returns + // results. + let weight = runtime_api + .query_xcm_weight(H256::zero(), local_program.clone()) + .unwrap() + .unwrap(); + assert_eq!(weight, Weight::from_parts(200, 20)); + let execution_fees = runtime_api + .query_weight_to_asset_fee( + H256::zero(), + weight, + VersionedAssetId::V4(HereLocation::get().into()), + ) + .unwrap() + .unwrap(); + assert_eq!(execution_fees, 220); + + let mut forwarded_messages_iter = dry_run_effects.forwarded_messages.into_iter(); + + let (destination, remote_message) = forwarded_messages_iter.next().unwrap(); + + let delivery_fees = runtime_api + .query_delivery_fees(H256::zero(), destination.clone(), remote_message.clone()) + .unwrap() + .unwrap(); + assert_eq!(delivery_fees, VersionedAssets::V4((Here, 20u128).into())); + + // TODO: This would have to be the runtime API of the destination, + // which we have the location for. + // If I had a mock runtime configured for "AssetHub" then I would use the + // runtime APIs from that. + let remote_execution_weight = runtime_api + .query_xcm_weight(H256::zero(), remote_message.clone()) + .unwrap() + .unwrap(); + let remote_execution_fees = runtime_api + .query_weight_to_asset_fee( + H256::zero(), + remote_execution_weight, + VersionedAssetId::V4(HereLocation::get().into()), + ) + .unwrap() + .unwrap(); + assert_eq!(remote_execution_fees, 440u128); + + // Now we know that locally we need to use `execution_fees` and + // `delivery_fees`. + // On the message we forward to the destination, we need to + // put `remote_execution_fees` in `BuyExecution`. + // For the `transfer_assets` extrinsic, it just means passing the correct amount + // of fees in the parameters. + }); } #[test] fn dry_run_reserve_asset_transfer() { - let _ = env_logger::builder() - .is_test(true) - .try_init(); - let who = 1; // AccountId = u64. - // Native token used for fees. - let balances = vec![(who, DeliveryFees::get() + ExistentialDeposit::get())]; - // Relay token is the one we want to transfer. - let assets = vec![(1, who, 100)]; // id, account_id, balance. - new_test_ext_with_balances_and_assets(balances, assets).execute_with(|| { - let client = TestClient; - let runtime_api = client.runtime_api(); - let extrinsic = TestXt::new( - RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { - dest: Box::new(VersionedLocation::V4((Parent, Parachain(1000)).into())), - beneficiary: Box::new(VersionedLocation::V4(AccountId32 { id: [0u8; 32], network: None }.into())), - assets: Box::new(VersionedAssets::V4((Parent, 100u128).into())), - fee_asset_item: 0, - weight_limit: Unlimited, - }), - Some((who, extra())), - ); - let dry_run_effects = runtime_api.dry_run_extrinsic( - H256::zero(), - extrinsic, - ).unwrap().unwrap(); - - assert_eq!( - dry_run_effects.local_program, - VersionedXcm::V4( - Xcm::builder_unsafe() - .withdraw_asset((Parent, 100u128).into()) - .burn_asset((Parent, 100u128).into()) - .build() - ), - ); - - // In this case, the transfer type is `DestinationReserve`, so the remote xcm just withdraws the assets. - assert_eq!( - dry_run_effects.forwarded_messages, - vec![ - ( - VersionedLocation::V4(Location::new(1, Parachain(1000))), - VersionedXcm::V4( - Xcm::<()>::builder_unsafe() - .withdraw_asset((Parent, 100u128).into()) - .clear_origin() - .buy_execution((Parent, 100u128).into(), Unlimited) - .deposit_asset(AllCounted(1).into(), AccountId32 { id: [0u8; 32], network: None }.into()) - .build() - ), - ), - ], - ); - }); + let _ = env_logger::builder().is_test(true).try_init(); + let who = 1; // AccountId = u64. + // Native token used for fees. + let balances = vec![(who, DeliveryFees::get() + ExistentialDeposit::get())]; + // Relay token is the one we want to transfer. + let assets = vec![(1, who, 100)]; // id, account_id, balance. + new_test_ext_with_balances_and_assets(balances, assets).execute_with(|| { + let client = TestClient; + let runtime_api = client.runtime_api(); + let extrinsic = TestXt::new( + RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4((Parent, Parachain(1000)).into())), + beneficiary: Box::new(VersionedLocation::V4( + AccountId32 { id: [0u8; 32], network: None }.into(), + )), + assets: Box::new(VersionedAssets::V4((Parent, 100u128).into())), + fee_asset_item: 0, + weight_limit: Unlimited, + }), + Some((who, extra())), + ); + let dry_run_effects = + runtime_api.dry_run_extrinsic(H256::zero(), extrinsic).unwrap().unwrap(); + + assert_eq!( + dry_run_effects.local_program, + VersionedXcm::V4( + Xcm::builder_unsafe() + .withdraw_asset((Parent, 100u128).into()) + .burn_asset((Parent, 100u128).into()) + .build() + ), + ); + + // In this case, the transfer type is `DestinationReserve`, so the remote xcm just withdraws + // the assets. + assert_eq!( + dry_run_effects.forwarded_messages, + vec![( + VersionedLocation::V4(Location::new(1, Parachain(1000))), + VersionedXcm::V4( + Xcm::<()>::builder_unsafe() + .withdraw_asset((Parent, 100u128).into()) + .clear_origin() + .buy_execution((Parent, 100u128).into(), Unlimited) + .deposit_asset( + AllCounted(1).into(), + AccountId32 { id: [0u8; 32], network: None }.into() + ) + .build() + ), + ),], + ); + }); } #[test] fn dry_run_xcm() { - let _ = env_logger::builder() - .is_test(true) - .try_init(); - let who = 1; // AccountId = u64. - let transfer_amount = 100u128; - // TODO: We need to build the XCM to weigh it and then build the real XCM - // that can pay for fees. - // There might be a better way. - let xcm_to_weigh = Xcm::::builder_unsafe() - .withdraw_asset((Here, transfer_amount).into()) - .clear_origin() - .buy_execution((Here, transfer_amount).into(), Unlimited) - .deposit_reserve_asset(AllCounted(1).into(), (Parent, Parachain(2100)).into(), Xcm(vec![])) - .build(); - let client = TestClient; - let runtime_api = client.runtime_api(); - let xcm_weight = runtime_api.query_xcm_weight( - H256::zero(), - VersionedXcm::V4(xcm_to_weigh.clone().into()), - ).unwrap().unwrap(); - let execution_fees = runtime_api.query_weight_to_asset_fee( - H256::zero(), - xcm_weight, - VersionedAssetId::V4(Here.into()) - ).unwrap().unwrap(); - let xcm = Xcm::::builder_unsafe() - .withdraw_asset((Here, transfer_amount + execution_fees).into()) - .clear_origin() - .buy_execution((Here, execution_fees).into(), Unlimited) - .deposit_reserve_asset(AllCounted(1).into(), (Parent, Parachain(2100)).into(), Xcm(vec![])) - .build(); - let balances = vec![(who, transfer_amount + execution_fees + DeliveryFees::get() + ExistentialDeposit::get())]; - new_test_ext_with_balances(balances).execute_with(|| { - let dry_run_effects = runtime_api.dry_run_xcm( - H256::zero(), - VersionedLocation::V4(AccountIndex64 { index: 1, network: None }.into()), - VersionedXcm::V4(xcm), - xcm_weight, - ).unwrap().unwrap(); - dbg!(&dry_run_effects); - }); + let _ = env_logger::builder().is_test(true).try_init(); + let who = 1; // AccountId = u64. + let transfer_amount = 100u128; + // TODO: We need to build the XCM to weigh it and then build the real XCM + // that can pay for fees. + // There might be a better way. + let inner_xcm = Xcm::<()>::builder_unsafe() + .buy_execution((Here, 1u128).into(), Unlimited.into()) // We'd need to query the destination chain for fees. + .deposit_asset(AllCounted(1).into(), AccountId32 { network: None, id: [0u8; 32] }.into()) + .build(); + let xcm_to_weigh = Xcm::::builder_unsafe() + .withdraw_asset((Here, transfer_amount).into()) + .clear_origin() + .buy_execution((Here, transfer_amount).into(), Unlimited) + .deposit_reserve_asset( + AllCounted(1).into(), + (Parent, Parachain(2100)).into(), + inner_xcm.clone(), + ) + .build(); + let client = TestClient; + let runtime_api = client.runtime_api(); + let xcm_weight = runtime_api + .query_xcm_weight(H256::zero(), VersionedXcm::V4(xcm_to_weigh.clone().into())) + .unwrap() + .unwrap(); + let execution_fees = runtime_api + .query_weight_to_asset_fee(H256::zero(), xcm_weight, VersionedAssetId::V4(Here.into())) + .unwrap() + .unwrap(); + let xcm = Xcm::::builder_unsafe() + .withdraw_asset((Here, transfer_amount + execution_fees).into()) + .clear_origin() + .buy_execution((Here, execution_fees).into(), Unlimited) + .deposit_reserve_asset( + AllCounted(1).into(), + (Parent, Parachain(2100)).into(), + inner_xcm.clone(), + ) + .build(); + let balances = vec![( + who, + transfer_amount + execution_fees + DeliveryFees::get() + ExistentialDeposit::get(), + )]; + dbg!(&transfer_amount); + dbg!(&execution_fees); + dbg!(&DeliveryFees::get()); + dbg!(&ExistentialDeposit::get()); + new_test_ext_with_balances(balances).execute_with(|| { + let dry_run_effects = runtime_api + .dry_run_xcm( + H256::zero(), + VersionedLocation::V4(AccountIndex64 { index: 1, network: None }.into()), + VersionedXcm::V4(xcm), + xcm_weight, + ) + .unwrap() + .unwrap(); + assert_eq!( + dry_run_effects.forwarded_messages, + vec![( + VersionedLocation::V4((Parent, Parachain(2100)).into()), + VersionedXcm::V4( + Xcm::<()>::builder_unsafe() + .reserve_asset_deposited( + ((Parent, Parachain(2000)), transfer_amount + execution_fees - DeliveryFees::get()).into() + ) + .clear_origin() + .buy_execution((Here, 1u128).into(), Unlimited.into()) + .deposit_asset( + AllCounted(1).into(), + AccountId32 { id: [0u8; 32], network: None }.into() + ) + .build() + ), + ),] + ); + }); } diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 74bb571dcfb6..00ef105fea15 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -18,38 +18,49 @@ //! Implements both runtime APIs for fee estimation and getting the messages for transfers. use codec::Encode; -use frame_system::{EnsureRoot, RawOrigin as SystemRawOrigin}; use frame_support::{ - construct_runtime, derive_impl, parameter_types, assert_ok, - traits::{Nothing, Contains, ConstU32, ConstU128, OriginTrait, ContainsPair, Everything, AsEnsureOriginWithArg}, - weights::WeightToFee as WeightToFeeT, + assert_ok, construct_runtime, derive_impl, parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstU128, ConstU32, Contains, ContainsPair, Everything, Nothing, + OriginTrait, + }, + weights::WeightToFee as WeightToFeeT, }; +use frame_system::{EnsureRoot, RawOrigin as SystemRawOrigin}; use pallet_xcm::TestWeightInfo; -use sp_runtime::{traits::{IdentityLookup, Block as BlockT, TryConvert, Get, MaybeEquivalence}, SaturatedConversion, BuildStorage}; +use sp_runtime::{ + traits::{Block as BlockT, Get, IdentityLookup, MaybeEquivalence, TryConvert}, + BuildStorage, SaturatedConversion, +}; use sp_std::{cell::RefCell, marker::PhantomData}; use xcm::{prelude::*, Version as XcmVersion}; use xcm_builder::{ - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, FungibleAdapter, MintLocation, - AllowTopLevelPaidExecutionFrom, TakeWeightCredit, FungiblesAdapter, ConvertedConcreteId, - NoChecking, FixedRateOfFungible, + AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, + FixedWeightBounds, FungibleAdapter, FungiblesAdapter, IsConcrete, MintLocation, NoChecking, + TakeWeightCredit, +}; +use xcm_executor::{ + traits::{ConvertLocation, JustTry}, + XcmExecutor, }; -use xcm_executor::{XcmExecutor, traits::{ConvertLocation, JustTry}}; -use xcm_fee_payment_runtime_api::{XcmDryRunApi, XcmPaymentApi, XcmPaymentApiError, XcmDryRunEffects}; +use xcm_fee_payment_runtime_api::{ + XcmDryRunApi, XcmDryRunEffects, XcmPaymentApi, XcmPaymentApiError, +}; construct_runtime! { - pub enum TestRuntime { - System: frame_system, - Balances: pallet_balances, - AssetsPallet: pallet_assets, - XcmPallet: pallet_xcm, - } + pub enum TestRuntime { + System: frame_system, + Balances: pallet_balances, + AssetsPallet: pallet_assets, + XcmPallet: pallet_xcm, + } } pub type SignedExtra = ( // frame_system::CheckEra, // frame_system::CheckNonce, - frame_system::CheckWeight, + frame_system::CheckWeight, ); pub type TestXt = sp_runtime::testing::TestXt; type Block = sp_runtime::testing::Block; @@ -58,106 +69,104 @@ type AssetIdForAssetsPallet = u32; type AccountId = u64; pub fn extra() -> SignedExtra { - ( - frame_system::CheckWeight::new(), - ) + (frame_system::CheckWeight::new(),) } type Executive = frame_executive::Executive< - TestRuntime, - Block, - frame_system::ChainContext, - TestRuntime, - AllPalletsWithSystem, - (), + TestRuntime, + Block, + frame_system::ChainContext, + TestRuntime, + AllPalletsWithSystem, + (), >; #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { - type Block = Block; - type AccountId = AccountId; - type AccountData = pallet_balances::AccountData; - type Lookup = IdentityLookup; + type Block = Block; + type AccountId = AccountId; + type AccountData = pallet_balances::AccountData; + type Lookup = IdentityLookup; } #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for TestRuntime { - type AccountStore = System; - type Balance = Balance; - type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type Balance = Balance; + type ExistentialDeposit = ExistentialDeposit; } #[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)] impl pallet_assets::Config for TestRuntime { - type AssetId = AssetIdForAssetsPallet; - type Balance = Balance; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = frame_system::EnsureRoot; - type Freezer = (); - type AssetDeposit = ConstU128<1>; - type AssetAccountDeposit = ConstU128<10>; - type MetadataDepositBase = ConstU128<1>; - type MetadataDepositPerByte = ConstU128<1>; - type ApprovalDeposit = ConstU128<1>; + type AssetId = AssetIdForAssetsPallet; + type Balance = Balance; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type Freezer = (); + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; } thread_local! { - pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); } pub(crate) fn sent_xcm() -> Vec<(Location, Xcm<()>)> { - SENT_XCM.with(|q| (*q.borrow()).clone()) + SENT_XCM.with(|q| (*q.borrow()).clone()) } pub struct TestXcmSender; impl SendXcm for TestXcmSender { - type Ticket = (Location, Xcm<()>); - fn validate( - dest: &mut Option, - msg: &mut Option>, - ) -> SendResult { - let ticket = (dest.take().unwrap(), msg.take().unwrap()); - let fees: Assets = (HereLocation::get(), DeliveryFees::get()).into(); - Ok((ticket, fees)) - } - fn deliver(ticket: Self::Ticket) -> Result { - let hash = fake_message_hash(&ticket.1); - SENT_XCM.with(|q| q.borrow_mut().push(ticket)); - Ok(hash) - } + type Ticket = (Location, Xcm<()>); + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult { + let ticket = (dest.take().unwrap(), msg.take().unwrap()); + let fees: Assets = (HereLocation::get(), DeliveryFees::get()).into(); + Ok((ticket, fees)) + } + fn deliver(ticket: Self::Ticket) -> Result { + let hash = fake_message_hash(&ticket.1); + SENT_XCM.with(|q| q.borrow_mut().push(ticket)); + Ok(hash) + } } fn fake_message_hash(message: &Xcm) -> XcmHash { - message.using_encoded(sp_io::hashing::blake2_256) + message.using_encoded(sp_io::hashing::blake2_256) } pub type XcmRouter = TestXcmSender; parameter_types! { - pub const DeliveryFees: u128 = 20; // Random value. - pub const ExistentialDeposit: u128 = 1; // Random value. - pub const BaseXcmWeight: Weight = Weight::from_parts(100, 10); // Random value. - pub const MaxInstructions: u32 = 100; - pub const NativeTokenPerSecondPerByte: (AssetId, u128, u128) = (AssetId(HereLocation::get()), 1, 1); - pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(2000)].into(); - pub static AdvertisedXcmVersion: XcmVersion = 4; - pub const HereLocation: Location = Location::here(); - pub const RelayLocation: Location = Location::parent(); - pub const MaxAssetsIntoHolding: u32 = 64; - pub CheckAccount: AccountId = XcmPallet::check_account(); - pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); - pub const AnyNetwork: Option = None; + pub const DeliveryFees: u128 = 20; // Random value. + pub const ExistentialDeposit: u128 = 1; // Random value. + pub const BaseXcmWeight: Weight = Weight::from_parts(100, 10); // Random value. + pub const MaxInstructions: u32 = 100; + pub const NativeTokenPerSecondPerByte: (AssetId, u128, u128) = (AssetId(HereLocation::get()), 1, 1); + pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(2000)].into(); + pub static AdvertisedXcmVersion: XcmVersion = 4; + pub const HereLocation: Location = Location::here(); + pub const RelayLocation: Location = Location::parent(); + pub const MaxAssetsIntoHolding: u32 = 64; + pub CheckAccount: AccountId = XcmPallet::check_account(); + pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); + pub const AnyNetwork: Option = None; } /// Simple `WeightToFee` implementation that adds the ref_time by the proof_size. pub struct WeightToFee; impl WeightToFeeT for WeightToFee { - type Balance = Balance; - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_add(Self::Balance::saturated_from(weight.proof_size())) - } + type Balance = Balance; + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_add(Self::Balance::saturated_from(weight.proof_size())) + } } type Weigher = FixedWeightBounds; @@ -167,10 +176,9 @@ type Weigher = FixedWeightBounds; /// coming from AssetHub as a teleport. pub struct NativeTokenToAssetHub; impl ContainsPair for NativeTokenToAssetHub { - fn contains(asset: &Asset, origin: &Location) -> bool { - matches!(asset.id.0.unpack(), (0, [])) - && matches!(origin.unpack(), (1, [Parachain(1000)])) - } + fn contains(asset: &Asset, origin: &Location) -> bool { + matches!(asset.id.0.unpack(), (0, [])) && matches!(origin.unpack(), (1, [Parachain(1000)])) + } } /// Matches the pair (RelayToken, AssetHub). @@ -178,363 +186,312 @@ impl ContainsPair for NativeTokenToAssetHub { /// coming from AssetHub as a reserve asset transfer. pub struct RelayTokenToAssetHub; impl ContainsPair for RelayTokenToAssetHub { - fn contains(asset: &Asset, origin: &Location) -> bool { - matches!(asset.id.0.unpack(), (1, [])) - && matches!(origin.unpack(), (1, [Parachain(1000)])) - } + fn contains(asset: &Asset, origin: &Location) -> bool { + matches!(asset.id.0.unpack(), (1, [])) && matches!(origin.unpack(), (1, [Parachain(1000)])) + } } /// Converts locations that are only the `AccountIndex64` junction into local u64 accounts. pub struct AccountIndex64Aliases(PhantomData<(Network, AccountId)>); -impl>, AccountId: From> - ConvertLocation for AccountIndex64Aliases +impl>, AccountId: From> ConvertLocation + for AccountIndex64Aliases { - fn convert_location(location: &Location) -> Option { - let index = match location.unpack() { - (0, [AccountIndex64 { index, network: None }]) => index, - (0, [AccountIndex64 { index, network }]) if *network == Network::get() => index, - _ => return None, - }; - Some((*index).into()) - } + fn convert_location(location: &Location) -> Option { + let index = match location.unpack() { + (0, [AccountIndex64 { index, network: None }]) => index, + (0, [AccountIndex64 { index, network }]) if *network == Network::get() => index, + _ => return None, + }; + Some((*index).into()) + } } /// Custom location converter to turn sibling chains into u64 accounts. pub struct SiblingChainToIndex64; impl ConvertLocation for SiblingChainToIndex64 { - fn convert_location(location: &Location) -> Option { - let index = match location.unpack() { - (1, [Parachain(id)]) => id, - _ => return None, - }; - Some((*index).into()) - } + fn convert_location(location: &Location) -> Option { + let index = match location.unpack() { + (1, [Parachain(id)]) => id, + _ => return None, + }; + Some((*index).into()) + } } /// We alias local account locations to actual local accounts. /// We also allow sovereign accounts for other sibling chains. -pub type LocationToAccountId = ( - AccountIndex64Aliases, - SiblingChainToIndex64, -); +pub type LocationToAccountId = (AccountIndex64Aliases, SiblingChainToIndex64); pub type NativeTokenTransactor = FungibleAdapter< - // We use pallet-balances for handling this fungible asset. - Balances, - // The fungible asset handled by this transactor is the native token of the chain. - IsConcrete, - // How we convert locations to accounts. - LocationToAccountId, - // We need to specify the AccountId type. - AccountId, - // We mint the native tokens locally, so we track how many we've sent away via teleports. - LocalCheckAccount, + // We use pallet-balances for handling this fungible asset. + Balances, + // The fungible asset handled by this transactor is the native token of the chain. + IsConcrete, + // How we convert locations to accounts. + LocationToAccountId, + // We need to specify the AccountId type. + AccountId, + // We mint the native tokens locally, so we track how many we've sent away via teleports. + LocalCheckAccount, >; pub struct LocationToAssetIdForAssetsPallet; impl MaybeEquivalence for LocationToAssetIdForAssetsPallet { - fn convert(location: &Location) -> Option { - match location.unpack() { - (1, []) => Some(1 as AssetIdForAssetsPallet), - _ => None, - } - } - - fn convert_back(id: &AssetIdForAssetsPallet) -> Option { - match id { - 1 => Some(Location::new(1, [])), - _ => None, - } - } + fn convert(location: &Location) -> Option { + match location.unpack() { + (1, []) => Some(1 as AssetIdForAssetsPallet), + _ => None, + } + } + + fn convert_back(id: &AssetIdForAssetsPallet) -> Option { + match id { + 1 => Some(Location::new(1, [])), + _ => None, + } + } } /// AssetTransactor for handling the relay chain token. pub type RelayTokenTransactor = FungiblesAdapter< - // We use pallet-assets for handling the relay token. - AssetsPallet, - // Matches the relay token. - ConvertedConcreteId< - AssetIdForAssetsPallet, - Balance, - LocationToAssetIdForAssetsPallet, - JustTry, - >, - // How we convert locations to accounts. - LocationToAccountId, - // We need to specify the AccountId type. - AccountId, - // We don't track teleports. - NoChecking, - (), + // We use pallet-assets for handling the relay token. + AssetsPallet, + // Matches the relay token. + ConvertedConcreteId, + // How we convert locations to accounts. + LocationToAccountId, + // We need to specify the AccountId type. + AccountId, + // We don't track teleports. + NoChecking, + (), >; pub type AssetTransactors = (NativeTokenTransactor, RelayTokenTransactor); pub struct HereAndInnerLocations; impl Contains for HereAndInnerLocations { - fn contains(location: &Location) -> bool { - matches!(location.unpack(), (0, []) | (0, _)) - } + fn contains(location: &Location) -> bool { + matches!(location.unpack(), (0, []) | (0, _)) + } } pub type Barrier = ( - TakeWeightCredit, // We need this for pallet-xcm's extrinsics to work. - AllowTopLevelPaidExecutionFrom, // TODO: Technically, we should allow messages from "AssetHub". + TakeWeightCredit, // We need this for pallet-xcm's extrinsics to work. + AllowTopLevelPaidExecutionFrom, /* TODO: Technically, we should allow + * messages from "AssetHub". */ ); pub type Trader = FixedRateOfFungible; pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = AssetTransactors; - type OriginConverter = (); - type IsReserve = RelayTokenToAssetHub; - type IsTeleporter = NativeTokenToAssetHub; - type UniversalLocation = UniversalLocation; - type Barrier = Barrier; - type Weigher = Weigher; - type Trader = Trader; - type ResponseHandler = (); - type AssetTrap = (); - type AssetLocker = (); - type AssetExchanger = (); - type AssetClaims = (); - type SubscriptionService = (); - type PalletInstancesInfo = AllPalletsWithSystem; - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = (); - type CallDispatcher = RuntimeCall; - type SafeCallFilter = Nothing; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = (); + type IsReserve = RelayTokenToAssetHub; + type IsTeleporter = NativeTokenToAssetHub; + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = Weigher; + type Trader = Trader; + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = (); + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Nothing; + type Aliasers = Nothing; + type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } -/// Converts a signed origin of a u64 account into a location with only the `AccountIndex64` junction. -pub struct SignedToAccountIndex64(PhantomData<(RuntimeOrigin, AccountId)>); -impl< - RuntimeOrigin: OriginTrait + Clone, - AccountId: Into, -> TryConvert for SignedToAccountIndex64 +/// Converts a signed origin of a u64 account into a location with only the `AccountIndex64` +/// junction. +pub struct SignedToAccountIndex64( + PhantomData<(RuntimeOrigin, AccountId)>, +); +impl> TryConvert + for SignedToAccountIndex64 where RuntimeOrigin::PalletsOrigin: From> + TryInto, Error = RuntimeOrigin::PalletsOrigin>, { - fn try_convert(origin: RuntimeOrigin) -> Result { - origin.try_with_caller(|caller| match caller.try_into() { - Ok(SystemRawOrigin::Signed(who)) => - Ok(Junction::AccountIndex64 { network: None, index: who.into() }.into()), - Ok(other) => Err(other.into()), - Err(other) => Err(other), - }) - } + fn try_convert(origin: RuntimeOrigin) -> Result { + origin.try_with_caller(|caller| match caller.try_into() { + Ok(SystemRawOrigin::Signed(who)) => + Ok(Junction::AccountIndex64 { network: None, index: who.into() }.into()), + Ok(other) => Err(other.into()), + Err(other) => Err(other), + }) + } } pub type LocalOriginToLocation = SignedToAccountIndex64; impl pallet_xcm::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Everything; // Put everything instead of something more restricted. - type XcmReserveTransferFilter = Everything; // Same. - type Weigher = Weigher; - type UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = AdvertisedXcmVersion; - type AdminOrigin = EnsureRoot; - type TrustedLockers = (); - type SovereignAccountOf = (); - type Currency = Balances; - type CurrencyMatcher = IsConcrete; - type MaxLockers = ConstU32<0>; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type WeightInfo = TestWeightInfo; + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; // Put everything instead of something more restricted. + type XcmReserveTransferFilter = Everything; // Same. + type Weigher = Weigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = AdvertisedXcmVersion; + type AdminOrigin = EnsureRoot; + type TrustedLockers = (); + type SovereignAccountOf = (); + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type MaxLockers = ConstU32<0>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type WeightInfo = TestWeightInfo; } pub fn new_test_ext_with_balances(balances: Vec<(AccountId, Balance)>) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances } - .assimilate_storage(&mut t) - .unwrap(); + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext } -pub fn new_test_ext_with_balances_and_assets(balances: Vec<(AccountId, Balance)>, assets: Vec<(AssetIdForAssetsPallet, AccountId, Balance)>) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - - pallet_balances::GenesisConfig:: { balances } - .assimilate_storage(&mut t) - .unwrap(); - - pallet_assets::GenesisConfig:: { - assets: vec![ - // id, owner, is_sufficient, min_balance. - // We don't actually need this to be sufficient, since we use the native assets in tests for the existential deposit. - (1, 0, true, 1), - ], - metadata: vec![ - // id, name, symbol, decimals. - (1, "Relay Token".into(), "RLY".into(), 12), - ], - accounts: assets, - }.assimilate_storage(&mut t).unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext +pub fn new_test_ext_with_balances_and_assets( + balances: Vec<(AccountId, Balance)>, + assets: Vec<(AssetIdForAssetsPallet, AccountId, Balance)>, +) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_assets::GenesisConfig:: { + assets: vec![ + // id, owner, is_sufficient, min_balance. + // We don't actually need this to be sufficient, since we use the native assets in + // tests for the existential deposit. + (1, 0, true, 1), + ], + metadata: vec![ + // id, name, symbol, decimals. + (1, "Relay Token".into(), "RLY".into(), 12), + ], + accounts: assets, + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext } #[derive(Clone)] pub(crate) struct TestClient; pub(crate) struct RuntimeApi { - _inner: TestClient, + _inner: TestClient, } impl sp_api::ProvideRuntimeApi for TestClient { - type Api = RuntimeApi; - fn runtime_api(&self) -> sp_api::ApiRef { - RuntimeApi { _inner: self.clone() }.into() - } + type Api = RuntimeApi; + fn runtime_api(&self) -> sp_api::ApiRef { + RuntimeApi { _inner: self.clone() }.into() + } } sp_api::mock_impl_runtime_apis! { - impl XcmPaymentApi for RuntimeApi { - fn query_acceptable_payment_assets(xcm_version: XcmVersion) -> Result, XcmPaymentApiError> { - if xcm_version != 4 { return Err(XcmPaymentApiError::UnhandledXcmVersion) }; - Ok(vec![VersionedAssetId::V4(HereLocation::get().into())]) - } - - fn query_xcm_weight(message: VersionedXcm<()>) -> Result { - XcmPallet::query_xcm_weight(message) - } - - fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let local_asset = VersionedAssetId::V4(HereLocation::get().into()); - let asset = asset - .into_version(4) - .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; - - if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } - - Ok(WeightToFee::weight_to_fee(&weight)) - } - - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - XcmPallet::query_delivery_fees(destination, message) - } - } - - impl XcmDryRunApi for RuntimeApi { - fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result { - // First we execute the extrinsic to check the queue. - // TODO: Not the best rust code out there. More functions could take references instead of ownership. - // Should fix on another PR. - match &extrinsic.call { - RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { - dest, - beneficiary, - assets, - fee_asset_item, - weight_limit, - }) => { - let assets: Assets = (**assets).clone().try_into()?; - let dest: Location = (**dest).clone().try_into()?; - let beneficiary: Location = (**beneficiary).clone().try_into()?; - let fee_asset_item = *fee_asset_item as usize; - let weight_limit = weight_limit.clone(); - let who = extrinsic.signature.as_ref().ok_or(())?.0; // TODO: Handle errors - assert_ok!(Executive::apply_extrinsic(extrinsic)); // Asserting only because it's a test. - let forwarded_messages = sent_xcm() - .into_iter() - .map(|(location, message)| ( - VersionedLocation::V4(location), - VersionedXcm::V4(message) - )).collect(); - let (fees_transfer_type, assets_transfer_type) = - XcmPallet::find_fee_and_assets_transfer_types(assets.inner(), fee_asset_item, &dest) - .map_err(|error| { - log::error!(target: "xcm", "Couldn't determinte the transfer type. Error: {:?}", error); - () - })?; - let origin_location: Location = AccountIndex64 { index: who.clone(), network: None }.into(); - let fees = assets.get(fee_asset_item).ok_or_else(|| { - log::error!(target: "xcm", "Couldn't get fee asset."); - () - })?; // TODO: Handle errors - let fees_handling = XcmPallet::find_fees_handling( - origin_location.clone(), - dest.clone(), - &fees_transfer_type, - &assets_transfer_type, - assets.inner().to_vec(), - fees.clone(), - &weight_limit, - fee_asset_item, - ).map_err(|error| { - log::error!(target: "xcm", "Unable to handle fees. Error: {:?}", error); - () // TODO: Handle errors. - })?; - let (local_xcm, _) = XcmPallet::build_xcm_transfer_type( - origin_location, - dest, - beneficiary, - assets.into_inner(), - assets_transfer_type, - fees_handling, - weight_limit, - ).map_err(|error| { - log::error!(target: "xcm", "Unable to construct local XCM program. Error: {:?}", error); - () // TODO: Handle errors. - })?; - let local_xcm: Xcm<()> = local_xcm.into(); - Ok(XcmDryRunEffects { - local_program: VersionedXcm::<()>::V4(local_xcm), - forwarded_messages, - }) - }, - _ => Err(()), - } - } - - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, max_weight: Weight) -> Result { - let origin_location: Location = origin_location.try_into()?; - let xcm: Xcm = xcm.try_into()?; - let mut hash = fake_message_hash(&xcm); - assert_ok!(XcmExecutor::::prepare_and_execute( - origin_location, - xcm, - &mut hash, - max_weight, - Weight::zero(), - )); // Asserting only because it's a test. - let forwarded_messages = sent_xcm() - .into_iter() - .map(|(location, message)| ( - VersionedLocation::V4(location), - VersionedXcm::V4(message), - )).collect(); - Ok(XcmDryRunEffects { - local_program: VersionedXcm::V4(Xcm(Vec::new())), - forwarded_messages, - }) - } - } + impl XcmPaymentApi for RuntimeApi { + fn query_acceptable_payment_assets(xcm_version: XcmVersion) -> Result, XcmPaymentApiError> { + if xcm_version != 4 { return Err(XcmPaymentApiError::UnhandledXcmVersion) }; + Ok(vec![VersionedAssetId::V4(HereLocation::get().into())]) + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + XcmPallet::query_xcm_weight(message) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(HereLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + XcmPallet::query_delivery_fees(destination, message) + } + } + + impl XcmDryRunApi for RuntimeApi { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result { + // We want to record the XCM that's executed, so we can return it. + pallet_xcm::ShouldRecordXcm::::put(true); + // Asserting only because it's a test. + assert_ok!(Executive::apply_extrinsic(extrinsic)); + // Turning recording off after we execute the extrinsic just in case. + pallet_xcm::ShouldRecordXcm::::put(false); + let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap(); + let forwarded_messages = sent_xcm() + .into_iter() + .map(|(location, message)| ( + VersionedLocation::V4(location), + VersionedXcm::V4(message) + )).collect(); + Ok(XcmDryRunEffects { + local_program: VersionedXcm::<()>::V4(local_xcm), + forwarded_messages, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, max_weight: Weight) -> Result { + let origin_location: Location = origin_location.try_into()?; + let xcm: Xcm = xcm.try_into()?; + let mut hash = fake_message_hash(&xcm); + assert_ok!(XcmExecutor::::prepare_and_execute( + origin_location, + xcm, + &mut hash, + max_weight, + Weight::zero(), + ).ensure_complete()); // Asserting only because it's a test. + let forwarded_messages = sent_xcm() + .into_iter() + .map(|(location, message)| ( + VersionedLocation::V4(location), + VersionedXcm::V4(message), + )).collect(); + Ok(XcmDryRunEffects { + local_program: VersionedXcm::V4(Xcm(Vec::new())), + forwarded_messages, + }) + } + } } From ccda0dfafb855f479a89eea28afa337e0899560c Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 9 Apr 2024 17:38:20 +0200 Subject: [PATCH 08/61] chore(xcm-dry-run-api): remove dbgs --- .../xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 297e61913d99..aed2a0b497b3 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -245,10 +245,6 @@ fn dry_run_xcm() { who, transfer_amount + execution_fees + DeliveryFees::get() + ExistentialDeposit::get(), )]; - dbg!(&transfer_amount); - dbg!(&execution_fees); - dbg!(&DeliveryFees::get()); - dbg!(&ExistentialDeposit::get()); new_test_ext_with_balances(balances).execute_with(|| { let dry_run_effects = runtime_api .dry_run_xcm( From a9e0ec4111de12cebed56d0a4899430c083e08b0 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 9 Apr 2024 17:41:03 +0200 Subject: [PATCH 09/61] chore(xcm-dry-run-api): fmt --- .../xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index aed2a0b497b3..47fba03963aa 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -262,7 +262,11 @@ fn dry_run_xcm() { VersionedXcm::V4( Xcm::<()>::builder_unsafe() .reserve_asset_deposited( - ((Parent, Parachain(2000)), transfer_amount + execution_fees - DeliveryFees::get()).into() + ( + (Parent, Parachain(2000)), + transfer_amount + execution_fees - DeliveryFees::get() + ) + .into() ) .clear_origin() .buy_execution((Here, 1u128).into(), Unlimited.into()) From 4bec8ee1582632ec78fe2142d4a836d57ddb75fb Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 9 Apr 2024 17:51:12 +0200 Subject: [PATCH 10/61] fix: compilation issues --- Cargo.lock | 13 ++++ .../tests/fee_estimation.rs | 68 +++++++------------ 2 files changed, 37 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a527876e5563..e3a7269d5587 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5074,6 +5074,19 @@ dependencies = [ "regex", ] +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.10.1" diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 47fba03963aa..e9c271514414 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -59,8 +59,8 @@ fn fee_estimation_for_teleport() { dry_run_effects.local_program, VersionedXcm::V4( Xcm::builder_unsafe() - .withdraw_asset((Here, 100u128).into()) - .burn_asset((Here, 100u128).into()) + .withdraw_asset((Here, 100u128)) + .burn_asset((Here, 100u128)) .build() ), ); @@ -70,13 +70,10 @@ fn fee_estimation_for_teleport() { VersionedLocation::V4(Location::new(1, [Parachain(1000)])), VersionedXcm::V4( Xcm::<()>::builder_unsafe() - .receive_teleported_asset(((Parent, Parachain(2000)), 100u128).into()) + .receive_teleported_asset(((Parent, Parachain(2000)), 100u128)) .clear_origin() - .buy_execution(((Parent, Parachain(2000)), 100u128).into(), Unlimited) - .deposit_asset( - AllCounted(1).into(), - AccountId32 { id: [0u8; 32], network: None }.into() - ) + .buy_execution(((Parent, Parachain(2000)), 100u128), Unlimited) + .deposit_asset(AllCounted(1), [0u8; 32]) .build() ) ),], @@ -171,8 +168,8 @@ fn dry_run_reserve_asset_transfer() { dry_run_effects.local_program, VersionedXcm::V4( Xcm::builder_unsafe() - .withdraw_asset((Parent, 100u128).into()) - .burn_asset((Parent, 100u128).into()) + .withdraw_asset((Parent, 100u128)) + .burn_asset((Parent, 100u128)) .build() ), ); @@ -185,13 +182,10 @@ fn dry_run_reserve_asset_transfer() { VersionedLocation::V4(Location::new(1, Parachain(1000))), VersionedXcm::V4( Xcm::<()>::builder_unsafe() - .withdraw_asset((Parent, 100u128).into()) + .withdraw_asset((Parent, 100u128)) .clear_origin() - .buy_execution((Parent, 100u128).into(), Unlimited) - .deposit_asset( - AllCounted(1).into(), - AccountId32 { id: [0u8; 32], network: None }.into() - ) + .buy_execution((Parent, 100u128), Unlimited) + .deposit_asset(AllCounted(1), [0u8; 32]) .build() ), ),], @@ -208,18 +202,14 @@ fn dry_run_xcm() { // that can pay for fees. // There might be a better way. let inner_xcm = Xcm::<()>::builder_unsafe() - .buy_execution((Here, 1u128).into(), Unlimited.into()) // We'd need to query the destination chain for fees. - .deposit_asset(AllCounted(1).into(), AccountId32 { network: None, id: [0u8; 32] }.into()) + .buy_execution((Here, 1u128), Unlimited) // We'd need to query the destination chain for fees. + .deposit_asset(AllCounted(1), [0u8; 32]) .build(); let xcm_to_weigh = Xcm::::builder_unsafe() - .withdraw_asset((Here, transfer_amount).into()) + .withdraw_asset((Here, transfer_amount)) .clear_origin() - .buy_execution((Here, transfer_amount).into(), Unlimited) - .deposit_reserve_asset( - AllCounted(1).into(), - (Parent, Parachain(2100)).into(), - inner_xcm.clone(), - ) + .buy_execution((Here, transfer_amount), Unlimited) + .deposit_reserve_asset(AllCounted(1), (Parent, Parachain(2100)), inner_xcm.clone()) .build(); let client = TestClient; let runtime_api = client.runtime_api(); @@ -232,14 +222,10 @@ fn dry_run_xcm() { .unwrap() .unwrap(); let xcm = Xcm::::builder_unsafe() - .withdraw_asset((Here, transfer_amount + execution_fees).into()) + .withdraw_asset((Here, transfer_amount + execution_fees)) .clear_origin() - .buy_execution((Here, execution_fees).into(), Unlimited) - .deposit_reserve_asset( - AllCounted(1).into(), - (Parent, Parachain(2100)).into(), - inner_xcm.clone(), - ) + .buy_execution((Here, execution_fees), Unlimited) + .deposit_reserve_asset(AllCounted(1), (Parent, Parachain(2100)), inner_xcm.clone()) .build(); let balances = vec![( who, @@ -261,19 +247,13 @@ fn dry_run_xcm() { VersionedLocation::V4((Parent, Parachain(2100)).into()), VersionedXcm::V4( Xcm::<()>::builder_unsafe() - .reserve_asset_deposited( - ( - (Parent, Parachain(2000)), - transfer_amount + execution_fees - DeliveryFees::get() - ) - .into() - ) + .reserve_asset_deposited(( + (Parent, Parachain(2000)), + transfer_amount + execution_fees - DeliveryFees::get() + )) .clear_origin() - .buy_execution((Here, 1u128).into(), Unlimited.into()) - .deposit_asset( - AllCounted(1).into(), - AccountId32 { id: [0u8; 32], network: None }.into() - ) + .buy_execution((Here, 1u128), Unlimited) + .deposit_asset(AllCounted(1), [0u8; 32]) .build() ), ),] From 35026663ea9055c985bf989f119cb3f04194c42d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 10 Apr 2024 15:30:36 +0200 Subject: [PATCH 11/61] chore(pallet-xcm): revert refactor --- polkadot/xcm/pallet-xcm/src/lib.rs | 172 ++++++++++++----------------- 1 file changed, 68 insertions(+), 104 deletions(-) diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index c30b81e3ab8f..a9c19d32490e 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -752,7 +752,7 @@ pub mod pallet { pub(super) type XcmExecutionSuspended = StorageValue<_, bool, ValueQuery>; /// Whether or not incoming XCMs (both executed locally and received) should be recorded. - /// Only one XCM will be recorded at a time. + /// Only one XCM program will be recorded at a time. /// This is meant to be used in runtime APIs, and it's advised it stays false /// for all other use cases, so as to not degrade regular performance. /// @@ -761,7 +761,8 @@ pub mod pallet { #[pallet::storage] pub type ShouldRecordXcm = StorageValue<_, bool, ValueQuery>; - /// If [`ShouldRecordXcm`] is set to true, then the recorded XCM will be stored here. + /// If [`ShouldRecordXcm`] is set to true, then the last XCM program executed locally + /// will be stored here. /// Runtime APIs can fetch the XCM that was executed by accessing this value. /// /// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] @@ -1317,7 +1318,7 @@ pub mod pallet { ); ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::::TooManyAssets); - let assets = assets.into_inner(); + let mut 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::::Empty)?.clone(); // Find transfer types for fee and non-fee assets. @@ -1325,27 +1326,58 @@ pub mod pallet { Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?; // local and remote XCM programs to potentially handle fees separately - let fees_handling = Self::find_fees_handling( - origin.clone(), - dest.clone(), - &fees_transfer_type, - &assets_transfer_type, - assets.clone(), - fees, - &weight_limit, - fee_asset_item, - )?; + 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::::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::::InvalidAssetUnsupportedReserve.into()), + }; + FeesHandling::Separate { local_xcm, remote_xcm } + }; - let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type( - origin.clone(), - dest.clone(), + Self::build_and_execute_xcm_transfer_type( + origin, + dest, beneficiary, assets, assets_transfer_type, - fees_handling, + fees, weight_limit, - )?; - Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm) + ) } /// Claims assets trapped on this pallet because of leftover assets during XCM execution. @@ -1438,7 +1470,7 @@ const MAX_ASSETS_FOR_TRANSFER: usize = 2; /// Specify how assets used for fees are handled during asset transfers. #[derive(Clone, PartialEq)] -pub enum FeesHandling { +enum FeesHandling { /// `fees` asset can be batch-transferred with rest of assets using same XCM instructions. Batched { fees: Asset }, /// fees cannot be batched, they are handled separately using XCM programs here. @@ -1522,7 +1554,7 @@ impl Pallet { /// transferring to `dest`. /// /// Validate `assets` to all have same `TransferType`. - pub fn find_fee_and_assets_transfer_types( + fn find_fee_and_assets_transfer_types( assets: &[Asset], fee_asset_item: usize, dest: &Location, @@ -1559,59 +1591,6 @@ impl Pallet { )) } - pub fn find_fees_handling( - origin: Location, - dest: Location, - fees_transfer_type: &TransferType, - assets_transfer_type: &TransferType, - mut assets: Vec, - fees: Asset, - weight_limit: &WeightLimit, - fee_asset_item: usize, - ) -> Result, Error> { - if fees_transfer_type == assets_transfer_type { - // no need for custom fees instructions, fees are batched with assets - Ok(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::::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::::InvalidAssetUnsupportedReserve.into()), - }; - Ok(FeesHandling::Separate { local_xcm, remote_xcm }) - } - } - fn do_reserve_transfer_assets( origin: OriginFor, dest: Box, @@ -1647,16 +1626,15 @@ impl Pallet { // Ensure all assets (including fees) have same reserve location. ensure!(assets_transfer_type == fees_transfer_type, Error::::TooManyReserves); - let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type( - origin.clone(), - dest.clone(), + Self::build_and_execute_xcm_transfer_type( + origin, + dest, beneficiary, assets, assets_transfer_type, FeesHandling::Batched { fees }, weight_limit, - )?; - Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm) + ) } fn do_teleport_assets( @@ -1689,19 +1667,18 @@ impl Pallet { } let fees = assets.get(fee_asset_item as usize).ok_or(Error::::Empty)?.clone(); - let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type( - origin_location.clone(), - dest.clone(), + Self::build_and_execute_xcm_transfer_type( + origin_location, + dest, beneficiary, assets, TransferType::Teleport, FeesHandling::Batched { fees }, weight_limit, - )?; - Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm) + ) } - pub fn build_xcm_transfer_type( + fn build_and_execute_xcm_transfer_type( origin: Location, dest: Location, beneficiary: Location, @@ -1709,14 +1686,14 @@ impl Pallet { transfer_type: TransferType, fees: FeesHandling, weight_limit: WeightLimit, - ) -> Result<(Xcm<::RuntimeCall>, Option>), Error> { + ) -> DispatchResult { log::debug!( - target: "xcm::pallet_xcm::build_xcm_transfer_type", + target: "xcm::pallet_xcm::build_and_execute_xcm_transfer_type", "origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, transfer_type {:?}, \ fees_handling {:?}, weight_limit: {:?}", origin, dest, beneficiary, assets, transfer_type, fees, weight_limit, ); - Ok(match transfer_type { + let (mut local_xcm, remote_xcm) = match transfer_type { TransferType::LocalReserve => { let (local, remote) = Self::local_reserve_transfer_programs( origin.clone(), @@ -1766,20 +1743,7 @@ impl Pallet { )?; (local, Some(remote)) }, - }) - } - - fn execute_xcm_transfer( - origin: Location, - dest: Location, - mut local_xcm: Xcm<::RuntimeCall>, - remote_xcm: Option>, - ) -> 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::::UnweighableMessage)?; @@ -1794,7 +1758,7 @@ impl Pallet { Self::deposit_event(Event::Attempted { outcome: outcome.clone() }); outcome.ensure_complete().map_err(|error| { log::error!( - target: "xcm::pallet_xcm::execute_xcm_transfer", + target: "xcm::pallet_xcm::build_and_execute_xcm_transfer", "XCM execution failed with error {:?}", error ); Error::::LocalExecutionIncomplete @@ -1806,7 +1770,7 @@ impl Pallet { if origin != Here.into_location() { Self::charge_fees(origin.clone(), price).map_err(|error| { log::error!( - target: "xcm::pallet_xcm::execute_xcm_transfer", + target: "xcm::pallet_xcm::build_and_execute_xcm_transfer", "Unable to charge fee with error {:?}", error ); Error::::FeesNotMet From 7ccd0e4859b4a72a24ca32020a358ef685e946ca Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 10 Apr 2024 16:02:05 +0200 Subject: [PATCH 12/61] docs(xcm-executor): update comments about RecordXcm --- polkadot/xcm/xcm-executor/src/config.rs | 2 +- polkadot/xcm/xcm-executor/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/polkadot/xcm/xcm-executor/src/config.rs b/polkadot/xcm/xcm-executor/src/config.rs index e87caec50163..63b113bc250f 100644 --- a/polkadot/xcm/xcm-executor/src/config.rs +++ b/polkadot/xcm/xcm-executor/src/config.rs @@ -122,6 +122,6 @@ pub trait Config { type HrmpChannelAcceptedHandler: HandleHrmpChannelAccepted; /// Allows optional logic execution for the `HrmpChannelClosing` XCM notification. type HrmpChannelClosingHandler: HandleHrmpChannelClosing; - /// Allows recording XCMs. + /// Allows recording the last executed XCM (used by dry-run runtime APIs). type XcmRecorder: RecordXcm; } diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 8e6ce1871587..894712c9cadd 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -205,8 +205,8 @@ impl ExecuteXcm for XcmExecutor Date: Wed, 10 Apr 2024 16:40:13 +0200 Subject: [PATCH 13/61] feat(xcm-dry-run-api): add emitted events to XcmDryRunEffects --- polkadot/xcm/pallet-xcm/src/lib.rs | 2 +- .../src/dry_run.rs | 14 +- .../tests/fee_estimation.rs | 130 +++++++++++++++--- .../xcm-fee-payment-runtime-api/tests/mock.rs | 15 +- 4 files changed, 127 insertions(+), 34 deletions(-) diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index a9c19d32490e..14cea9ca982f 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -1326,7 +1326,7 @@ pub mod pallet { 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 { + let fees = if fees_transfer_type == assets_transfer_type { // no need for custom fees instructions, fees are batched with assets FeesHandling::Batched { fees } } else { diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index ea9bad64cb41..0bc320e1aa82 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -14,8 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Runtime API definition for getting xcm transfer messages. -//! These messages can be used to get the fees that need to be paid. +//! Runtime API definition for dry-running XCM-related extrinsics. +//! This API can be used to simulate XCMs and, for example, find the fees +//! that need to be paid. use codec::{Decode, Encode}; use frame_support::pallet_prelude::TypeInfo; @@ -24,9 +25,10 @@ use sp_std::vec::Vec; use xcm::prelude::*; #[derive(Encode, Decode, Debug, TypeInfo)] -pub struct XcmDryRunEffects { +pub struct XcmDryRunEffects { pub local_program: VersionedXcm<()>, pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, + pub emitted_events: Vec, } sp_api::decl_runtime_apis! { @@ -35,11 +37,11 @@ sp_api::decl_runtime_apis! { /// All calls return a vector of tuples (location, xcm) where each "xcm" is executed in "location". /// If there's local execution, the location will be "Here". /// This vector can be used to calculate both execution and delivery fees. - pub trait XcmDryRunApi { + pub trait XcmDryRunApi { /// Dry run extrinsic. - fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result; + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, ()>; /// Dry run XCM program - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result; + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result, ()>; } } diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index e9c271514414..a23d2cbf4a20 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -16,6 +16,10 @@ //! Tests for using both the XCM fee payment API and the transfers API. +use frame_support::{ + dispatch::DispatchInfo, + pallet_prelude::{DispatchClass, Pays}, +}; use sp_api::ProvideRuntimeApi; use sp_runtime::testing::H256; use xcm::prelude::*; @@ -23,8 +27,8 @@ use xcm_fee_payment_runtime_api::{XcmDryRunApi, XcmPaymentApi}; mod mock; use mock::{ - extra, new_test_ext_with_balances, new_test_ext_with_balances_and_assets, DeliveryFees, - ExistentialDeposit, HereLocation, RuntimeCall, TestClient, TestXt, + extra, fake_message_hash, new_test_ext_with_balances, new_test_ext_with_balances_and_assets, + DeliveryFees, ExistentialDeposit, HereLocation, RuntimeCall, RuntimeEvent, TestClient, TestXt, }; // Scenario: User `1` in the local chain wants to transfer assets to account `[0u8; 32]` on @@ -64,22 +68,61 @@ fn fee_estimation_for_teleport() { .build() ), ); + let send_destination = Location::new(1, [Parachain(1000)]); + let send_message = Xcm::<()>::builder_unsafe() + .receive_teleported_asset(((Parent, Parachain(2000)), 100u128)) + .clear_origin() + .buy_execution(((Parent, Parachain(2000)), 100u128), Unlimited) + .deposit_asset(AllCounted(1), [0u8; 32]) + .build(); assert_eq!( dry_run_effects.forwarded_messages, vec![( - VersionedLocation::V4(Location::new(1, [Parachain(1000)])), - VersionedXcm::V4( - Xcm::<()>::builder_unsafe() - .receive_teleported_asset(((Parent, Parachain(2000)), 100u128)) - .clear_origin() - .buy_execution(((Parent, Parachain(2000)), 100u128), Unlimited) - .deposit_asset(AllCounted(1), [0u8; 32]) - .build() - ) + VersionedLocation::V4(send_destination.clone()), + VersionedXcm::V4(send_message.clone()), ),], ); - // TODO: Weighing the local program is not relevant for extrinsics that already + assert_eq!( + dry_run_effects.emitted_events, + vec![ + RuntimeEvent::System(frame_system::Event::NewAccount { + account: 8660274132218572653 // TODO: Why is this not `1`? + }), + RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: 8660274132218572653, + free_balance: 100 + }), + RuntimeEvent::Balances(pallet_balances::Event::Minted { + who: 8660274132218572653, + amount: 100 + }), + RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 100 }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { + outcome: Outcome::Complete { used: Weight::from_parts(200, 20) }, + }), + RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 20 }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::FeesPaid { + paying: AccountIndex64 { index: 1, network: None }.into(), + fees: (Here, 20u128).into(), + }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { + origin: AccountIndex64 { index: 1, network: None }.into(), + destination: (Parent, Parachain(1000)).into(), + message: send_message.clone(), + message_id: fake_message_hash(&send_message), + }), + RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { + dispatch_info: DispatchInfo { + weight: Weight::from_parts(124414066, 0), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + } + }), + ] + ); + + // Weighing the local program is not relevant for extrinsics that already // take this weight into account. // In this case, we really only care about delivery fees. let local_program = dry_run_effects.local_program; @@ -176,20 +219,52 @@ fn dry_run_reserve_asset_transfer() { // In this case, the transfer type is `DestinationReserve`, so the remote xcm just withdraws // the assets. + let send_destination = Location::new(1, Parachain(1000)); + let send_message = Xcm::<()>::builder_unsafe() + .withdraw_asset((Parent, 100u128)) + .clear_origin() + .buy_execution((Parent, 100u128), Unlimited) + .deposit_asset(AllCounted(1), [0u8; 32]) + .build(); assert_eq!( dry_run_effects.forwarded_messages, vec![( - VersionedLocation::V4(Location::new(1, Parachain(1000))), - VersionedXcm::V4( - Xcm::<()>::builder_unsafe() - .withdraw_asset((Parent, 100u128)) - .clear_origin() - .buy_execution((Parent, 100u128), Unlimited) - .deposit_asset(AllCounted(1), [0u8; 32]) - .build() - ), + VersionedLocation::V4(send_destination.clone()), + VersionedXcm::V4(send_message.clone()), ),], ); + + assert_eq!( + dry_run_effects.emitted_events, + vec![ + RuntimeEvent::AssetsPallet(pallet_assets::Event::Burned { + asset_id: 1, + owner: 1, + balance: 100 + }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { + outcome: Outcome::Complete { used: Weight::from_parts(200, 20) } + }), + RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 20 }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::FeesPaid { + paying: AccountIndex64 { index: 1, network: None }.into(), + fees: (Here, 20u128).into() + }), + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { + origin: AccountIndex64 { index: 1, network: None }.into(), + destination: send_destination.clone(), + message: send_message.clone(), + message_id: fake_message_hash(&send_message), + }), + RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { + dispatch_info: DispatchInfo { + weight: Weight::from_parts(124414066, 0), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + } + }), + ] + ); }); } @@ -258,5 +333,18 @@ fn dry_run_xcm() { ), ),] ); + + assert_eq!( + dry_run_effects.emitted_events, + vec![ + RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 540 }), + RuntimeEvent::System(frame_system::Event::NewAccount { account: 2100 }), + RuntimeEvent::Balances(pallet_balances::Event::Endowed { + account: 2100, + free_balance: 520 + }), + RuntimeEvent::Balances(pallet_balances::Event::Minted { who: 2100, amount: 520 }), + ] + ); }); } diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 00ef105fea15..19c09abd5d87 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -137,7 +137,7 @@ impl SendXcm for TestXcmSender { } } -fn fake_message_hash(message: &Xcm) -> XcmHash { +pub(crate) fn fake_message_hash(message: &Xcm) -> XcmHash { message.using_encoded(sp_io::hashing::blake2_256) } @@ -450,14 +450,13 @@ sp_api::mock_impl_runtime_apis! { } } - impl XcmDryRunApi for RuntimeApi { - fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result { + impl XcmDryRunApi for RuntimeApi { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, ()> { // We want to record the XCM that's executed, so we can return it. pallet_xcm::ShouldRecordXcm::::put(true); // Asserting only because it's a test. assert_ok!(Executive::apply_extrinsic(extrinsic)); - // Turning recording off after we execute the extrinsic just in case. - pallet_xcm::ShouldRecordXcm::::put(false); + // Nothing gets committed to storage in runtime APIs, so there's no harm in leaving the flag as true. let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap(); let forwarded_messages = sent_xcm() .into_iter() @@ -465,13 +464,15 @@ sp_api::mock_impl_runtime_apis! { VersionedLocation::V4(location), VersionedXcm::V4(message) )).collect(); + let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); Ok(XcmDryRunEffects { local_program: VersionedXcm::<()>::V4(local_xcm), forwarded_messages, + emitted_events: events, }) } - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, max_weight: Weight) -> Result { + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, max_weight: Weight) -> Result, ()> { let origin_location: Location = origin_location.try_into()?; let xcm: Xcm = xcm.try_into()?; let mut hash = fake_message_hash(&xcm); @@ -488,9 +489,11 @@ sp_api::mock_impl_runtime_apis! { VersionedLocation::V4(location), VersionedXcm::V4(message), )).collect(); + let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); Ok(XcmDryRunEffects { local_program: VersionedXcm::V4(Xcm(Vec::new())), forwarded_messages, + emitted_events: events, }) } } From 3767e4dc76c43cc4342eb8213c837f7caa8d3b8d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 10 Apr 2024 16:45:34 +0200 Subject: [PATCH 14/61] chore(pallet-xcm): remove refactor leftovers --- polkadot/xcm/pallet-xcm/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 14cea9ca982f..286eb61a1268 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -1744,7 +1744,6 @@ impl Pallet { (local, Some(remote)) }, }; - let weight = T::Weigher::weight(&mut local_xcm).map_err(|()| Error::::UnweighableMessage)?; let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256); @@ -1758,7 +1757,7 @@ impl Pallet { 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", + target: "xcm::pallet_xcm::build_and_execute_xcm_transfer_type", "XCM execution failed with error {:?}", error ); Error::::LocalExecutionIncomplete @@ -1770,7 +1769,7 @@ impl Pallet { 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", + target: "xcm::pallet_xcm::build_and_execute_xcm_transfer_type", "Unable to charge fee with error {:?}", error ); Error::::FeesNotMet From f7c55acf713ee0e65fcf059f80c67c651ddd65b4 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 10 Apr 2024 18:28:09 +0200 Subject: [PATCH 15/61] feat(xcm-fee-payment-runtime-api): improve test --- .../tests/fee_estimation.rs | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index a23d2cbf4a20..26d6bd0422e6 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -31,27 +31,33 @@ use mock::{ DeliveryFees, ExistentialDeposit, HereLocation, RuntimeCall, RuntimeEvent, TestClient, TestXt, }; -// Scenario: User `1` in the local chain wants to transfer assets to account `[0u8; 32]` on +// Scenario: User `1` in the local chain (id 2000) wants to transfer assets to account `[0u8; 32]` on // "AssetHub". He wants to make sure he has enough for fees, so before he calls the `transfer_asset` // extrinsic to do the transfer, he decides to use the `XcmDryRunApi` and `XcmPaymentApi` runtime // APIs to estimate fees. This uses a teleport because we're dealing with the native token of the -// chain, which is registered on "AssetHub". +// chain, which is registered on "AssetHub". The fees are sent as a reserve asset transfer, since they're +// paid in the relay token. +// +// Teleport Parachain(2000) Token +// Reserve Asset Transfer Relay Token for fees +// Parachain(2000) -------------------------------------------> Parachain(1000) #[test] fn fee_estimation_for_teleport() { let _ = env_logger::builder().is_test(true).try_init(); - let balances = vec![(1, 100 + DeliveryFees::get() + ExistentialDeposit::get())]; - new_test_ext_with_balances(balances).execute_with(|| { + let who = 1; // AccountId = u64. + let balances = vec![(who, 100 + DeliveryFees::get() + ExistentialDeposit::get())]; + let assets = vec![(1, who, 50)]; + new_test_ext_with_balances_and_assets(balances, assets).execute_with(|| { let client = TestClient; let runtime_api = client.runtime_api(); - let who = 1; // AccountId = u64. let extrinsic = TestXt::new( RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { dest: Box::new(VersionedLocation::V4((Parent, Parachain(1000)).into())), beneficiary: Box::new(VersionedLocation::V4( AccountId32 { id: [0u8; 32], network: None }.into(), )), - assets: Box::new(VersionedAssets::V4((Here, 100u128).into())), - fee_asset_item: 0, + assets: Box::new(VersionedAssets::V4(vec![(Here, 100u128).into(), (Parent, 20u128).into()].into())), + fee_asset_item: 1, // Fees are paid with the RelayToken weight_limit: Unlimited, }), Some((who, extra())), @@ -63,6 +69,8 @@ fn fee_estimation_for_teleport() { dry_run_effects.local_program, VersionedXcm::V4( Xcm::builder_unsafe() + .withdraw_asset((Parent, 20u128)) + .burn_asset((Parent, 20u128)) .withdraw_asset((Here, 100u128)) .burn_asset((Here, 100u128)) .build() @@ -70,10 +78,11 @@ fn fee_estimation_for_teleport() { ); let send_destination = Location::new(1, [Parachain(1000)]); let send_message = Xcm::<()>::builder_unsafe() + .withdraw_asset((Parent, 20u128)) + .buy_execution((Parent, 20u128), Unlimited) .receive_teleported_asset(((Parent, Parachain(2000)), 100u128)) .clear_origin() - .buy_execution(((Parent, Parachain(2000)), 100u128), Unlimited) - .deposit_asset(AllCounted(1), [0u8; 32]) + .deposit_asset(AllCounted(2), [0u8; 32]) .build(); assert_eq!( dry_run_effects.forwarded_messages, @@ -97,9 +106,10 @@ fn fee_estimation_for_teleport() { who: 8660274132218572653, amount: 100 }), + RuntimeEvent::AssetsPallet(pallet_assets::Event::Burned { asset_id: 1, owner: 1, balance: 20 }), RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 100 }), RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { - outcome: Outcome::Complete { used: Weight::from_parts(200, 20) }, + outcome: Outcome::Complete { used: Weight::from_parts(400, 40) }, }), RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 20 }), RuntimeEvent::XcmPallet(pallet_xcm::Event::FeesPaid { @@ -114,7 +124,7 @@ fn fee_estimation_for_teleport() { }), RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_parts(124414066, 0), + weight: Weight::from_parts(124414070, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes, } @@ -133,7 +143,7 @@ fn fee_estimation_for_teleport() { .query_xcm_weight(H256::zero(), local_program.clone()) .unwrap() .unwrap(); - assert_eq!(weight, Weight::from_parts(200, 20)); + assert_eq!(weight, Weight::from_parts(400, 40)); let execution_fees = runtime_api .query_weight_to_asset_fee( H256::zero(), @@ -142,7 +152,7 @@ fn fee_estimation_for_teleport() { ) .unwrap() .unwrap(); - assert_eq!(execution_fees, 220); + assert_eq!(execution_fees, 440); let mut forwarded_messages_iter = dry_run_effects.forwarded_messages.into_iter(); @@ -170,7 +180,7 @@ fn fee_estimation_for_teleport() { ) .unwrap() .unwrap(); - assert_eq!(remote_execution_fees, 440u128); + assert_eq!(remote_execution_fees, 550); // Now we know that locally we need to use `execution_fees` and // `delivery_fees`. @@ -181,6 +191,12 @@ fn fee_estimation_for_teleport() { }); } +// Same scenario as in `fee_estimation_for_teleport`, but the user in parachain 2000 wants +// to send relay tokens over to parachain 1000. +// +// Reserve Asset Transfer Relay Token +// Reserve Asset Transfer Relay Token for fees +// Parachain(2000) -------------------------------------------> Parachain(1000) #[test] fn dry_run_reserve_asset_transfer() { let _ = env_logger::builder().is_test(true).try_init(); From 26c2b7dccbc1ff0ddf9b0ea3ff1441c6849a20b9 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 10 Apr 2024 18:41:50 +0200 Subject: [PATCH 16/61] feat(xcm-dry-run-api): add execution result to XcmDryRunEffects and ExtrinsicDryRunEffects --- .../xcm-fee-payment-runtime-api/src/dry_run.rs | 14 +++++++++++--- .../xcm/xcm-fee-payment-runtime-api/src/lib.rs | 2 +- .../xcm-fee-payment-runtime-api/tests/mock.rs | 17 +++++++++-------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index 0bc320e1aa82..7a33c76e342e 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -19,16 +19,24 @@ //! that need to be paid. use codec::{Decode, Encode}; -use frame_support::pallet_prelude::TypeInfo; +use frame_support::pallet_prelude::{TypeInfo, DispatchResult}; use sp_runtime::traits::Block as BlockT; use sp_std::vec::Vec; use xcm::prelude::*; #[derive(Encode, Decode, Debug, TypeInfo)] -pub struct XcmDryRunEffects { +pub struct ExtrinsicDryRunEffects { pub local_program: VersionedXcm<()>, pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, pub emitted_events: Vec, + pub execution_result: DispatchResult, +} + +#[derive(Encode, Decode, Debug, TypeInfo)] +pub struct XcmDryRunEffects { + pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, + pub emitted_events: Vec, + pub execution_result: Outcome, } sp_api::decl_runtime_apis! { @@ -39,7 +47,7 @@ sp_api::decl_runtime_apis! { /// This vector can be used to calculate both execution and delivery fees. pub trait XcmDryRunApi { /// Dry run extrinsic. - fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, ()>; + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, ()>; /// Dry run XCM program fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result, ()>; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs index 52ee65381cdd..06aa31018483 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs @@ -31,5 +31,5 @@ mod dry_run; /// Estimates fees. mod fees; -pub use dry_run::{XcmDryRunApi, XcmDryRunEffects}; +pub use dry_run::{XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects}; pub use fees::{Error as XcmPaymentApiError, XcmPaymentApi}; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 19c09abd5d87..b4d5d186b79c 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -45,7 +45,7 @@ use xcm_executor::{ }; use xcm_fee_payment_runtime_api::{ - XcmDryRunApi, XcmDryRunEffects, XcmPaymentApi, XcmPaymentApiError, + XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects, XcmPaymentApi, XcmPaymentApiError, }; construct_runtime! { @@ -451,13 +451,13 @@ sp_api::mock_impl_runtime_apis! { } impl XcmDryRunApi for RuntimeApi { - fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, ()> { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, ()> { // We want to record the XCM that's executed, so we can return it. pallet_xcm::ShouldRecordXcm::::put(true); // Asserting only because it's a test. - assert_ok!(Executive::apply_extrinsic(extrinsic)); + let result = Executive::apply_extrinsic(extrinsic).unwrap(); // TODO: Why is it a double result? // Nothing gets committed to storage in runtime APIs, so there's no harm in leaving the flag as true. - let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap(); + let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); let forwarded_messages = sent_xcm() .into_iter() .map(|(location, message)| ( @@ -465,10 +465,11 @@ sp_api::mock_impl_runtime_apis! { VersionedXcm::V4(message) )).collect(); let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); - Ok(XcmDryRunEffects { + Ok(ExtrinsicDryRunEffects { local_program: VersionedXcm::<()>::V4(local_xcm), forwarded_messages, emitted_events: events, + execution_result: result, }) } @@ -476,13 +477,13 @@ sp_api::mock_impl_runtime_apis! { let origin_location: Location = origin_location.try_into()?; let xcm: Xcm = xcm.try_into()?; let mut hash = fake_message_hash(&xcm); - assert_ok!(XcmExecutor::::prepare_and_execute( + let result = XcmExecutor::::prepare_and_execute( origin_location, xcm, &mut hash, max_weight, Weight::zero(), - ).ensure_complete()); // Asserting only because it's a test. + ); let forwarded_messages = sent_xcm() .into_iter() .map(|(location, message)| ( @@ -491,9 +492,9 @@ sp_api::mock_impl_runtime_apis! { )).collect(); let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); Ok(XcmDryRunEffects { - local_program: VersionedXcm::V4(Xcm(Vec::new())), forwarded_messages, emitted_events: events, + execution_result: result, }) } } From 609137b8ce112563a57fccca72611184ecbd638e Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 11:52:10 +0200 Subject: [PATCH 17/61] feat(xcm-dry-run-api): add error enum and return error when extrinsic is invalid --- .../src/dry_run.rs | 40 ++++++++++++++++--- .../xcm-fee-payment-runtime-api/src/fees.rs | 2 +- .../xcm-fee-payment-runtime-api/src/lib.rs | 8 ++-- .../tests/fee_estimation.rs | 8 ++-- .../xcm-fee-payment-runtime-api/tests/mock.rs | 36 +++++++++++++---- 5 files changed, 71 insertions(+), 23 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index 7a33c76e342e..69ac9ae4eb04 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -24,19 +24,28 @@ use sp_runtime::traits::Block as BlockT; use sp_std::vec::Vec; use xcm::prelude::*; +/// Effects of dry-running an extrinsic. #[derive(Encode, Decode, Debug, TypeInfo)] pub struct ExtrinsicDryRunEffects { + /// The result of executing the extrinsic. + pub execution_result: DispatchResult, + /// The list of events fired by the extrinsic. + pub emitted_events: Vec, + /// The local XCM program that was attempted to be executed, if any. pub local_program: VersionedXcm<()>, + /// The list of XCMs that were queued for sending. pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, - pub emitted_events: Vec, - pub execution_result: DispatchResult, } +/// Effects of dry-running an XCM program. #[derive(Encode, Decode, Debug, TypeInfo)] pub struct XcmDryRunEffects { - pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, - pub emitted_events: Vec, + /// The outcome of the XCM program execution. pub execution_result: Outcome, + /// List of events fired by the XCM program execution. + pub emitted_events: Vec, + /// List of queued messages for sending. + pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, } sp_api::decl_runtime_apis! { @@ -45,11 +54,30 @@ sp_api::decl_runtime_apis! { /// All calls return a vector of tuples (location, xcm) where each "xcm" is executed in "location". /// If there's local execution, the location will be "Here". /// This vector can be used to calculate both execution and delivery fees. + /// + /// Extrinsics or XCMs might fail when executed, this doesn't mean the result of these calls will be an `Err`. + /// In those cases, there might still be a valid result, with the execution error inside it. + /// The only reasons why these calls might return an error are listed in the [`Error`] enum. pub trait XcmDryRunApi { /// Dry run extrinsic. - fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, ()>; + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, Error>; /// Dry run XCM program - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result, ()>; + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result, Error>; } } + +#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] +pub enum Error { + /// An API call is unsupported. + #[codec(index = 0)] + Unimplemented, + + /// Converting a versioned data structure from one version to another failed. + #[codec(index = 1)] + VersionedConversionFailed, + + /// Extrinsic was invalid. + #[codec(index = 2)] + InvalidExtrinsic, +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs index 796bfda880bd..572d4edf5338 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/fees.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Runtime API definition for getting xcm fees. +//! Runtime API definition for getting XCM fees. use codec::{Decode, Encode}; use frame_support::pallet_prelude::TypeInfo; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs index 06aa31018483..7a94da23c735 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs @@ -25,11 +25,11 @@ #![cfg_attr(not(feature = "std"), no_std)] /// Dry-run API. -/// Returns the messages that need to be passed to the fees API. +/// Given an extrinsic or an XCM program, it returns the outcome of its execution. mod dry_run; -/// Main API. -/// Estimates fees. +/// Fee estimation API. +/// Given an XCM program, it will return the fees needed to execute it properly or send it. mod fees; -pub use dry_run::{XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects}; +pub use dry_run::{XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects, Error as XcmDryRunApiError}; pub use fees::{Error as XcmPaymentApiError, XcmPaymentApi}; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 26d6bd0422e6..ded32f2b0694 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Tests for using both the XCM fee payment API and the transfers API. +//! Tests for using both the XCM fee payment API and the dry-run API. use frame_support::{ dispatch::DispatchInfo, @@ -164,7 +164,7 @@ fn fee_estimation_for_teleport() { .unwrap(); assert_eq!(delivery_fees, VersionedAssets::V4((Here, 20u128).into())); - // TODO: This would have to be the runtime API of the destination, + // This would have to be the runtime API of the destination, // which we have the location for. // If I had a mock runtime configured for "AssetHub" then I would use the // runtime APIs from that. @@ -289,9 +289,7 @@ fn dry_run_xcm() { let _ = env_logger::builder().is_test(true).try_init(); let who = 1; // AccountId = u64. let transfer_amount = 100u128; - // TODO: We need to build the XCM to weigh it and then build the real XCM - // that can pay for fees. - // There might be a better way. + // We need to build the XCM to weigh it and then build the real XCM that can pay for fees. let inner_xcm = Xcm::<()>::builder_unsafe() .buy_execution((Here, 1u128), Unlimited) // We'd need to query the destination chain for fees. .deposit_asset(AllCounted(1), [0u8; 32]) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index b4d5d186b79c..2c1303db4c06 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -19,7 +19,7 @@ use codec::Encode; use frame_support::{ - assert_ok, construct_runtime, derive_impl, parameter_types, + construct_runtime, derive_impl, parameter_types, traits::{ AsEnsureOriginWithArg, ConstU128, ConstU32, Contains, ContainsPair, Everything, Nothing, OriginTrait, @@ -45,7 +45,8 @@ use xcm_executor::{ }; use xcm_fee_payment_runtime_api::{ - XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects, XcmPaymentApi, XcmPaymentApiError, + XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects, XcmDryRunApiError, + XcmPaymentApi, XcmPaymentApiError, }; construct_runtime! { @@ -451,11 +452,18 @@ sp_api::mock_impl_runtime_apis! { } impl XcmDryRunApi for RuntimeApi { - fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, ()> { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { // We want to record the XCM that's executed, so we can return it. pallet_xcm::ShouldRecordXcm::::put(true); // Asserting only because it's a test. - let result = Executive::apply_extrinsic(extrinsic).unwrap(); // TODO: Why is it a double result? + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; // Nothing gets committed to storage in runtime APIs, so there's no harm in leaving the flag as true. let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); let forwarded_messages = sent_xcm() @@ -473,9 +481,23 @@ sp_api::mock_impl_runtime_apis! { }) } - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, max_weight: Weight) -> Result, ()> { - let origin_location: Location = origin_location.try_into()?; - let xcm: Xcm = xcm.try_into()?; + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, max_weight: Weight) -> Result, XcmDryRunApiError> { + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let xcm: Xcm = xcm.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; let mut hash = fake_message_hash(&xcm); let result = XcmExecutor::::prepare_and_execute( origin_location, From 465e5eeac317289882bba14763bc06094f6e72bf Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 11:59:16 +0200 Subject: [PATCH 18/61] Update polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs Co-authored-by: Adrian Catangiu --- polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 2c1303db4c06..2bb16c728093 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -455,7 +455,6 @@ sp_api::mock_impl_runtime_apis! { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { // We want to record the XCM that's executed, so we can return it. pallet_xcm::ShouldRecordXcm::::put(true); - // Asserting only because it's a test. let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_extrinsic", From b6864fe264eb5d8e9cbd33ae5b3043b2fa03872c Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 13:15:13 +0200 Subject: [PATCH 19/61] fix(xcm-fee-payment-runtime-api): reexport inner modules instead of specific items --- polkadot/runtime/rococo/src/lib.rs | 4 ++-- polkadot/runtime/rococo/src/xcm_config.rs | 1 + polkadot/runtime/westend/src/lib.rs | 4 ++-- polkadot/runtime/westend/src/xcm_config.rs | 1 + polkadot/xcm/pallet-xcm/src/lib.rs | 2 +- polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs | 7 ++----- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 894d7fac2f0a..866de5fd4542 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -127,7 +127,7 @@ use governance::{ pallet_custom_origins, AuctionAdmin, Fellows, GeneralAdmin, LeaseAdmin, Treasurer, TreasurySpender, }; -use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; +use xcm_fee_payment_runtime_api::fees::Error as XcmPaymentApiError; #[cfg(test)] mod tests; @@ -1662,7 +1662,7 @@ sp_api::impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { if !matches!(xcm_version, 3 | 4) { return Err(XcmPaymentApiError::UnhandledXcmVersion); diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index c7063bd7ad61..decbc795143f 100644 --- a/polkadot/runtime/rococo/src/xcm_config.rs +++ b/polkadot/runtime/rococo/src/xcm_config.rs @@ -224,6 +224,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } parameter_types! { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 4930610c1d80..161ee15f79a0 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -106,7 +106,7 @@ use xcm::{ }; use xcm_builder::PayOverXcm; -use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; +use xcm_fee_payment_runtime_api::fees::Error as XcmPaymentApiError; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -2200,7 +2200,7 @@ sp_api::impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { if !matches!(xcm_version, 3 | 4) { return Err(XcmPaymentApiError::UnhandledXcmVersion); diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs index f661c4b0e4f4..c6c5fb9e72a4 100644 --- a/polkadot/runtime/westend/src/xcm_config.rs +++ b/polkadot/runtime/westend/src/xcm_config.rs @@ -222,6 +222,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } parameter_types! { diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 286eb61a1268..730db792aceb 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -61,7 +61,7 @@ use xcm_executor::{ }, AssetsInHolding, }; -use xcm_fee_payment_runtime_api::XcmPaymentApiError; +use xcm_fee_payment_runtime_api::fees::Error as XcmPaymentApiError; #[cfg(any(feature = "try-runtime", test))] use sp_runtime::TryRuntimeError; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs index 7a94da23c735..616ee4c2eccb 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/lib.rs @@ -26,10 +26,7 @@ /// Dry-run API. /// Given an extrinsic or an XCM program, it returns the outcome of its execution. -mod dry_run; +pub mod dry_run; /// Fee estimation API. /// Given an XCM program, it will return the fees needed to execute it properly or send it. -mod fees; - -pub use dry_run::{XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects, Error as XcmDryRunApiError}; -pub use fees::{Error as XcmPaymentApiError, XcmPaymentApi}; +pub mod fees; From aba11e8c87594a29ab593fdf48481a75f3706cfb Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 13:51:42 +0200 Subject: [PATCH 20/61] fix(xcm-recorder): add XcmRecorder config item to all XCM configs --- cumulus/pallets/xcmp-queue/src/mock.rs | 1 + .../runtimes/assets/asset-hub-rococo/src/xcm_config.rs | 1 + .../assets/asset-hub-westend/src/xcm_config.rs | 1 + .../bridge-hubs/bridge-hub-rococo/src/xcm_config.rs | 1 + .../bridge-hubs/bridge-hub-westend/src/xcm_config.rs | 1 + .../collectives/collectives-westend/src/xcm_config.rs | 1 + .../contracts/contracts-rococo/src/xcm_config.rs | 1 + .../coretime/coretime-rococo/src/xcm_config.rs | 1 + .../coretime/coretime-westend/src/xcm_config.rs | 1 + .../runtimes/glutton/glutton-westend/src/xcm_config.rs | 1 + .../runtimes/people/people-rococo/src/xcm_config.rs | 1 + .../runtimes/people/people-westend/src/xcm_config.rs | 1 + .../runtimes/starters/shell/src/xcm_config.rs | 1 + .../runtimes/testing/penpal/src/xcm_config.rs | 1 + .../runtimes/testing/rococo-parachain/src/lib.rs | 1 + polkadot/node/service/src/fake_runtime_api.rs | 10 +++++----- polkadot/runtime/test-runtime/src/xcm_config.rs | 1 + .../xcm/pallet-xcm-benchmarks/src/fungible/mock.rs | 1 + polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs | 1 + polkadot/xcm/pallet-xcm/src/tests/mod.rs | 8 ++++---- polkadot/xcm/xcm-builder/src/tests/mock.rs | 1 + polkadot/xcm/xcm-builder/src/tests/pay/mock.rs | 1 + polkadot/xcm/xcm-builder/tests/mock/mod.rs | 1 + .../tests/fee_estimation.rs | 3 ++- polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs | 8 +++++--- polkadot/xcm/xcm-simulator/example/src/parachain.rs | 1 + polkadot/xcm/xcm-simulator/example/src/relay_chain.rs | 1 + polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs | 1 + polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs | 1 + .../frame/contracts/mock-network/src/parachain.rs | 1 + .../frame/contracts/mock-network/src/relay_chain.rs | 1 + templates/parachain/runtime/src/configs/xcm_config.rs | 1 + 32 files changed, 44 insertions(+), 13 deletions(-) diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs index 9d9a723cf8b5..e258576aa3f6 100644 --- a/cumulus/pallets/xcmp-queue/src/mock.rs +++ b/cumulus/pallets/xcmp-queue/src/mock.rs @@ -178,6 +178,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } pub type XcmRouter = ( 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 1c58abcb379e..7eb5f13ed1ef 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 @@ -636,6 +636,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM 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 360b1a7055b7..72bd0489d967 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 @@ -657,6 +657,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 063c999aa7ad..2d67544a2108 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -344,6 +344,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } pub type PriceForParentDelivery = diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index 4870b4a52d7a..1fcfa10d4b65 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -272,6 +272,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } pub type PriceForParentDelivery = diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs index 21ccd3b9cdb0..e999e1ddeb4e 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs @@ -294,6 +294,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. 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 3863ea5022f9..c716f8737928 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -200,6 +200,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. 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 b0b276128272..c74128c692e8 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs @@ -265,6 +265,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs index 346bdfa4d8c9..5b370dcb41cd 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs @@ -272,6 +272,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs index 15bb519e115c..f712b4114ddb 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs @@ -91,6 +91,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs index 1a42adeafd1d..a682cf319c01 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs @@ -277,6 +277,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs index 114923270645..75ee151d8c1f 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs @@ -285,6 +285,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Converts a local signed origin into an XCM location. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs index df89158729cd..a9a79e995630 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -91,6 +91,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index c12372abbe90..62f87639bf32 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -347,6 +347,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance. diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 154c2c460004..171b0e27c435 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -488,6 +488,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index c6cfb7a27d04..49fb3c6cf58c 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -398,20 +398,20 @@ sp_api::impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { - fn query_acceptable_payment_assets(_: xcm::Version) -> Result, xcm_fee_payment_runtime_api::Error> { + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(_: xcm::Version) -> Result, xcm_fee_payment_runtime_api::fees::Error> { unimplemented!() } - fn query_weight_to_asset_fee(_: Weight, _: VersionedAssetId) -> Result { + fn query_weight_to_asset_fee(_: Weight, _: VersionedAssetId) -> Result { unimplemented!() } - fn query_xcm_weight(_: VersionedXcm<()>) -> Result { + fn query_xcm_weight(_: VersionedXcm<()>) -> Result { unimplemented!() } - fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>) -> Result { + fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>) -> Result { unimplemented!() } } diff --git a/polkadot/runtime/test-runtime/src/xcm_config.rs b/polkadot/runtime/test-runtime/src/xcm_config.rs index 8411b79f2529..f40e0606edbd 100644 --- a/polkadot/runtime/test-runtime/src/xcm_config.rs +++ b/polkadot/runtime/test-runtime/src/xcm_config.rs @@ -156,6 +156,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = super::Xcm; } impl pallet_xcm::Config for crate::Runtime { diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index c831cd024659..ef57cf9f7fa1 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -148,6 +148,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } impl crate::Config for Test { diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index 534f7d85ea2e..2d9a338637aa 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -138,6 +138,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } parameter_types! { diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index 62d9867aee86..7a5de931e732 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -1272,11 +1272,11 @@ fn record_xcm_works() { let balances = vec![(ALICE, INITIAL_BALANCE)]; new_test_ext_with_balances(balances).execute_with(|| { let message = Xcm::::builder() - .withdraw_asset((Here, SEND_AMOUNT).into()) - .buy_execution((Here, SEND_AMOUNT).into(), Unlimited) + .withdraw_asset((Here, SEND_AMOUNT)) + .buy_execution((Here, SEND_AMOUNT), Unlimited) .deposit_asset( - AllCounted(1).into(), - Junction::AccountId32 { network: None, id: BOB.into() }.into(), + AllCounted(1), + Junction::AccountId32 { network: None, id: BOB.into() }, ) .build(); // Test default values. diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs index 3d03ab054248..eecb778f5034 100644 --- a/polkadot/xcm/xcm-builder/src/tests/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs @@ -745,6 +745,7 @@ impl Config for TestConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } pub fn fungible_multi_asset(location: Location, amount: u128) -> Asset { diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index 019113a12b2f..34b204b434d6 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -221,6 +221,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } parameter_types! { diff --git a/polkadot/xcm/xcm-builder/tests/mock/mod.rs b/polkadot/xcm/xcm-builder/tests/mock/mod.rs index f3cf5ab26490..51dc90fc9d22 100644 --- a/polkadot/xcm/xcm-builder/tests/mock/mod.rs +++ b/polkadot/xcm/xcm-builder/tests/mock/mod.rs @@ -207,6 +207,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index ded32f2b0694..ccc0bdf1c79f 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -23,7 +23,8 @@ use frame_support::{ use sp_api::ProvideRuntimeApi; use sp_runtime::testing::H256; use xcm::prelude::*; -use xcm_fee_payment_runtime_api::{XcmDryRunApi, XcmPaymentApi}; +use xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi; +use xcm_fee_payment_runtime_api::fees::XcmPaymentApi; mod mock; use mock::{ diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 2bb16c728093..bff8f0887a5a 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -44,9 +44,11 @@ use xcm_executor::{ XcmExecutor, }; -use xcm_fee_payment_runtime_api::{ - XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects, XcmDryRunApiError, - XcmPaymentApi, XcmPaymentApiError, +use xcm_fee_payment_runtime_api::dry_run::{ + XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects, Error as XcmDryRunApiError, +}; +use xcm_fee_payment_runtime_api::fees::{ + XcmPaymentApi, Error as XcmPaymentApiError, }; construct_runtime! { diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain.rs b/polkadot/xcm/xcm-simulator/example/src/parachain.rs index 86401d756af3..6b7e7623ec49 100644 --- a/polkadot/xcm/xcm-simulator/example/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/parachain.rs @@ -254,6 +254,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } #[frame_support::pallet] diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs index 286d0038e187..2a1e80349d9b 100644 --- a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs @@ -201,6 +201,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs index cadfc1e7200c..2e943c3490a9 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -159,6 +159,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } #[frame_support::pallet] diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs index 6790b535d169..5134ed53cfec 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -160,6 +160,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = (); } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/substrate/frame/contracts/mock-network/src/parachain.rs b/substrate/frame/contracts/mock-network/src/parachain.rs index d4ad47581d16..41aee1058920 100644 --- a/substrate/frame/contracts/mock-network/src/parachain.rs +++ b/substrate/frame/contracts/mock-network/src/parachain.rs @@ -285,6 +285,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } impl mock_msg_queue::Config for Runtime { diff --git a/substrate/frame/contracts/mock-network/src/relay_chain.rs b/substrate/frame/contracts/mock-network/src/relay_chain.rs index 470304ed357e..9821d5b6252b 100644 --- a/substrate/frame/contracts/mock-network/src/relay_chain.rs +++ b/substrate/frame/contracts/mock-network/src/relay_chain.rs @@ -185,6 +185,7 @@ impl Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = XcmPallet; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/templates/parachain/runtime/src/configs/xcm_config.rs b/templates/parachain/runtime/src/configs/xcm_config.rs index 13da2363b053..0f6526f53913 100644 --- a/templates/parachain/runtime/src/configs/xcm_config.rs +++ b/templates/parachain/runtime/src/configs/xcm_config.rs @@ -140,6 +140,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); + type XcmRecorder = PolkadotXcm; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. From 3a4210b4ade28a127c1cf52463dddb03ec2b6754 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 13:54:48 +0200 Subject: [PATCH 21/61] fix: fmt --- polkadot/xcm/pallet-xcm/src/tests/mod.rs | 5 +--- .../src/dry_run.rs | 2 +- .../tests/fee_estimation.rs | 25 +++++++++++-------- .../xcm-fee-payment-runtime-api/tests/mock.rs | 8 +++--- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index 7a5de931e732..6c8515bbb773 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -1274,10 +1274,7 @@ fn record_xcm_works() { let message = Xcm::::builder() .withdraw_asset((Here, SEND_AMOUNT)) .buy_execution((Here, SEND_AMOUNT), Unlimited) - .deposit_asset( - AllCounted(1), - Junction::AccountId32 { network: None, id: BOB.into() }, - ) + .deposit_asset(AllCounted(1), Junction::AccountId32 { network: None, id: BOB.into() }) .build(); // Test default values. assert_eq!(ShouldRecordXcm::::get(), false); diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index 69ac9ae4eb04..9c4a43b0aa74 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -19,7 +19,7 @@ //! that need to be paid. use codec::{Decode, Encode}; -use frame_support::pallet_prelude::{TypeInfo, DispatchResult}; +use frame_support::pallet_prelude::{DispatchResult, TypeInfo}; use sp_runtime::traits::Block as BlockT; use sp_std::vec::Vec; use xcm::prelude::*; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index ccc0bdf1c79f..043c754a345d 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -23,8 +23,7 @@ use frame_support::{ use sp_api::ProvideRuntimeApi; use sp_runtime::testing::H256; use xcm::prelude::*; -use xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi; -use xcm_fee_payment_runtime_api::fees::XcmPaymentApi; +use xcm_fee_payment_runtime_api::{dry_run::XcmDryRunApi, fees::XcmPaymentApi}; mod mock; use mock::{ @@ -32,12 +31,12 @@ use mock::{ DeliveryFees, ExistentialDeposit, HereLocation, RuntimeCall, RuntimeEvent, TestClient, TestXt, }; -// Scenario: User `1` in the local chain (id 2000) wants to transfer assets to account `[0u8; 32]` on -// "AssetHub". He wants to make sure he has enough for fees, so before he calls the `transfer_asset` -// extrinsic to do the transfer, he decides to use the `XcmDryRunApi` and `XcmPaymentApi` runtime -// APIs to estimate fees. This uses a teleport because we're dealing with the native token of the -// chain, which is registered on "AssetHub". The fees are sent as a reserve asset transfer, since they're -// paid in the relay token. +// Scenario: User `1` in the local chain (id 2000) wants to transfer assets to account `[0u8; 32]` +// on "AssetHub". He wants to make sure he has enough for fees, so before he calls the +// `transfer_asset` extrinsic to do the transfer, he decides to use the `XcmDryRunApi` and +// `XcmPaymentApi` runtime APIs to estimate fees. This uses a teleport because we're dealing with +// the native token of the chain, which is registered on "AssetHub". The fees are sent as a reserve +// asset transfer, since they're paid in the relay token. // // Teleport Parachain(2000) Token // Reserve Asset Transfer Relay Token for fees @@ -57,7 +56,9 @@ fn fee_estimation_for_teleport() { beneficiary: Box::new(VersionedLocation::V4( AccountId32 { id: [0u8; 32], network: None }.into(), )), - assets: Box::new(VersionedAssets::V4(vec![(Here, 100u128).into(), (Parent, 20u128).into()].into())), + assets: Box::new(VersionedAssets::V4( + vec![(Here, 100u128).into(), (Parent, 20u128).into()].into(), + )), fee_asset_item: 1, // Fees are paid with the RelayToken weight_limit: Unlimited, }), @@ -107,7 +108,11 @@ fn fee_estimation_for_teleport() { who: 8660274132218572653, amount: 100 }), - RuntimeEvent::AssetsPallet(pallet_assets::Event::Burned { asset_id: 1, owner: 1, balance: 20 }), + RuntimeEvent::AssetsPallet(pallet_assets::Event::Burned { + asset_id: 1, + owner: 1, + balance: 20 + }), RuntimeEvent::Balances(pallet_balances::Event::Burned { who: 1, amount: 100 }), RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { outcome: Outcome::Complete { used: Weight::from_parts(400, 40) }, diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index bff8f0887a5a..561b92eed973 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -44,11 +44,9 @@ use xcm_executor::{ XcmExecutor, }; -use xcm_fee_payment_runtime_api::dry_run::{ - XcmDryRunApi, ExtrinsicDryRunEffects, XcmDryRunEffects, Error as XcmDryRunApiError, -}; -use xcm_fee_payment_runtime_api::fees::{ - XcmPaymentApi, Error as XcmPaymentApiError, +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunApi, XcmDryRunEffects}, + fees::{Error as XcmPaymentApiError, XcmPaymentApi}, }; construct_runtime! { From eca20db593ee29ceefd5dedf20d43868bddb97a8 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 14:21:10 +0200 Subject: [PATCH 22/61] fix(xcm-fee-payment-runtime-api): add missing BenchmarkHelper in mock --- polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 561b92eed973..0fd57a20aa98 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -110,6 +110,8 @@ impl pallet_assets::Config for TestRuntime { type MetadataDepositBase = ConstU128<1>; type MetadataDepositPerByte = ConstU128<1>; type ApprovalDeposit = ConstU128<1>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } thread_local! { From 34cdb177f83b9892222f5983222f141956cc27be Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 16:35:18 +0200 Subject: [PATCH 23/61] doc: add prdoc --- prdoc/pr_3872.prdoc | 72 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 prdoc/pr_3872.prdoc diff --git a/prdoc/pr_3872.prdoc b/prdoc/pr_3872.prdoc new file mode 100644 index 000000000000..5dfff9c83476 --- /dev/null +++ b/prdoc/pr_3872.prdoc @@ -0,0 +1,72 @@ +title: XcmDryRunApi - Runtime API for dry-running extrinsics and XCM programs. + +doc: + - audience: Runtime Dev + description: | + This PR introduces a new runtime API, the XcmDryRunApi, that allows dry-running + extrinsics and XCM programs to get their execution effects. + These effects include: + - Local execution result, either pass or fail + - Emitted events + - Forwarded XCMs + - In the case of extrinsics, the XCM program that they execute + This API can be used on its own to test extrinsics or XCM programs, + or used alongside the XcmPaymentApi to estimate execution and delivery + fees. + + This PR also adds a new configuration item to XCM: XcmRecorder. + This can be set to either (), the xcm pallet, or some custom implementation. + If set to (), the dry run API will not return the local XCM program executed + by running an extrinsic. + After this PR, it is necessary to add the new configuration item to your xcm + configs. + +crates: + - name: xcm-fee-payment-runtime-api + bump: major + - name: pallet-xcm + bump: minor + - name: xcm-executor + bump: minor + - name: rococo-runtime + bump: minor + - name: westend-runtime + bump: minor + - name: pallet-xcm-benchmarks + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: bridge-hub-rococo-runtime + bump: minor + - name: bridge-hub-westend-runtime + bump: minor + - name: collectives-westend-runtime + bump: minor + - name: contracts-rococo-runtime + bump: minor + - name: coretime-rococo-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor + - name: glutton-westend-runtime + bump: minor + - name: people-rococo-runtime + bump: minor + - name: people-westend-runtime + bump: minor + - name: shell-runtime + bump: minor + - name: penpal-runtime + bump: minor + - name: rococo-parachain-runtime + bump: minor + - name: polkadot-service + bump: minor + - name: polkadot-test-runtime + bump: minor + - name: parachain-template-runtime + bump: minor + - name: pallet-contracts-mock-network + bump: minor From 317fb6ea7247c879e3c16f63fe42345b534dfd9d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 16:43:42 +0200 Subject: [PATCH 24/61] feat(xcm-dry-run-api): add XcmDryRunApi to fake_runtime_api --- polkadot/node/service/src/fake_runtime_api.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index 49fb3c6cf58c..21367acd5976 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -415,4 +415,14 @@ sp_api::impl_runtime_apis! { unimplemented!() } } + + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(_: ::Extrinsic) -> Result, xcm_fee_payment_runtime_api::dry_run::Error> { + unimplemented!() + } + + fn dry_run_xcm(_: VersionedLocation, _: VersionedXcm<()>, _: Weight) -> Result, xcm_fee_payment_runtime_api::dry_run::Error> { + unimplemented!() + } + } } From 08d5ebca99ee2b970b1cdc98a5c813f273432ed2 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 16:48:22 +0200 Subject: [PATCH 25/61] doc: update prdoc --- prdoc/pr_3872.prdoc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/prdoc/pr_3872.prdoc b/prdoc/pr_3872.prdoc index 5dfff9c83476..6e23933b180a 100644 --- a/prdoc/pr_3872.prdoc +++ b/prdoc/pr_3872.prdoc @@ -20,6 +20,18 @@ doc: by running an extrinsic. After this PR, it is necessary to add the new configuration item to your xcm configs. + - audience: Runtime User + description: | + This PR introduces a new runtime API, the XcmDryRunApi, that allows dry-running + extrinsics and XCM programs to get their execution effects. + These effects include: + - Local execution result, either pass or fail + - Emitted events + - Forwarded XCMs + - In the case of extrinsics, the XCM program that they execute + This API can be used on its own to test extrinsics or XCM programs, + or used alongside the XcmPaymentApi to estimate execution and delivery + fees. crates: - name: xcm-fee-payment-runtime-api From a202ddf4917b76ce9467d62b621ae603b838c21e Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 16:57:28 +0200 Subject: [PATCH 26/61] fix(xcm-fee-payment-runtime-api): add runtime-benchmarks feature and propagate --- polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index 6d497ec186ba..b6bb93ecbcdf 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -58,3 +58,13 @@ std = [ "xcm-executor/std", "frame-executive/std", ] +runtime-benchmarks = [ + "sp-runtime/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", +] From 2462d7a5d515704fbbd6ffd03301867a29f32d5b Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 17:02:32 +0200 Subject: [PATCH 27/61] fix(prdoc): fix crate names and add xcm-builder --- prdoc/pr_3872.prdoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prdoc/pr_3872.prdoc b/prdoc/pr_3872.prdoc index 6e23933b180a..3a5be3d2bc74 100644 --- a/prdoc/pr_3872.prdoc +++ b/prdoc/pr_3872.prdoc @@ -38,7 +38,9 @@ crates: bump: major - name: pallet-xcm bump: minor - - name: xcm-executor + - name: staging-xcm-executor + bump: minor + - name: staging-xcm-builder bump: minor - name: rococo-runtime bump: minor From 84b1e0ba2ce72a8c88740a6e5114cfa2ea4bf931 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 17:27:19 +0200 Subject: [PATCH 28/61] fix(xcm-fee-payment-runtime-api): propagate features and format Cargo.toml file --- polkadot/node/service/Cargo.toml | 1 + polkadot/runtime/rococo/Cargo.toml | 1 + polkadot/runtime/westend/Cargo.toml | 1 + polkadot/xcm/pallet-xcm/Cargo.toml | 1 + .../xcm-fee-payment-runtime-api/Cargo.toml | 20 +++++++++---------- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index 9688ab556473..7c010778d50d 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -206,6 +206,7 @@ runtime-benchmarks = [ "service/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "westend-runtime?/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 20a914fb8085..fa691cc0f875 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -265,6 +265,7 @@ runtime-benchmarks = [ "sp-staking/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "frame-executive/try-runtime", diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index d726adfb8e6e..f02cae0e9d49 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -283,6 +283,7 @@ runtime-benchmarks = [ "sp-staking/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "frame-election-provider-support/try-runtime", diff --git a/polkadot/xcm/pallet-xcm/Cargo.toml b/polkadot/xcm/pallet-xcm/Cargo.toml index 460597e6649a..fc4d23426fbc 100644 --- a/polkadot/xcm/pallet-xcm/Cargo.toml +++ b/polkadot/xcm/pallet-xcm/Cargo.toml @@ -69,6 +69,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index b6bb93ecbcdf..4cf2eae76686 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -42,29 +42,29 @@ env_logger = "0.9.0" default = ["std"] std = [ "codec/std", + "frame-executive/std", "frame-support/std", + "frame-system/std", + "pallet-assets/std", + "pallet-balances/std", + "pallet-xcm/std", "scale-info/std", "sp-api/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", "sp-weights/std", - "xcm/std", - "frame-system/std", - "pallet-xcm/std", "xcm-builder/std", - "sp-io/std", - "pallet-balances/std", - "pallet-assets/std", "xcm-executor/std", - "frame-executive/std", + "xcm/std", ] runtime-benchmarks = [ - "sp-runtime/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", "xcm-executor/runtime-benchmarks", ] From 114860fa63e0db7dade88da3b21da749cc66aaaf Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 17:38:27 +0200 Subject: [PATCH 29/61] fix(xcm-fee-payment-runtime-api): fix weight update issue in tests --- .../xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 043c754a345d..5b58c9e0fbed 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -130,7 +130,7 @@ fn fee_estimation_for_teleport() { }), RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_parts(124414070, 0), + weight: Weight::from_parts(107074070, 0), // Will break if weights get updated. class: DispatchClass::Normal, pays_fee: Pays::Yes, } @@ -280,7 +280,7 @@ fn dry_run_reserve_asset_transfer() { }), RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_parts(124414066, 0), + weight: Weight::from_parts(107074066, 0), // Will break if weights get updated. class: DispatchClass::Normal, pays_fee: Pays::Yes, } From c696674c899b63258af08e72db15862d838137c1 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 17:42:33 +0200 Subject: [PATCH 30/61] fix: fmt --- .../xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 5b58c9e0fbed..03fa71dbfb0d 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -130,7 +130,8 @@ fn fee_estimation_for_teleport() { }), RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_parts(107074070, 0), // Will break if weights get updated. + weight: Weight::from_parts(107074070, 0), /* Will break if weights get + * updated. */ class: DispatchClass::Normal, pays_fee: Pays::Yes, } @@ -280,7 +281,8 @@ fn dry_run_reserve_asset_transfer() { }), RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { dispatch_info: DispatchInfo { - weight: Weight::from_parts(107074066, 0), // Will break if weights get updated. + weight: Weight::from_parts(107074066, 0), /* Will break if weights get + * updated. */ class: DispatchClass::Normal, pays_fee: Pays::Yes, } From 3e7dc0c96b8c5c70834bde0fd52dc2a8ea296eeb Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 17:52:55 +0200 Subject: [PATCH 31/61] fix(xcm-fee-payment-runtime-api): propagate std feature to log --- polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index 4cf2eae76686..278eac85eec7 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -57,6 +57,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "log", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", From f3044003c5f2eabc909245a3267f4082b2608d76 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 17:56:26 +0200 Subject: [PATCH 32/61] fix(xcm-fee-payment-runtime-api): fix feature propagation --- polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index 278eac85eec7..2fea2b9e1245 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -57,7 +57,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", - "log", + "log/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", From f80aaf5cd6217f04361e2359638a12563cf19b5b Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 12 Apr 2024 18:03:32 +0200 Subject: [PATCH 33/61] fix(xcm-fee-payment-runtime-api): format Cargo.toml --- polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml index 2fea2b9e1245..cec76e7327ec 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/Cargo.toml @@ -45,6 +45,7 @@ std = [ "frame-executive/std", "frame-support/std", "frame-system/std", + "log/std", "pallet-assets/std", "pallet-balances/std", "pallet-xcm/std", @@ -57,7 +58,6 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", - "log/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", From 16d3a5488e93d7ff83e628a7ebe6d8d1d418f9ef Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 16 Apr 2024 15:30:53 +0200 Subject: [PATCH 34/61] fix(xcm-builder): remove XcmRecorder from polkadot-test-runtime --- polkadot/runtime/test-runtime/src/xcm_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/test-runtime/src/xcm_config.rs b/polkadot/runtime/test-runtime/src/xcm_config.rs index f40e0606edbd..fc3d0dc42a3b 100644 --- a/polkadot/runtime/test-runtime/src/xcm_config.rs +++ b/polkadot/runtime/test-runtime/src/xcm_config.rs @@ -156,7 +156,7 @@ impl xcm_executor::Config for XcmConfig { type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); - type XcmRecorder = super::Xcm; + type XcmRecorder = (); } impl pallet_xcm::Config for crate::Runtime { From 030255c120eb76c6af359f309edd55a3b98a3d9c Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 17 Apr 2024 17:43:57 +0200 Subject: [PATCH 35/61] feat(xcm-dry-run-api): add test setup with emulator --- Cargo.lock | 6 ++ .../emulated/chains/relays/westend/Cargo.toml | 2 + .../tests/assets/asset-hub-westend/Cargo.toml | 4 ++ .../assets/asset-hub-westend/src/tests/mod.rs | 1 + .../src/tests/xcm_fee_estimation.rs | 63 +++++++++++++++++++ polkadot/runtime/parachains/src/dmp.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 36 ++++++++++- 7 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs diff --git a/Cargo.lock b/Cargo.lock index 3e901fa1c60f..509c87ac9e71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -944,21 +944,25 @@ dependencies = [ "cumulus-pallet-xcmp-queue", "emulated-integration-tests-common", "frame-support", + "frame-system", "pallet-asset-conversion", "pallet-assets", "pallet-balances", "pallet-message-queue", + "pallet-transaction-payment", "pallet-treasury", "pallet-xcm", "parachains-common", "parity-scale-codec", "penpal-runtime", "polkadot-runtime-common", + "sp-core", "sp-runtime", "staging-xcm", "staging-xcm-executor", "westend-runtime", "westend-system-emulated-network", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -22791,8 +22795,10 @@ dependencies = [ "sp-consensus-beefy", "sp-core", "sp-runtime", + "staging-xcm", "westend-runtime", "westend-runtime-constants", + "xcm-fee-payment-runtime-api", ] [[package]] diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml index 12a3ad60e0e0..e74184d84054 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/Cargo.toml @@ -24,6 +24,8 @@ pallet-staking = { path = "../../../../../../../substrate/frame/staking", defaul polkadot-primitives = { path = "../../../../../../../polkadot/primitives", default-features = false } westend-runtime-constants = { path = "../../../../../../../polkadot/runtime/westend/constants", default-features = false } westend-runtime = { path = "../../../../../../../polkadot/runtime/westend" } +xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../../../../../../polkadot/xcm/xcm-fee-payment-runtime-api", default-features = false } # Cumulus parachains-common = { path = "../../../../../common" } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml index 00f4308324a9..5cdcd385d21c 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml @@ -16,18 +16,22 @@ assert_matches = "1.5.0" # Substrate sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } +sp-core = { path = "../../../../../../../substrate/primitives/core", default-features = false } frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../../../../substrate/frame/system", default-features = false } pallet-balances = { path = "../../../../../../../substrate/frame/balances", default-features = false } pallet-assets = { path = "../../../../../../../substrate/frame/assets", default-features = false } pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-conversion", default-features = false } pallet-treasury = { path = "../../../../../../../substrate/frame/treasury", default-features = false } pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue", default-features = false } +pallet-transaction-payment = { path = "../../../../../../../substrate/frame/transaction-payment", default-features = false } # Polkadot polkadot-runtime-common = { path = "../../../../../../../polkadot/runtime/common" } xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../../polkadot/xcm/xcm-executor", default-features = false } pallet-xcm = { path = "../../../../../../../polkadot/xcm/pallet-xcm", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../../../../../../polkadot/xcm/xcm-fee-payment-runtime-api", default-features = false } westend-runtime = { path = "../../../../../../../polkadot/runtime/westend" } # Cumulus 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 e463e21e9e52..5f9079ed3694 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 @@ -21,3 +21,4 @@ mod set_xcm_versions; mod swap; mod teleport; mod treasury; +mod xcm_fee_estimation; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs new file mode 100644 index 000000000000..84a2d3b8e0be --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -0,0 +1,63 @@ +use crate::imports::*; + +use polkadot_runtime_common::BlockHashCount; +use sp_core::Pair; +use sp_runtime::SaturatedConversion; +use xcm_fee_payment_runtime_api::dry_run::runtime_decl_for_xcm_dry_run_api::XcmDryRunApiV1; + +// We are able to dry-run and estimate the fees for a whole XCM journey. +#[test] +fn xcm_dry_run_api_works() { + ::new_ext().execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + let pair = sp_core::sr25519::Pair::from_seed(&WestendSender::get().into()); + let account_id = [0u8; 32]; + let destination: Location = Parachain(1000).into(); + let beneficiary: Location = + AccountId32 { id: WestendReceiver::get().into(), network: Some(NetworkId::Westend) } + .into(); + let assets: Assets = vec![(Here, 100u128).into()].into(); + let call = RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4(destination)), + beneficiary: Box::new(VersionedLocation::V4(beneficiary)), + assets: Box::new(VersionedAssets::V4(assets)), + fee_asset_item: 0, + weight_limit: Unlimited, + }); + use sp_runtime::traits::StaticLookup; + // take the biggest period possible. + let period = + BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; + + let current_block = ::System::block_number() + .saturated_into::() + // The `System::block_number` is initialized with `n+1`, + // so the actual block number is `n`. + .saturating_sub(1); + let tip = 0; + let nonce = 0; + let extra: westend_runtime::SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(sp_runtime::generic::Era::mortal( + period, + current_block, + )), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + ); + let raw_payload = westend_runtime::SignedPayload::new(call, extra).unwrap(); + let signature = raw_payload.using_encoded(|payload| pair.sign(payload)); + let (call, extra, _) = raw_payload.deconstruct(); + let address = ::Lookup::unlookup(account_id.into()); + let extrinsic = + westend_runtime::UncheckedExtrinsic::new_signed(call, address, signature.into(), extra); + let result = Runtime::dry_run_extrinsic(extrinsic); + dbg!(&result); + }); +} diff --git a/polkadot/runtime/parachains/src/dmp.rs b/polkadot/runtime/parachains/src/dmp.rs index 354b16cc3f08..df2f93e19421 100644 --- a/polkadot/runtime/parachains/src/dmp.rs +++ b/polkadot/runtime/parachains/src/dmp.rs @@ -119,7 +119,7 @@ pub mod pallet { /// The downward messages addressed for a certain para. #[pallet::storage] - pub(crate) type DownwardMessageQueues = StorageMap< + pub type DownwardMessageQueues = StorageMap< _, Twox64Concat, ParaId, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index dde40a7864ab..561afcf74745 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -106,7 +106,10 @@ use xcm::{ }; use xcm_builder::PayOverXcm; -use xcm_fee_payment_runtime_api::fees::Error as XcmPaymentApiError; +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -2242,6 +2245,37 @@ sp_api::impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + pallet_xcm::ShouldRecordXcm::::put(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); + // TODO: Make a trait to inspect messages in queues. `InspectMessages` or the like. + let forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)> = runtime_parachains::dmp::DownwardMessageQueues::::iter().map(|(para_id, messages)| ( + VersionedLocation::V4(Parachain(para_id.into()).into()), + VersionedXcm::V4(Xcm::<()>::decode(&mut &messages[0].msg[..]).unwrap()), + )).collect(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_program: VersionedXcm::<()>::V4(local_xcm), + forwarded_messages, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result, XcmDryRunApiError> { + todo!() + } + } + impl pallet_nomination_pools_runtime_api::NominationPoolsApi< Block, AccountId, From 0d2e19cf692ccf1ab562f250437baf70eed0cd86 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Sat, 20 Apr 2024 00:07:39 +0200 Subject: [PATCH 36/61] feat(xcm-fee-estimation): add xcm emulator test for fee estimation --- Cargo.lock | 2 + .../tests/assets/asset-hub-westend/Cargo.toml | 1 + .../src/tests/xcm_fee_estimation.rs | 161 +++++++++++++----- .../assets/asset-hub-westend/Cargo.toml | 3 + .../assets/asset-hub-westend/src/lib.rs | 39 ++++- polkadot/runtime/common/src/xcm_sender.rs | 17 ++ polkadot/runtime/westend/src/lib.rs | 7 +- polkadot/xcm/xcm-builder/src/lib.rs | 2 +- polkadot/xcm/xcm-builder/src/routing.rs | 26 ++- .../src/dry_run.rs | 4 +- 10 files changed, 208 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 509c87ac9e71..be656a157ddc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -957,6 +957,7 @@ dependencies = [ "penpal-runtime", "polkadot-runtime-common", "sp-core", + "sp-keyring", "sp-runtime", "staging-xcm", "staging-xcm-executor", @@ -1041,6 +1042,7 @@ dependencies = [ "substrate-wasm-builder", "testnet-parachains-constants", "westend-runtime-constants", + "xcm-fee-payment-runtime-api", ] [[package]] diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml index 5cdcd385d21c..d6bcb367adbd 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml @@ -16,6 +16,7 @@ assert_matches = "1.5.0" # Substrate sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } +sp-keyring = { path = "../../../../../../../substrate/primitives/keyring", default-features = false } sp-core = { path = "../../../../../../../substrate/primitives/core", default-features = false } frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../../../../../substrate/frame/system", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index 84a2d3b8e0be..2905ffcd8b92 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -1,63 +1,136 @@ use crate::imports::*; use polkadot_runtime_common::BlockHashCount; -use sp_core::Pair; -use sp_runtime::SaturatedConversion; +use sp_keyring::AccountKeyring::Alice; +use sp_runtime::{MultiSignature, SaturatedConversion}; use xcm_fee_payment_runtime_api::dry_run::runtime_decl_for_xcm_dry_run_api::XcmDryRunApiV1; +use xcm_fee_payment_runtime_api::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1; -// We are able to dry-run and estimate the fees for a whole XCM journey. +type RelayToAssetHubTest = Test; + +/// We are able to dry-run and estimate the fees for a whole XCM journey. +/// Scenario: Alice on Westend relay chain wants to teleport WND to Asset Hub. +/// We want to know the fees using the `XcmDryRunApi` and `XcmPaymentApi`. #[test] fn xcm_dry_run_api_works() { + let destination: Location = Parachain(1000).into(); // Asset Hub. + let beneficiary_id = AssetHubWestendReceiver::get(); + let beneficiary: Location = + AccountId32 { id: beneficiary_id.clone().into(), network: None } // Test doesn't allow specifying a network here. + .into(); // Beneficiary in Asset Hub. + let teleport_amount = 1_000_000_000_000; // One WND (12 decimals). + let assets: Assets = vec![(Here, teleport_amount).into()].into(); + + // We get them from the Westend closure. + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); ::new_ext().execute_with(|| { type Runtime = ::Runtime; type RuntimeCall = ::RuntimeCall; - let pair = sp_core::sr25519::Pair::from_seed(&WestendSender::get().into()); - let account_id = [0u8; 32]; - let destination: Location = Parachain(1000).into(); - let beneficiary: Location = - AccountId32 { id: WestendReceiver::get().into(), network: Some(NetworkId::Westend) } - .into(); - let assets: Assets = vec![(Here, 100u128).into()].into(); let call = RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { - dest: Box::new(VersionedLocation::V4(destination)), + dest: Box::new(VersionedLocation::V4(destination.clone())), beneficiary: Box::new(VersionedLocation::V4(beneficiary)), assets: Box::new(VersionedAssets::V4(assets)), fee_asset_item: 0, weight_limit: Unlimited, }); - use sp_runtime::traits::StaticLookup; - // take the biggest period possible. - let period = - BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; - - let current_block = ::System::block_number() - .saturated_into::() - // The `System::block_number` is initialized with `n+1`, - // so the actual block number is `n`. - .saturating_sub(1); - let tip = 0; - let nonce = 0; - let extra: westend_runtime::SignedExtra = ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckMortality::::from(sp_runtime::generic::Era::mortal( - period, - current_block, - )), - frame_system::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ); - let raw_payload = westend_runtime::SignedPayload::new(call, extra).unwrap(); - let signature = raw_payload.using_encoded(|payload| pair.sign(payload)); - let (call, extra, _) = raw_payload.deconstruct(); - let address = ::Lookup::unlookup(account_id.into()); - let extrinsic = - westend_runtime::UncheckedExtrinsic::new_signed(call, address, signature.into(), extra); - let result = Runtime::dry_run_extrinsic(extrinsic); - dbg!(&result); + let sender = Alice; // Is the same as `WestendSender`. + let extrinsic = construct_extrinsic(sender, call); + let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); + let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; + remote_message = messages_to_query[0].clone(); + let delivery_fees = Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()).unwrap(); + delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + assert_eq!(delivery_fees_amount, 39_700_000_000); + }); + + // This is set in the AssetHubWestend closure. + let mut remote_execution_fees = 0; + ::new_ext().execute_with(|| { + type Runtime = ::Runtime; + + let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); + remote_execution_fees = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())).unwrap(); }); + + let test_args = TestContext { + sender: WestendSender::get(), // Alice. + receiver: AssetHubWestendReceiver::get(), // Bob in Asset Hub. + args: TestArgs::new_relay(destination, beneficiary_id, teleport_amount), + }; + let mut test = RelayToAssetHubTest::new(test_args); + + let sender_balance_before = test.sender.balance; + let receiver_balance_before = test.receiver.balance; + assert_eq!(sender_balance_before, 1_000_000_000_000_000_000); + assert_eq!(receiver_balance_before, 4_096_000_000_000); + + test.set_dispatchable::(transfer_assets); + test.assert(); + + let sender_balance_after = test.sender.balance; + let receiver_balance_after = test.receiver.balance; + + // We now know the exact fees. + assert_eq!(sender_balance_after, sender_balance_before - delivery_fees_amount - teleport_amount); + assert_eq!(receiver_balance_after, receiver_balance_before + teleport_amount - remote_execution_fees); +} + +fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 { + let latest_assets: Assets = assets.try_into().unwrap(); + let Fungible(amount) = latest_assets.inner()[0].fun else { + unreachable!("asset is fungible"); + }; + amount +} + +fn transfer_assets(test: RelayToAssetHubTest) -> DispatchResult { + ::XcmPallet::transfer_assets( + test.signed_origin, + bx!(test.args.dest.into()), + bx!(test.args.beneficiary.into()), + bx!(test.args.assets.into()), + test.args.fee_asset_item, + test.args.weight_limit, + ) +} + +// TODO: Could make it generic over the runtime? +/// Constructs an extrinsic. +fn construct_extrinsic( + sender: sp_keyring::AccountKeyring, + call: westend_runtime::RuntimeCall, +) -> westend_runtime::UncheckedExtrinsic { + type Runtime = ::Runtime; + + let account_id = ::AccountId::from(sender.public()); + // take the biggest period possible. + let period = + BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; + let current_block = ::System::block_number() + .saturated_into::() + // The `System::block_number` is initialized with `n+1`, + // so the actual block number is `n`. + .saturating_sub(1); + let tip = 0; + let extra: westend_runtime::SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(sp_runtime::generic::Era::mortal( + period, + current_block, + )), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account_id).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + ); + let raw_payload = westend_runtime::SignedPayload::new(call, extra).unwrap(); + let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); + let (call, extra, _) = raw_payload.deconstruct(); + westend_runtime::UncheckedExtrinsic::new_signed(call, account_id.into(), MultiSignature::Sr25519(signature), extra) } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index e25554ec0a5f..b3c5e4438e89 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -65,6 +65,7 @@ westend-runtime-constants = { path = "../../../../../polkadot/runtime/westend/co xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../../../../polkadot/xcm/xcm-fee-payment-runtime-api", default-features = false } # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } @@ -132,6 +133,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", @@ -235,6 +237,7 @@ std = [ "westend-runtime-constants/std", "xcm-builder/std", "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", "xcm/std", ] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index cb2f11637187..7afbed1ca2fc 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -45,7 +45,7 @@ use frame_support::{ AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Equals, InstanceFilter, TransformOrigin, }, - weights::{ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, }; use frame_system::{ @@ -84,6 +84,9 @@ pub use sp_runtime::BuildStorage; use assets_common::{foreign_creators::ForeignCreators, matching::FromSiblingParachain}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; +use xcm::IntoVersion; +use xcm::prelude::{VersionedLocation, VersionedXcm, VersionedAssetId, VersionedAssets}; + // We exclude `Assets` since it's the name of a pallet use xcm::latest::prelude::AssetId; @@ -93,6 +96,8 @@ use xcm::latest::prelude::{ NonFungible, Parent, ParentThen, Response, XCM_VERSION, }; +use xcm_fee_payment_runtime_api::fees::Error as XcmPaymentApiError; + use crate::xcm_config::ForeignCreatorsSovereignAccountOf; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; @@ -1280,6 +1285,38 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + if !matches!(xcm_version, 3 | 4) { + return Err(XcmPaymentApiError::UnhandledXcmVersion); + } + // TODO: For now only WND. + Ok([VersionedAssetId::V4(xcm_config::WestendLocation::get().into())] + .into_iter() + .filter_map(|asset| asset.into_version(xcm_version).ok()) + .collect()) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(xcm_config::WestendLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi for Runtime { diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index 0cbc2e603c8e..951bbf4e2f4c 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -16,6 +16,7 @@ //! XCM sender for relay chain. +use parity_scale_codec::Decode; use frame_support::traits::Get; use frame_system::pallet_prelude::BlockNumberFor; use parity_scale_codec::Encode; @@ -27,6 +28,7 @@ use runtime_parachains::{ use sp_runtime::FixedPointNumber; use sp_std::{marker::PhantomData, prelude::*}; use xcm::prelude::*; +use xcm_builder::InspectMessageQueues; use SendError::*; /// Simple value-bearing trait for determining/expressing the assets required to be paid for a @@ -136,6 +138,21 @@ where } } +impl InspectMessageQueues for ChildParachainRouter { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + dmp::DownwardMessageQueues::::iter().map(|(para_id, messages)| { + let decoded_messages: Vec> = messages + .iter() + .map(|downward_message| VersionedXcm::<()>::decode(&mut &downward_message.msg[..]).unwrap()) + .collect(); + ( + VersionedLocation::V4(Parachain(para_id.into()).into()), + decoded_messages, + ) + }).collect() + } +} + /// Implementation of `xcm_builder::EnsureDelivery` which helps to ensure delivery to the /// `ParaId` parachain (sibling or child). Deposits existential deposit for origin (if needed). /// Deposits estimated fee to the origin account (if needed). diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 561afcf74745..a381e8cdf8ce 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2247,6 +2247,7 @@ sp_api::impl_runtime_apis! { impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; pallet_xcm::ShouldRecordXcm::::put(true); let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { log::error!( @@ -2257,11 +2258,7 @@ sp_api::impl_runtime_apis! { XcmDryRunApiError::InvalidExtrinsic })?; let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); - // TODO: Make a trait to inspect messages in queues. `InspectMessages` or the like. - let forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)> = runtime_parachains::dmp::DownwardMessageQueues::::iter().map(|(para_id, messages)| ( - VersionedLocation::V4(Parachain(para_id.into()).into()), - VersionedXcm::V4(Xcm::<()>::decode(&mut &messages[0].msg[..]).unwrap()), - )).collect(); + let forwarded_messages = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { local_program: VersionedXcm::<()>::V4(local_xcm), diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index c3400cc72b48..7c7db816e525 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -119,7 +119,7 @@ mod process_xcm_message; pub use process_xcm_message::ProcessXcmMessage; mod routing; -pub use routing::{EnsureDelivery, WithTopicSource, WithUniqueTopic}; +pub use routing::{EnsureDelivery, WithTopicSource, WithUniqueTopic, InspectMessageQueues}; mod transactional; pub use transactional::FrameTransactionalProcessor; diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs index 529ef80c15ff..9fae01d0a0ac 100644 --- a/polkadot/xcm/xcm-builder/src/routing.rs +++ b/polkadot/xcm/xcm-builder/src/routing.rs @@ -18,7 +18,7 @@ use frame_system::unique; use parity_scale_codec::Encode; -use sp_std::{marker::PhantomData, result::Result}; +use sp_std::{marker::PhantomData, result::Result, vec::Vec}; use xcm::prelude::*; use xcm_executor::{traits::FeeReason, FeesMode}; @@ -60,6 +60,11 @@ impl SendXcm for WithUniqueTopic { Ok(unique_id) } } +impl InspectMessageQueues for WithUniqueTopic { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + Inner::get_messages() + } +} pub trait SourceTopic { fn source_topic(entropy: impl Encode) -> XcmHash; @@ -139,3 +144,22 @@ impl EnsureDelivery for Tuple { (None, None) } } + +/// Inspects messages in queues. +pub trait InspectMessageQueues { + /// Get queued messages and their destinations. + fn get_messages() -> Vec<(VersionedLocation, Vec>)>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl InspectMessageQueues for Tuple { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + let mut messages = Vec::new(); + + for_tuples!( #( + messages.append(&mut Tuple::get_messages()); + )* ); + + messages + } +} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index 9c4a43b0aa74..1125dbff4642 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -34,7 +34,7 @@ pub struct ExtrinsicDryRunEffects { /// The local XCM program that was attempted to be executed, if any. pub local_program: VersionedXcm<()>, /// The list of XCMs that were queued for sending. - pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, + pub forwarded_messages: Vec<(VersionedLocation, Vec>)>, } /// Effects of dry-running an XCM program. @@ -45,7 +45,7 @@ pub struct XcmDryRunEffects { /// List of events fired by the XCM program execution. pub emitted_events: Vec, /// List of queued messages for sending. - pub forwarded_messages: Vec<(VersionedLocation, VersionedXcm<()>)>, + pub forwarded_messages: Vec<(VersionedLocation, Vec>)>, } sp_api::decl_runtime_apis! { From 9fcf4b267baf480ec55876d9b7febe60a142b63b Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 25 Apr 2024 17:55:16 +0200 Subject: [PATCH 37/61] fix(pallet-xcm): RecordXcm test was using old function --- polkadot/xcm/pallet-xcm/src/tests/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index b79ebda01725..e0022a787d7e 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -1264,18 +1264,18 @@ fn record_xcm_works() { assert_eq!(RecordedXcm::::get(), None); // By default the message won't be recorded. - assert_ok!(XcmPallet::execute_blob( + assert_ok!(XcmPallet::execute( RuntimeOrigin::signed(ALICE), - VersionedXcm::from(message.clone()).encode().try_into().unwrap(), + Box::new(VersionedXcm::from(message.clone())), BaseXcmWeight::get() * 3, )); assert_eq!(RecordedXcm::::get(), None); // We explicitly set the record flag to true so we record the XCM. ShouldRecordXcm::::put(true); - assert_ok!(XcmPallet::execute_blob( + assert_ok!(XcmPallet::execute( RuntimeOrigin::signed(ALICE), - VersionedXcm::from(message.clone()).encode().try_into().unwrap(), + Box::new(VersionedXcm::from(message.clone())), BaseXcmWeight::get() * 3, )); assert_eq!(RecordedXcm::::get(), Some(message.into())); From 5aeae3ab7951a952ffe405f96f5bd1697b2f5d11 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 25 Apr 2024 17:55:45 +0200 Subject: [PATCH 38/61] fix: fmt --- .../src/tests/xcm_fee_estimation.rs | 40 +++++++++++++------ .../assets/asset-hub-westend/src/lib.rs | 6 ++- polkadot/runtime/common/src/xcm_sender.rs | 24 +++++------ polkadot/xcm/pallet-xcm/src/tests/mod.rs | 6 +-- polkadot/xcm/xcm-builder/src/lib.rs | 4 +- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index 2905ffcd8b92..acecd5c67c86 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -3,8 +3,10 @@ use crate::imports::*; use polkadot_runtime_common::BlockHashCount; use sp_keyring::AccountKeyring::Alice; use sp_runtime::{MultiSignature, SaturatedConversion}; -use xcm_fee_payment_runtime_api::dry_run::runtime_decl_for_xcm_dry_run_api::XcmDryRunApiV1; -use xcm_fee_payment_runtime_api::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_xcm_dry_run_api::XcmDryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; type RelayToAssetHubTest = Test; @@ -15,9 +17,8 @@ type RelayToAssetHubTest = Test; fn xcm_dry_run_api_works() { let destination: Location = Parachain(1000).into(); // Asset Hub. let beneficiary_id = AssetHubWestendReceiver::get(); - let beneficiary: Location = - AccountId32 { id: beneficiary_id.clone().into(), network: None } // Test doesn't allow specifying a network here. - .into(); // Beneficiary in Asset Hub. + let beneficiary: Location = AccountId32 { id: beneficiary_id.clone().into(), network: None } // Test doesn't allow specifying a network here. + .into(); // Beneficiary in Asset Hub. let teleport_amount = 1_000_000_000_000; // One WND (12 decimals). let assets: Assets = vec![(Here, teleport_amount).into()].into(); @@ -36,11 +37,13 @@ fn xcm_dry_run_api_works() { weight_limit: Unlimited, }); let sender = Alice; // Is the same as `WestendSender`. - let extrinsic = construct_extrinsic(sender, call); + let extrinsic = construct_extrinsic(sender, call); let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; remote_message = messages_to_query[0].clone(); - let delivery_fees = Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()).unwrap(); + let delivery_fees = + Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) + .unwrap(); delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); assert_eq!(delivery_fees_amount, 39_700_000_000); }); @@ -51,11 +54,13 @@ fn xcm_dry_run_api_works() { type Runtime = ::Runtime; let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); - remote_execution_fees = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())).unwrap(); + remote_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) + .unwrap(); }); let test_args = TestContext { - sender: WestendSender::get(), // Alice. + sender: WestendSender::get(), // Alice. receiver: AssetHubWestendReceiver::get(), // Bob in Asset Hub. args: TestArgs::new_relay(destination, beneficiary_id, teleport_amount), }; @@ -73,8 +78,14 @@ fn xcm_dry_run_api_works() { let receiver_balance_after = test.receiver.balance; // We now know the exact fees. - assert_eq!(sender_balance_after, sender_balance_before - delivery_fees_amount - teleport_amount); - assert_eq!(receiver_balance_after, receiver_balance_before + teleport_amount - remote_execution_fees); + assert_eq!( + sender_balance_after, + sender_balance_before - delivery_fees_amount - teleport_amount + ); + assert_eq!( + receiver_balance_after, + receiver_balance_before + teleport_amount - remote_execution_fees + ); } fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 { @@ -132,5 +143,10 @@ fn construct_extrinsic( let raw_payload = westend_runtime::SignedPayload::new(call, extra).unwrap(); let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); let (call, extra, _) = raw_payload.deconstruct(); - westend_runtime::UncheckedExtrinsic::new_signed(call, account_id.into(), MultiSignature::Sr25519(signature), extra) + westend_runtime::UncheckedExtrinsic::new_signed( + call, + account_id.into(), + MultiSignature::Sr25519(signature), + extra, + ) } 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 ccd9bea7b2a5..ece7755ca278 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -84,8 +84,10 @@ pub use sp_runtime::BuildStorage; use assets_common::{foreign_creators::ForeignCreators, matching::FromSiblingParachain}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; -use xcm::IntoVersion; -use xcm::prelude::{VersionedLocation, VersionedXcm, VersionedAssetId, VersionedAssets}; +use xcm::{ + prelude::{VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}, + IntoVersion, +}; // We exclude `Assets` since it's the name of a pallet use xcm::latest::prelude::AssetId; diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index 020ed61dd035..15d59adf78a5 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -16,10 +16,9 @@ //! XCM sender for relay chain. -use parity_scale_codec::Decode; use frame_support::traits::Get; use frame_system::pallet_prelude::BlockNumberFor; -use parity_scale_codec::Encode; +use parity_scale_codec::{Decode, Encode}; use primitives::Id as ParaId; use runtime_parachains::{ configuration::{self, HostConfiguration}, @@ -142,16 +141,17 @@ where impl InspectMessageQueues for ChildParachainRouter { fn get_messages() -> Vec<(VersionedLocation, Vec>)> { - dmp::DownwardMessageQueues::::iter().map(|(para_id, messages)| { - let decoded_messages: Vec> = messages - .iter() - .map(|downward_message| VersionedXcm::<()>::decode(&mut &downward_message.msg[..]).unwrap()) - .collect(); - ( - VersionedLocation::V4(Parachain(para_id.into()).into()), - decoded_messages, - ) - }).collect() + dmp::DownwardMessageQueues::::iter() + .map(|(para_id, messages)| { + let decoded_messages: Vec> = messages + .iter() + .map(|downward_message| { + VersionedXcm::<()>::decode(&mut &downward_message.msg[..]).unwrap() + }) + .collect(); + (VersionedLocation::V4(Parachain(para_id.into()).into()), decoded_messages) + }) + .collect() } } diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index e0022a787d7e..c4bf20cb4d7a 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -20,9 +20,9 @@ pub(crate) mod assets_transfer; use crate::{ mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error, - ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus, RecordedXcm, ShouldRecordXcm, - VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, - WeightInfo, + ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus, + RecordedXcm, ShouldRecordXcm, VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, + VersionNotifyTargets, WeightInfo, }; use frame_support::{ assert_err_ignore_postinfo, assert_noop, assert_ok, diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index d84682fb0346..cc06c298a418 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -120,7 +120,9 @@ mod process_xcm_message; pub use process_xcm_message::ProcessXcmMessage; mod routing; -pub use routing::{EnsureDecodableXcm, EnsureDelivery, WithTopicSource, WithUniqueTopic, InspectMessageQueues}; +pub use routing::{ + EnsureDecodableXcm, EnsureDelivery, InspectMessageQueues, WithTopicSource, WithUniqueTopic, +}; mod transactional; pub use transactional::FrameTransactionalProcessor; From a90c091ca86a9b3c80efa3b85a361894f458e7e4 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 25 Apr 2024 18:29:20 +0200 Subject: [PATCH 39/61] feat: finish implementing XcmDryRunApi for westend and asset hub westend --- .../src/tests/xcm_fee_estimation.rs | 18 +++++- .../assets/asset-hub-westend/src/lib.rs | 63 ++++++++++++++++++- polkadot/runtime/westend/src/lib.rs | 32 +++++++++- 3 files changed, 110 insertions(+), 3 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index acecd5c67c86..2f8fdbfebe7f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -1,3 +1,20 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests to ensure correct XCM fee estimation between Asset Hub Westend and the Westend relay chain. + use crate::imports::*; use polkadot_runtime_common::BlockHashCount; @@ -107,7 +124,6 @@ fn transfer_assets(test: RelayToAssetHubTest) -> DispatchResult { ) } -// TODO: Could make it generic over the runtime? /// Constructs an extrinsic. fn construct_extrinsic( sender: sp_keyring::AccountKeyring, 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 ece7755ca278..db0ddf9ca799 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -98,7 +98,10 @@ use xcm::latest::prelude::{ NonFungible, Parent, ParentThen, Response, XCM_VERSION, }; -use xcm_fee_payment_runtime_api::fees::Error as XcmPaymentApiError; +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; use crate::xcm_config::ForeignCreatorsSovereignAccountOf; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; @@ -1345,6 +1348,64 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + pallet_xcm::ShouldRecordXcm::::put(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); + let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_program: VersionedXcm::<()>::V4(local_xcm), + forwarded_messages, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let xcm: Xcm = xcm.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let result = XcmExecutor::::prepare_and_execute( + origin_location, + xcm, + &mut hash, + max_weight, + Weight::zero(), + ); + let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_messages, + emitted_events: events, + execution_result: result, + }) + } + } + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi for Runtime { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index f39af38bb501..6e9d0e73982f 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2252,7 +2252,37 @@ sp_api::impl_runtime_apis! { } fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result, XcmDryRunApiError> { - todo!() + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let xcm: Xcm = xcm.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let result = XcmExecutor::::prepare_and_execute( + origin_location, + xcm, + &mut hash, + max_weight, + Weight::zero(), + ); + let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_messages, + emitted_events: events, + execution_result: result, + }) } } From 6a3529423ef3d64a774c19bcd22e3b3c62df5927 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 26 Apr 2024 10:53:52 +0200 Subject: [PATCH 40/61] fix(xcm-dry-run-api): fix multiple forwarded messages instead of one --- .../tests/fee_estimation.rs | 11 ++++++----- .../xcm/xcm-fee-payment-runtime-api/tests/mock.rs | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 03fa71dbfb0d..e7884c9ad152 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -90,7 +90,7 @@ fn fee_estimation_for_teleport() { dry_run_effects.forwarded_messages, vec![( VersionedLocation::V4(send_destination.clone()), - VersionedXcm::V4(send_message.clone()), + vec![VersionedXcm::V4(send_message.clone())], ),], ); @@ -163,7 +163,8 @@ fn fee_estimation_for_teleport() { let mut forwarded_messages_iter = dry_run_effects.forwarded_messages.into_iter(); - let (destination, remote_message) = forwarded_messages_iter.next().unwrap(); + let (destination, remote_messages) = forwarded_messages_iter.next().unwrap(); + let remote_message = &remote_messages[0]; let delivery_fees = runtime_api .query_delivery_fees(H256::zero(), destination.clone(), remote_message.clone()) @@ -253,7 +254,7 @@ fn dry_run_reserve_asset_transfer() { dry_run_effects.forwarded_messages, vec![( VersionedLocation::V4(send_destination.clone()), - VersionedXcm::V4(send_message.clone()), + vec![VersionedXcm::V4(send_message.clone())], ),], ); @@ -342,7 +343,7 @@ fn dry_run_xcm() { dry_run_effects.forwarded_messages, vec![( VersionedLocation::V4((Parent, Parachain(2100)).into()), - VersionedXcm::V4( + vec![VersionedXcm::V4( Xcm::<()>::builder_unsafe() .reserve_asset_deposited(( (Parent, Parachain(2000)), @@ -352,7 +353,7 @@ fn dry_run_xcm() { .buy_execution((Here, 1u128), Unlimited) .deposit_asset(AllCounted(1), [0u8; 32]) .build() - ), + )], ),] ); diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 0fd57a20aa98..a75f1fb63dbe 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -471,7 +471,7 @@ sp_api::mock_impl_runtime_apis! { .into_iter() .map(|(location, message)| ( VersionedLocation::V4(location), - VersionedXcm::V4(message) + vec![VersionedXcm::V4(message)], )).collect(); let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { @@ -511,7 +511,7 @@ sp_api::mock_impl_runtime_apis! { .into_iter() .map(|(location, message)| ( VersionedLocation::V4(location), - VersionedXcm::V4(message), + vec![VersionedXcm::V4(message)], )).collect(); let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); Ok(XcmDryRunEffects { From 51b279c844e2d642b76193826179b2e7e36850f6 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Mon, 29 Apr 2024 16:50:51 +0200 Subject: [PATCH 41/61] feat: add multi-hop example to xcm estimation --- Cargo.lock | 3 + .../modules/xcm-bridge-hub-router/src/lib.rs | 26 +- cumulus/pallets/parachain-system/Cargo.toml | 3 + cumulus/pallets/parachain-system/src/lib.rs | 17 +- cumulus/pallets/xcmp-queue/Cargo.toml | 5 +- cumulus/pallets/xcmp-queue/src/lib.rs | 29 +- cumulus/pallets/xcmp-queue/src/tests.rs | 40 +++ .../tests/assets/asset-hub-westend/Cargo.toml | 1 + .../src/tests/xcm_fee_estimation.rs | 265 +++++++++++++++++- .../assets/asset-hub-westend/src/lib.rs | 17 +- .../runtimes/testing/penpal/Cargo.toml | 3 + .../runtimes/testing/penpal/src/lib.rs | 102 +++++++ cumulus/primitives/utility/src/lib.rs | 10 +- polkadot/runtime/common/src/xcm_sender.rs | 4 +- polkadot/runtime/westend/src/lib.rs | 5 +- polkadot/xcm/xcm-builder/src/routing.rs | 1 + .../xcm-fee-payment-runtime-api/tests/mock.rs | 2 +- 17 files changed, 503 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a14d85e83314..e68b26e837b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -952,6 +952,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-asset-conversion", + "pallet-asset-tx-payment", "pallet-assets", "pallet-balances", "pallet-message-queue", @@ -4055,6 +4056,7 @@ dependencies = [ "sp-trie", "sp-version", "staging-xcm", + "staging-xcm-builder", "trie-db", "trie-standardmap", ] @@ -12324,6 +12326,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "xcm-fee-payment-runtime-api", ] [[package]] diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index ece72ac8494b..d1ad82a4a66b 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -37,8 +37,9 @@ use codec::Encode; use frame_support::traits::Get; use sp_core::H256; use sp_runtime::{FixedPointNumber, FixedU128, Saturating}; +use sp_std::vec::Vec; use xcm::prelude::*; -use xcm_builder::{ExporterFor, SovereignPaidRemoteExporter}; +use xcm_builder::{ExporterFor, SovereignPaidRemoteExporter, InspectMessageQueues}; pub use pallet::*; pub use weights::WeightInfo; @@ -396,6 +397,13 @@ impl, I: 'static> SendXcm for Pallet { } } +impl, I: 'static> InspectMessageQueues for Pallet { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + // TODO: Get messages queued for bridging. + Vec::new() + } +} + #[cfg(test)] mod tests { use super::*; @@ -635,4 +643,20 @@ mod tests { ); }); } + + #[test] + fn get_messages_works() { + run_test(|| { + assert_ok!( + send_xcm::(( + Parent, + Parent, + GlobalConsensus(BridgedNetworkId::get()), + Parachain(1000) + ).into(), vec![ClearOrigin].into()) + ); + // TODO: Get messages queued for bridging. + assert_eq!(XcmBridgeHubRouter::get_messages(), vec![]); + }); + } } diff --git a/cumulus/pallets/parachain-system/Cargo.toml b/cumulus/pallets/parachain-system/Cargo.toml index cc2e8943caad..fef697d22757 100644 --- a/cumulus/pallets/parachain-system/Cargo.toml +++ b/cumulus/pallets/parachain-system/Cargo.toml @@ -38,6 +38,7 @@ polkadot-parachain-primitives = { path = "../../../polkadot/parachain", default- polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", default-features = false } polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false, optional = true } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } # Cumulus cumulus-pallet-parachain-system-proc-macro = { path = "proc-macro", default-features = false } @@ -96,6 +97,7 @@ std = [ "sp-trie/std", "trie-db/std", "xcm/std", + "xcm-builder/std", ] runtime-benchmarks = [ @@ -109,6 +111,7 @@ runtime-benchmarks = [ "polkadot-runtime-common/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", ] try-runtime = [ diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index 54a1def59600..6363c2f79a7f 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -55,7 +55,8 @@ use sp_runtime::{ BoundedSlice, FixedU128, RuntimeDebug, Saturating, }; use sp_std::{cmp, collections::btree_map::BTreeMap, prelude::*}; -use xcm::latest::XcmHash; +use xcm::{VersionedLocation, VersionedXcm, latest::XcmHash}; +use xcm_builder::InspectMessageQueues; mod benchmarking; pub mod migration; @@ -1608,6 +1609,20 @@ impl UpwardMessageSender for Pallet { } } +impl InspectMessageQueues for Pallet { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + use xcm::prelude::*; + + let messages: Vec> = PendingUpwardMessages::::get().iter() + .map(|encoded_message| { + VersionedXcm::<()>::decode(&mut &encoded_message[..]).unwrap() + }) + .collect(); + + vec![(VersionedLocation::V4(Parent.into()), messages)] + } +} + #[cfg(feature = "runtime-benchmarks")] impl polkadot_runtime_common::xcm_sender::EnsureForParachain for Pallet { fn ensure(para_id: ParaId) { diff --git a/cumulus/pallets/xcmp-queue/Cargo.toml b/cumulus/pallets/xcmp-queue/Cargo.toml index ab196c6d3ec6..1b4b37c909ba 100644 --- a/cumulus/pallets/xcmp-queue/Cargo.toml +++ b/cumulus/pallets/xcmp-queue/Cargo.toml @@ -28,6 +28,7 @@ polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-f polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", default-features = false } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } # Cumulus cumulus-primitives-core = { path = "../../primitives/core", default-features = false } @@ -46,9 +47,6 @@ sp-core = { path = "../../../substrate/primitives/core" } pallet-balances = { path = "../../../substrate/frame/balances" } frame-support = { path = "../../../substrate/frame/support", features = ["experimental"] } -# Polkadot -xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder" } - # Cumulus cumulus-pallet-parachain-system = { path = "../parachain-system", features = ["parameterized-consensus-hook"] } @@ -73,6 +71,7 @@ std = [ "sp-std/std", "xcm-executor/std", "xcm/std", + "xcm-builder/std", ] runtime-benchmarks = [ diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 7de2fd809421..363ee663af6f 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -70,8 +70,9 @@ use scale_info::TypeInfo; use sp_core::MAX_POSSIBLE_ALLOCATION; use sp_runtime::{FixedU128, RuntimeDebug, Saturating}; use sp_std::prelude::*; -use xcm::{latest::prelude::*, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; +use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; use xcm_executor::traits::ConvertOrigin; +use xcm_builder::InspectMessageQueues; pub use pallet::*; @@ -947,6 +948,32 @@ impl SendXcm for Pallet { } } +impl InspectMessageQueues for Pallet { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + use xcm::prelude::*; + + OutboundXcmpMessages::::iter() + .map(|(para_id, _, messages)| { + let mut data = &messages[..]; + let decoded_format = XcmpMessageFormat::decode_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut data, + ).unwrap(); + if decoded_format != XcmpMessageFormat::ConcatenatedVersionedXcm { panic!("Unexpected format.") } + let mut decoded_messages = Vec::new(); + while !data.is_empty() { + let decoded_message = VersionedXcm::<()>::decode_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut data, + ).unwrap(); + decoded_messages.push(decoded_message); + } + (VersionedLocation::V4((Parent, Parachain(para_id.into())).into()), decoded_messages) + }) + .collect() + } +} + impl FeeTracker for Pallet { type Id = ParaId; diff --git a/cumulus/pallets/xcmp-queue/src/tests.rs b/cumulus/pallets/xcmp-queue/src/tests.rs index 0b41095828f2..f48e9eec3ac0 100644 --- a/cumulus/pallets/xcmp-queue/src/tests.rs +++ b/cumulus/pallets/xcmp-queue/src/tests.rs @@ -844,3 +844,43 @@ fn verify_fee_factor_increase_and_decrease() { assert!(DeliveryFeeFactor::::get(sibling_para_id) < FixedU128::from_float(1.63)); }); } + +#[test] +fn get_messages_works() { + new_test_ext().execute_with(|| { + use xcm_builder::InspectMessageQueues; + let sibling_para_id = ParaId::from(2001); + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(sibling_para_id); + let destination: Location = (Parent, Parachain(sibling_para_id.into())).into(); + let other_sibling_para_id = ParaId::from(2002); + let other_destination: Location = (Parent, Parachain(other_sibling_para_id.into())).into(); + let message = Xcm(vec![ClearOrigin]); + assert_ok!(send_xcm::(destination.clone(), message.clone())); + assert_ok!(send_xcm::(destination.clone(), message.clone())); + assert_ok!(send_xcm::(destination.clone(), message.clone())); + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(other_sibling_para_id); + assert_ok!(send_xcm::(other_destination.clone(), message.clone())); + assert_ok!(send_xcm::(other_destination.clone(), message)); + let queued_messages = XcmpQueue::get_messages(); + assert_eq!( + queued_messages, + vec![ + ( + VersionedLocation::V4(other_destination), + vec![ + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + ], + ), + ( + VersionedLocation::V4(destination), + vec![ + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + VersionedXcm::V4(Xcm(vec![ClearOrigin])), + ], + ), + ], + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml index d6bcb367adbd..0a2b0f6d45ee 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml @@ -26,6 +26,7 @@ pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-c pallet-treasury = { path = "../../../../../../../substrate/frame/treasury", default-features = false } pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue", default-features = false } pallet-transaction-payment = { path = "../../../../../../../substrate/frame/transaction-payment", default-features = false } +pallet-asset-tx-payment = { path = "../../../../../../../substrate/frame/transaction-payment/asset-tx-payment", default-features = false } # Polkadot polkadot-runtime-common = { path = "../../../../../../../polkadot/runtime/common" } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index 2f8fdbfebe7f..ff124b0c356b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -19,7 +19,7 @@ use crate::imports::*; use polkadot_runtime_common::BlockHashCount; use sp_keyring::AccountKeyring::Alice; -use sp_runtime::{MultiSignature, SaturatedConversion}; +use sp_runtime::{MultiSignature, SaturatedConversion, generic}; use xcm_fee_payment_runtime_api::{ dry_run::runtime_decl_for_xcm_dry_run_api::XcmDryRunApiV1, fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, @@ -27,11 +27,11 @@ use xcm_fee_payment_runtime_api::{ type RelayToAssetHubTest = Test; -/// We are able to dry-run and estimate the fees for a whole XCM journey. +/// We are able to dry-run and estimate the fees for a teleport between relay and system para. /// Scenario: Alice on Westend relay chain wants to teleport WND to Asset Hub. /// We want to know the fees using the `XcmDryRunApi` and `XcmPaymentApi`. #[test] -fn xcm_dry_run_api_works() { +fn teleport_relay_system_para_works() { let destination: Location = Parachain(1000).into(); // Asset Hub. let beneficiary_id = AssetHubWestendReceiver::get(); let beneficiary: Location = AccountId32 { id: beneficiary_id.clone().into(), network: None } // Test doesn't allow specifying a network here. @@ -42,7 +42,7 @@ fn xcm_dry_run_api_works() { // We get them from the Westend closure. let mut delivery_fees_amount = 0; let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); - ::new_ext().execute_with(|| { + ::execute_with(|| { type Runtime = ::Runtime; type RuntimeCall = ::RuntimeCall; @@ -54,7 +54,8 @@ fn xcm_dry_run_api_works() { weight_limit: Unlimited, }); let sender = Alice; // Is the same as `WestendSender`. - let extrinsic = construct_extrinsic(sender, call); + let extra = construct_westend_extra(sender); + let extrinsic = construct_extrinsic_westend(sender, call, extra); let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; remote_message = messages_to_query[0].clone(); @@ -67,7 +68,7 @@ fn xcm_dry_run_api_works() { // This is set in the AssetHubWestend closure. let mut remote_execution_fees = 0; - ::new_ext().execute_with(|| { + ::execute_with(|| { type Runtime = ::Runtime; let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); @@ -105,6 +106,180 @@ fn xcm_dry_run_api_works() { ); } +/// We are able to dry-run and estimate the fees for a multi-hop XCM journey. +/// Scenario: Alice on PenpalA has some WND and wants to send them to PenpalB. +/// We want to know the fees using the `XcmDryRunApi` and `XcmPaymentApi`. +#[test] +fn multi_hop_works() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let amount_to_send = 1_000_000_000_000; // One WND (12 decimals). + let asset_owner = PenpalAssetOwner::get(); + let assets: Assets = (Parent, amount_to_send).into(); + 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.clone()); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner.clone()), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + + // fund the Parachain Origin's SA on Relay Chain with the native tokens held in reserve + Westend::fund_accounts(vec![(sov_of_sender_on_relay.clone().into(), amount_to_send * 2)]); + + // Init values for Parachain Destination + let beneficiary_id = PenpalBReceiver::get(); + let beneficiary: Location = AccountId32 { + id: beneficiary_id.clone().into(), + network: None // Test doesn't allow specifying a network here. + }.into(); + + // We get them from the PenpalA closure. + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::V4(destination.clone())), + beneficiary: Box::new(VersionedLocation::V4(beneficiary)), + assets: Box::new(VersionedAssets::V4(assets.clone())), + fee_asset_item: 0, + weight_limit: Unlimited, + }); + let sender = Alice; // Same as `PenpalASender`. + let extra = construct_penpal_extra(sender); + let extrinsic = construct_extrinsic_penpal(sender, call, extra); + let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); + let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; + remote_message = messages_to_query[0].clone(); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + remote_message.clone() + ).unwrap(); + delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + assert_eq!(delivery_fees_amount, 31_180_000_000); + }); + + // This is set in the Westend closure. + let mut intermediate_execution_fees = 0; + let mut intermediate_delivery_fees_amount = 0; + let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + // First we get the execution fees. + let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); + intermediate_execution_fees = Runtime::query_weight_to_asset_fee( + weight, + VersionedAssetId::V4(Here.into()), + ).unwrap(); + + // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. + let xcm_program = VersionedXcm::V4(Xcm::::from( + remote_message.clone().try_into().unwrap(), + )); + + // Now we get the delivery fees to the final destination. + let result = Runtime::dry_run_xcm( + sender_as_seen_by_relay.clone().into(), + xcm_program, + weight.clone(), + ).unwrap(); + let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; + // There's actually two messages here. + // One created when the message we sent from PenpalA arrived and was executed. + // The second one when we dry-run the xcm. + // We could've gotten the message from the queue without having to dry-run, but + // offchain applications would have to dry-run, so we do it here as well. + intermediate_remote_message = messages_to_query[0].clone(); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + intermediate_remote_message.clone(), + ).unwrap(); + intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + assert_eq!(intermediate_delivery_fees_amount, 39_700_000_000); + }); + + // Get the final execution fees in the destination. + let mut final_execution_fees = 0; + ::execute_with(|| { + type Runtime = ::Runtime; + + let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); + final_execution_fees = Runtime::query_weight_to_asset_fee( + weight, + VersionedAssetId::V4(Parent.into()), + ).unwrap(); + assert_eq!(final_execution_fees, 3_276_800_000); + }); + + // Dry-running is done. + PenpalA::reset_ext(); + Westend::reset_ext(); + PenpalB::reset_ext(); + + // Fund accounts again. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + Westend::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]); + + // Actually run the extrinsic. + let test_args = TestContext { + sender: PenpalASender::get(), // Alice. + receiver: PenpalBReceiver::get(), // Bob in PenpalB. + args: TestArgs::new_para(destination, beneficiary_id.clone(), amount_to_send, assets, None, 0), + }; + let mut test = ParaToParaThroughRelayTest::new(test_args); + + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &beneficiary_id) + }); + + test.set_dispatchable::(transfer_assets_para_to_para); + test.assert(); + + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &beneficiary_id) + }); + + // We know the exact fees on every hop. + assert_eq!( + sender_assets_after, + sender_assets_before + - amount_to_send + - delivery_fees_amount // This is charged directly from the sender's account. + ); + assert_eq!( + receiver_assets_after, + receiver_assets_before + + amount_to_send + - intermediate_execution_fees + - intermediate_delivery_fees_amount + - final_execution_fees + ); +} + fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 { let latest_assets: Assets = assets.try_into().unwrap(); let Fungible(amount) = latest_assets.inner()[0].fun else { @@ -124,13 +299,20 @@ fn transfer_assets(test: RelayToAssetHubTest) -> DispatchResult { ) } -/// Constructs an extrinsic. -fn construct_extrinsic( - sender: sp_keyring::AccountKeyring, - call: westend_runtime::RuntimeCall, -) -> westend_runtime::UncheckedExtrinsic { - type Runtime = ::Runtime; +fn transfer_assets_para_to_para(test: ParaToParaThroughRelayTest) -> DispatchResult { + ::PolkadotXcm::transfer_assets( + test.signed_origin, + bx!(test.args.dest.into()), + bx!(test.args.beneficiary.into()), + bx!(test.args.assets.into()), + test.args.fee_asset_item, + test.args.weight_limit, + ) +} +// Constructs the SignedExtra component of an extrinsic for the Westend runtime. +fn construct_westend_extra(sender: sp_keyring::AccountKeyring) -> westend_runtime::SignedExtra { + type Runtime = ::Runtime; let account_id = ::AccountId::from(sender.public()); // take the biggest period possible. let period = @@ -156,6 +338,45 @@ fn construct_extrinsic( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), ); + extra +} + +// Constructs the SignedExtra component of an extrinsic for the Westend runtime. +fn construct_penpal_extra(sender: sp_keyring::AccountKeyring) -> penpal_runtime::SignedExtra { + type Runtime = ::Runtime; + let account_id = ::AccountId::from(sender.public()); + // take the biggest period possible. + let period = + BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; + let current_block = ::System::block_number() + .saturated_into::() + // The `System::block_number` is initialized with `n+1`, + // so the actual block number is `n`. + .saturating_sub(1); + let tip = 0; + let extra: penpal_runtime::SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), + frame_system::CheckNonce::::from( + frame_system::Pallet::::account(&account_id).nonce, + ), + frame_system::CheckWeight::::new(), + pallet_asset_tx_payment::ChargeAssetTxPayment::::from(tip, None), + ); + extra +} + +/// Constructs an extrinsic for the Westend runtime. +fn construct_extrinsic_westend( + sender: sp_keyring::AccountKeyring, + call: westend_runtime::RuntimeCall, + extra: westend_runtime::SignedExtra, +) -> westend_runtime::UncheckedExtrinsic { + type Runtime = ::Runtime; + let account_id = ::AccountId::from(sender.public()); let raw_payload = westend_runtime::SignedPayload::new(call, extra).unwrap(); let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); let (call, extra, _) = raw_payload.deconstruct(); @@ -166,3 +387,23 @@ fn construct_extrinsic( extra, ) } + +/// Constructs an extrinsic for the Penpal runtime. +fn construct_extrinsic_penpal( + sender: sp_keyring::AccountKeyring, + call: penpal_runtime::RuntimeCall, + extra: penpal_runtime::SignedExtra, +) -> penpal_runtime::UncheckedExtrinsic { + type Runtime = ::Runtime; + type SignedPayload = generic::SignedPayload::; + let account_id = ::AccountId::from(sender.public()); + let raw_payload = SignedPayload::new(call, extra).unwrap(); + let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); + let (call, extra, _) = raw_payload.deconstruct(); + penpal_runtime::UncheckedExtrinsic::new_signed( + call, + account_id.into(), + MultiSignature::Sr25519(signature), + extra, + ) +} 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 db0ddf9ca799..5b0cd2c8b48e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1351,6 +1351,8 @@ impl_runtime_apis! { impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { use xcm_builder::InspectMessageQueues; + use xcm::prelude::*; + pallet_xcm::ShouldRecordXcm::::put(true); let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { log::error!( @@ -1371,7 +1373,10 @@ impl_runtime_apis! { }) } - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm, weight: Weight) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm::prelude::*; + let origin_location: Location = origin_location.try_into().map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_xcm", @@ -1380,7 +1385,7 @@ impl_runtime_apis! { ); XcmDryRunApiError::VersionedConversionFailed })?; - let xcm: Xcm = xcm.try_into().map_err(|error| { + let program: Xcm = program.try_into().map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_xcm", "Xcm version conversion failed with error {:?}", @@ -1388,12 +1393,12 @@ impl_runtime_apis! { ); XcmDryRunApiError::VersionedConversionFailed })?; - let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); - let result = XcmExecutor::::prepare_and_execute( + let mut hash = program.using_encoded(sp_core::hashing::blake2_256); + let result = xcm_executor::XcmExecutor::::prepare_and_execute( origin_location, - xcm, + program, &mut hash, - max_weight, + weight, Weight::zero(), ); let forwarded_messages = xcm_config::XcmRouter::get_messages(); diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index 028aa002a91e..9fd1b51b46b8 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -64,6 +64,7 @@ polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", def xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } +xcm-fee-payment-runtime-api = { path = "../../../../../polkadot/xcm/xcm-fee-payment-runtime-api", default-features = false } # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } @@ -135,6 +136,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std", ] runtime-benchmarks = [ @@ -164,6 +166,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 89885d77378b..2f45fa571625 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -32,6 +32,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); mod weights; pub mod xcm_config; +use codec::Encode; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ @@ -46,6 +47,7 @@ use frame_support::{ weights::{ constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, + WeightToFee as _, }, PalletId, }; @@ -81,6 +83,11 @@ use parachains_common::{AccountId, Signature}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::latest::prelude::{AssetId as AssetLocationId, BodyId}; +use xcm::{VersionedLocation, VersionedAssets, VersionedAssetId, VersionedXcm, IntoVersion}; +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; /// Balance of an account. pub type Balance = u128; @@ -835,6 +842,101 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + if !matches!(xcm_version, 3 | 4) { + return Err(XcmPaymentApiError::UnhandledXcmVersion); + } + // TODO: For now only Relay tokens. + Ok([VersionedAssetId::V4(xcm_config::RelayLocation::get().into())] + .into_iter() + .filter_map(|asset| asset.into_version(xcm_version).ok()) + .collect()) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let local_asset = VersionedAssetId::V4(xcm_config::RelayLocation::get().into()); + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + + if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } + + Ok(WeightToFee::weight_to_fee(&weight)) + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm::prelude::*; + + pallet_xcm::ShouldRecordXcm::::put(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); + let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_program: VersionedXcm::<()>::V4(local_xcm), + forwarded_messages, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm, weight: Weight) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm::prelude::*; + + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let program: Xcm = program.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = program.using_encoded(sp_core::hashing::blake2_256); + let result = xcm_executor::XcmExecutor::::prepare_and_execute( + origin_location, + program, + &mut hash, + weight, + Weight::zero(), + ); + let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_messages, + emitted_events: events, + execution_result: result, + }) + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 54f40bd01097..582a7f33f86b 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -34,8 +34,8 @@ use sp_runtime::{ SaturatedConversion, }; use sp_std::{marker::PhantomData, prelude::*}; -use xcm::{latest::prelude::*, WrapVersion}; -use xcm_builder::TakeRevenue; +use xcm::{latest::prelude::*, WrapVersion, VersionedLocation, VersionedXcm}; +use xcm_builder::{TakeRevenue, InspectMessageQueues}; use xcm_executor::{ traits::{MatchesFungibles, TransactAsset, WeightTrader}, AssetsInHolding, @@ -93,6 +93,12 @@ where } } +impl InspectMessageQueues for ParentAsUmp { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + T::get_messages() + } +} + /// Contains information to handle refund/payment for xcm-execution #[derive(Clone, Eq, PartialEq, Debug)] struct AssetTraderRefunder { diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index 15d59adf78a5..cbec1a8ca103 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -146,7 +146,9 @@ impl InspectMessageQueues for ChildParachainRouter> = messages .iter() .map(|downward_message| { - VersionedXcm::<()>::decode(&mut &downward_message.msg[..]).unwrap() + let message = VersionedXcm::<()>::decode(&mut &downward_message.msg[..]).unwrap(); + log::trace!(target: "xcm::DownwardMessageQueues::get_messages", "Message: {:?}, sent at: {:?}", message, downward_message.sent_at); + message }) .collect(); (VersionedLocation::V4(Parachain(para_id.into()).into()), decoded_messages) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 6e9d0e73982f..9132cc2c7257 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2252,6 +2252,7 @@ sp_api::impl_runtime_apis! { } fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; let origin_location: Location = origin_location.try_into().map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_xcm", @@ -2269,11 +2270,11 @@ sp_api::impl_runtime_apis! { XcmDryRunApiError::VersionedConversionFailed })?; let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); - let result = XcmExecutor::::prepare_and_execute( + let result = xcm_executor::XcmExecutor::::prepare_and_execute( origin_location, xcm, &mut hash, - max_weight, + weight, Weight::zero(), ); let forwarded_messages = xcm_config::XcmRouter::get_messages(); diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs index f4ba38640634..5c284aaf1475 100644 --- a/polkadot/xcm/xcm-builder/src/routing.rs +++ b/polkadot/xcm/xcm-builder/src/routing.rs @@ -146,6 +146,7 @@ impl EnsureDelivery for Tuple { } /// Inspects messages in queues. +/// Meant to be used in runtime APIs, not in runtimes. pub trait InspectMessageQueues { /// Get queued messages and their destinations. fn get_messages() -> Vec<(VersionedLocation, Vec>)>; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index a75f1fb63dbe..12db53cbc27e 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -115,7 +115,7 @@ impl pallet_assets::Config for TestRuntime { } thread_local! { - pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); + pub static SENT_XCM: RefCell)>> = const { RefCell::new(Vec::new()) }; } pub(crate) fn sent_xcm() -> Vec<(Location, Xcm<()>)> { From cad8cd5cb968ac4c1c5d2d6a7196e58424848e16 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Mon, 29 Apr 2024 17:04:01 +0200 Subject: [PATCH 42/61] fix: remove unnecessary clone --- .../assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index ff124b0c356b..faa8fc1c74e8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -190,7 +190,7 @@ fn multi_hop_works() { let result = Runtime::dry_run_xcm( sender_as_seen_by_relay.clone().into(), xcm_program, - weight.clone(), + weight, ).unwrap(); let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; // There's actually two messages here. From 3c12610875c176f5d2f1b14a2505a59a9e2d2d4f Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 11:36:22 +0200 Subject: [PATCH 43/61] fix(xcm-fee-estimation-tests): use immortal transactions --- .../src/tests/xcm_fee_estimation.rs | 78 ++++--------------- 1 file changed, 17 insertions(+), 61 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index faa8fc1c74e8..00d1e9cc50b2 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -17,9 +17,8 @@ use crate::imports::*; -use polkadot_runtime_common::BlockHashCount; use sp_keyring::AccountKeyring::Alice; -use sp_runtime::{MultiSignature, SaturatedConversion, generic}; +use sp_runtime::{MultiSignature, generic}; use xcm_fee_payment_runtime_api::{ dry_run::runtime_decl_for_xcm_dry_run_api::XcmDryRunApiV1, fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, @@ -42,7 +41,7 @@ fn teleport_relay_system_para_works() { // We get them from the Westend closure. let mut delivery_fees_amount = 0; let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); - ::execute_with(|| { + ::new_ext().execute_with(|| { type Runtime = ::Runtime; type RuntimeCall = ::RuntimeCall; @@ -54,8 +53,7 @@ fn teleport_relay_system_para_works() { weight_limit: Unlimited, }); let sender = Alice; // Is the same as `WestendSender`. - let extra = construct_westend_extra(sender); - let extrinsic = construct_extrinsic_westend(sender, call, extra); + let extrinsic = construct_extrinsic_westend(sender, call); let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; remote_message = messages_to_query[0].clone(); @@ -153,8 +151,7 @@ fn multi_hop_works() { weight_limit: Unlimited, }); let sender = Alice; // Same as `PenpalASender`. - let extra = construct_penpal_extra(sender); - let extrinsic = construct_extrinsic_penpal(sender, call, extra); + let extrinsic = construct_extrinsic_penpal(sender, call); let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; remote_message = messages_to_query[0].clone(); @@ -311,92 +308,51 @@ fn transfer_assets_para_to_para(test: ParaToParaThroughRelayTest) -> DispatchRes } // Constructs the SignedExtra component of an extrinsic for the Westend runtime. -fn construct_westend_extra(sender: sp_keyring::AccountKeyring) -> westend_runtime::SignedExtra { +fn construct_extrinsic_westend(sender: sp_keyring::AccountKeyring, call: westend_runtime::RuntimeCall) -> westend_runtime::UncheckedExtrinsic { type Runtime = ::Runtime; let account_id = ::AccountId::from(sender.public()); - // take the biggest period possible. - let period = - BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; - let current_block = ::System::block_number() - .saturated_into::() - // The `System::block_number` is initialized with `n+1`, - // so the actual block number is `n`. - .saturating_sub(1); let tip = 0; let extra: westend_runtime::SignedExtra = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), frame_system::CheckGenesis::::new(), - frame_system::CheckMortality::::from(sp_runtime::generic::Era::mortal( - period, - current_block, - )), + frame_system::CheckMortality::::from(sp_runtime::generic::Era::immortal()), frame_system::CheckNonce::::from( frame_system::Pallet::::account(&account_id).nonce, ), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), ); - extra + let raw_payload = westend_runtime::SignedPayload::new(call, extra).unwrap(); + let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); + let (call, extra, _) = raw_payload.deconstruct(); + westend_runtime::UncheckedExtrinsic::new_signed( + call, + account_id.into(), + MultiSignature::Sr25519(signature), + extra, + ) } // Constructs the SignedExtra component of an extrinsic for the Westend runtime. -fn construct_penpal_extra(sender: sp_keyring::AccountKeyring) -> penpal_runtime::SignedExtra { +fn construct_extrinsic_penpal(sender: sp_keyring::AccountKeyring, call: penpal_runtime::RuntimeCall) -> penpal_runtime::UncheckedExtrinsic { type Runtime = ::Runtime; let account_id = ::AccountId::from(sender.public()); - // take the biggest period possible. - let period = - BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; - let current_block = ::System::block_number() - .saturated_into::() - // The `System::block_number` is initialized with `n+1`, - // so the actual block number is `n`. - .saturating_sub(1); let tip = 0; let extra: penpal_runtime::SignedExtra = ( frame_system::CheckNonZeroSender::::new(), frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from(generic::Era::mortal(period, current_block)), + frame_system::CheckEra::::from(generic::Era::immortal()), frame_system::CheckNonce::::from( frame_system::Pallet::::account(&account_id).nonce, ), frame_system::CheckWeight::::new(), pallet_asset_tx_payment::ChargeAssetTxPayment::::from(tip, None), ); - extra -} - -/// Constructs an extrinsic for the Westend runtime. -fn construct_extrinsic_westend( - sender: sp_keyring::AccountKeyring, - call: westend_runtime::RuntimeCall, - extra: westend_runtime::SignedExtra, -) -> westend_runtime::UncheckedExtrinsic { - type Runtime = ::Runtime; - let account_id = ::AccountId::from(sender.public()); - let raw_payload = westend_runtime::SignedPayload::new(call, extra).unwrap(); - let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); - let (call, extra, _) = raw_payload.deconstruct(); - westend_runtime::UncheckedExtrinsic::new_signed( - call, - account_id.into(), - MultiSignature::Sr25519(signature), - extra, - ) -} - -/// Constructs an extrinsic for the Penpal runtime. -fn construct_extrinsic_penpal( - sender: sp_keyring::AccountKeyring, - call: penpal_runtime::RuntimeCall, - extra: penpal_runtime::SignedExtra, -) -> penpal_runtime::UncheckedExtrinsic { - type Runtime = ::Runtime; type SignedPayload = generic::SignedPayload::; - let account_id = ::AccountId::from(sender.public()); let raw_payload = SignedPayload::new(call, extra).unwrap(); let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); let (call, extra, _) = raw_payload.deconstruct(); From 6246d7fac936f1dd0c485846a9cd448306ffd318 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 12:44:28 +0200 Subject: [PATCH 44/61] fix: fmt --- .../modules/xcm-bridge-hub-router/src/lib.rs | 14 ++- cumulus/pallets/parachain-system/src/lib.rs | 9 +- cumulus/pallets/xcmp-queue/src/lib.rs | 21 +++-- .../src/tests/xcm_fee_estimation.rs | 85 ++++++++++--------- .../runtimes/testing/penpal/src/lib.rs | 9 +- cumulus/primitives/utility/src/lib.rs | 8 +- 6 files changed, 79 insertions(+), 67 deletions(-) diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index d1ad82a4a66b..e79eaf7890f0 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -39,7 +39,7 @@ use sp_core::H256; use sp_runtime::{FixedPointNumber, FixedU128, Saturating}; use sp_std::vec::Vec; use xcm::prelude::*; -use xcm_builder::{ExporterFor, SovereignPaidRemoteExporter, InspectMessageQueues}; +use xcm_builder::{ExporterFor, InspectMessageQueues, SovereignPaidRemoteExporter}; pub use pallet::*; pub use weights::WeightInfo; @@ -647,14 +647,10 @@ mod tests { #[test] fn get_messages_works() { run_test(|| { - assert_ok!( - send_xcm::(( - Parent, - Parent, - GlobalConsensus(BridgedNetworkId::get()), - Parachain(1000) - ).into(), vec![ClearOrigin].into()) - ); + assert_ok!(send_xcm::( + (Parent, Parent, GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)).into(), + vec![ClearOrigin].into() + )); // TODO: Get messages queued for bridging. assert_eq!(XcmBridgeHubRouter::get_messages(), vec![]); }); diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index 6363c2f79a7f..c8e7d1bb30f7 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -55,7 +55,7 @@ use sp_runtime::{ BoundedSlice, FixedU128, RuntimeDebug, Saturating, }; use sp_std::{cmp, collections::btree_map::BTreeMap, prelude::*}; -use xcm::{VersionedLocation, VersionedXcm, latest::XcmHash}; +use xcm::{latest::XcmHash, VersionedLocation, VersionedXcm}; use xcm_builder::InspectMessageQueues; mod benchmarking; @@ -1613,10 +1613,9 @@ impl InspectMessageQueues for Pallet { fn get_messages() -> Vec<(VersionedLocation, Vec>)> { use xcm::prelude::*; - let messages: Vec> = PendingUpwardMessages::::get().iter() - .map(|encoded_message| { - VersionedXcm::<()>::decode(&mut &encoded_message[..]).unwrap() - }) + let messages: Vec> = PendingUpwardMessages::::get() + .iter() + .map(|encoded_message| VersionedXcm::<()>::decode(&mut &encoded_message[..]).unwrap()) .collect(); vec![(VersionedLocation::V4(Parent.into()), messages)] diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 363ee663af6f..7846cae1d9ea 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -71,8 +71,8 @@ use sp_core::MAX_POSSIBLE_ALLOCATION; use sp_runtime::{FixedU128, RuntimeDebug, Saturating}; use sp_std::prelude::*; use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; -use xcm_executor::traits::ConvertOrigin; use xcm_builder::InspectMessageQueues; +use xcm_executor::traits::ConvertOrigin; pub use pallet::*; @@ -955,20 +955,25 @@ impl InspectMessageQueues for Pallet { OutboundXcmpMessages::::iter() .map(|(para_id, _, messages)| { let mut data = &messages[..]; - let decoded_format = XcmpMessageFormat::decode_with_depth_limit( - MAX_XCM_DECODE_DEPTH, - &mut data, - ).unwrap(); - if decoded_format != XcmpMessageFormat::ConcatenatedVersionedXcm { panic!("Unexpected format.") } + let decoded_format = + XcmpMessageFormat::decode_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut data) + .unwrap(); + if decoded_format != XcmpMessageFormat::ConcatenatedVersionedXcm { + panic!("Unexpected format.") + } let mut decoded_messages = Vec::new(); while !data.is_empty() { let decoded_message = VersionedXcm::<()>::decode_with_depth_limit( MAX_XCM_DECODE_DEPTH, &mut data, - ).unwrap(); + ) + .unwrap(); decoded_messages.push(decoded_message); } - (VersionedLocation::V4((Parent, Parachain(para_id.into())).into()), decoded_messages) + ( + VersionedLocation::V4((Parent, Parachain(para_id.into())).into()), + decoded_messages, + ) }) .collect() } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index 00d1e9cc50b2..28c38c20cf81 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -13,12 +13,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Tests to ensure correct XCM fee estimation between Asset Hub Westend and the Westend relay chain. +//! Tests to ensure correct XCM fee estimation between Asset Hub Westend and the Westend relay +//! chain. use crate::imports::*; use sp_keyring::AccountKeyring::Alice; -use sp_runtime::{MultiSignature, generic}; +use sp_runtime::{generic, MultiSignature}; use xcm_fee_payment_runtime_api::{ dry_run::runtime_decl_for_xcm_dry_run_api::XcmDryRunApiV1, fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, @@ -133,8 +134,9 @@ fn multi_hop_works() { let beneficiary_id = PenpalBReceiver::get(); let beneficiary: Location = AccountId32 { id: beneficiary_id.clone().into(), - network: None // Test doesn't allow specifying a network here. - }.into(); + network: None, // Test doesn't allow specifying a network here. + } + .into(); // We get them from the PenpalA closure. let mut delivery_fees_amount = 0; @@ -155,10 +157,9 @@ fn multi_hop_works() { let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; remote_message = messages_to_query[0].clone(); - let delivery_fees = Runtime::query_delivery_fees( - destination_to_query.clone(), - remote_message.clone() - ).unwrap(); + let delivery_fees = + Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) + .unwrap(); delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); assert_eq!(delivery_fees_amount, 31_180_000_000); }); @@ -173,22 +174,17 @@ fn multi_hop_works() { // First we get the execution fees. let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); - intermediate_execution_fees = Runtime::query_weight_to_asset_fee( - weight, - VersionedAssetId::V4(Here.into()), - ).unwrap(); + intermediate_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Here.into())).unwrap(); // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. - let xcm_program = VersionedXcm::V4(Xcm::::from( - remote_message.clone().try_into().unwrap(), - )); + let xcm_program = + VersionedXcm::V4(Xcm::::from(remote_message.clone().try_into().unwrap())); // Now we get the delivery fees to the final destination. - let result = Runtime::dry_run_xcm( - sender_as_seen_by_relay.clone().into(), - xcm_program, - weight, - ).unwrap(); + let result = + Runtime::dry_run_xcm(sender_as_seen_by_relay.clone().into(), xcm_program, weight) + .unwrap(); let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; // There's actually two messages here. // One created when the message we sent from PenpalA arrived and was executed. @@ -199,7 +195,8 @@ fn multi_hop_works() { let delivery_fees = Runtime::query_delivery_fees( destination_to_query.clone(), intermediate_remote_message.clone(), - ).unwrap(); + ) + .unwrap(); intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); assert_eq!(intermediate_delivery_fees_amount, 39_700_000_000); }); @@ -210,10 +207,9 @@ fn multi_hop_works() { type Runtime = ::Runtime; let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); - final_execution_fees = Runtime::query_weight_to_asset_fee( - weight, - VersionedAssetId::V4(Parent.into()), - ).unwrap(); + final_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) + .unwrap(); assert_eq!(final_execution_fees, 3_276_800_000); }); @@ -233,9 +229,16 @@ fn multi_hop_works() { // Actually run the extrinsic. let test_args = TestContext { - sender: PenpalASender::get(), // Alice. + sender: PenpalASender::get(), // Alice. receiver: PenpalBReceiver::get(), // Bob in PenpalB. - args: TestArgs::new_para(destination, beneficiary_id.clone(), amount_to_send, assets, None, 0), + args: TestArgs::new_para( + destination, + beneficiary_id.clone(), + amount_to_send, + assets, + None, + 0, + ), }; let mut test = ParaToParaThroughRelayTest::new(test_args); @@ -263,17 +266,16 @@ fn multi_hop_works() { // We know the exact fees on every hop. assert_eq!( sender_assets_after, - sender_assets_before - - amount_to_send - - delivery_fees_amount // This is charged directly from the sender's account. + sender_assets_before - amount_to_send - delivery_fees_amount /* This is charged directly + * from the sender's + * account. */ ); assert_eq!( receiver_assets_after, - receiver_assets_before - + amount_to_send - - intermediate_execution_fees - - intermediate_delivery_fees_amount - - final_execution_fees + receiver_assets_before + amount_to_send - + intermediate_execution_fees - + intermediate_delivery_fees_amount - + final_execution_fees ); } @@ -308,7 +310,10 @@ fn transfer_assets_para_to_para(test: ParaToParaThroughRelayTest) -> DispatchRes } // Constructs the SignedExtra component of an extrinsic for the Westend runtime. -fn construct_extrinsic_westend(sender: sp_keyring::AccountKeyring, call: westend_runtime::RuntimeCall) -> westend_runtime::UncheckedExtrinsic { +fn construct_extrinsic_westend( + sender: sp_keyring::AccountKeyring, + call: westend_runtime::RuntimeCall, +) -> westend_runtime::UncheckedExtrinsic { type Runtime = ::Runtime; let account_id = ::AccountId::from(sender.public()); let tip = 0; @@ -336,7 +341,10 @@ fn construct_extrinsic_westend(sender: sp_keyring::AccountKeyring, call: westend } // Constructs the SignedExtra component of an extrinsic for the Westend runtime. -fn construct_extrinsic_penpal(sender: sp_keyring::AccountKeyring, call: penpal_runtime::RuntimeCall) -> penpal_runtime::UncheckedExtrinsic { +fn construct_extrinsic_penpal( + sender: sp_keyring::AccountKeyring, + call: penpal_runtime::RuntimeCall, +) -> penpal_runtime::UncheckedExtrinsic { type Runtime = ::Runtime; let account_id = ::AccountId::from(sender.public()); let tip = 0; @@ -352,7 +360,8 @@ fn construct_extrinsic_penpal(sender: sp_keyring::AccountKeyring, call: penpal_r frame_system::CheckWeight::::new(), pallet_asset_tx_payment::ChargeAssetTxPayment::::from(tip, None), ); - type SignedPayload = generic::SignedPayload::; + type SignedPayload = + generic::SignedPayload; let raw_payload = SignedPayload::new(call, extra).unwrap(); let signature = raw_payload.using_encoded(|payload| sender.sign(payload)); let (call, extra, _) = raw_payload.deconstruct(); diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 2f45fa571625..16ab0136bdea 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -45,9 +45,8 @@ use frame_support::{ AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, ConstU8, Everything, TransformOrigin, }, weights::{ - constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, + constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, WeightToFee as _, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, - WeightToFee as _, }, PalletId, }; @@ -82,8 +81,10 @@ pub use sp_runtime::BuildStorage; use parachains_common::{AccountId, Signature}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; -use xcm::latest::prelude::{AssetId as AssetLocationId, BodyId}; -use xcm::{VersionedLocation, VersionedAssets, VersionedAssetId, VersionedXcm, IntoVersion}; +use xcm::{ + latest::prelude::{AssetId as AssetLocationId, BodyId}, + IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, +}; use xcm_fee_payment_runtime_api::{ dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, fees::Error as XcmPaymentApiError, diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 582a7f33f86b..64784eb36f84 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -34,8 +34,8 @@ use sp_runtime::{ SaturatedConversion, }; use sp_std::{marker::PhantomData, prelude::*}; -use xcm::{latest::prelude::*, WrapVersion, VersionedLocation, VersionedXcm}; -use xcm_builder::{TakeRevenue, InspectMessageQueues}; +use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm, WrapVersion}; +use xcm_builder::{InspectMessageQueues, TakeRevenue}; use xcm_executor::{ traits::{MatchesFungibles, TransactAsset, WeightTrader}, AssetsInHolding, @@ -93,7 +93,9 @@ where } } -impl InspectMessageQueues for ParentAsUmp { +impl InspectMessageQueues + for ParentAsUmp +{ fn get_messages() -> Vec<(VersionedLocation, Vec>)> { T::get_messages() } From 69a26ec1eabe49856951d4155d8c0f1c5c453238 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 13:13:41 +0200 Subject: [PATCH 45/61] fix: Cargo.toml format --- cumulus/pallets/parachain-system/Cargo.toml | 2 +- cumulus/pallets/xcmp-queue/Cargo.toml | 2 +- cumulus/parachains/runtimes/testing/penpal/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cumulus/pallets/parachain-system/Cargo.toml b/cumulus/pallets/parachain-system/Cargo.toml index fef697d22757..57e274db361d 100644 --- a/cumulus/pallets/parachain-system/Cargo.toml +++ b/cumulus/pallets/parachain-system/Cargo.toml @@ -96,8 +96,8 @@ std = [ "sp-tracing/std", "sp-trie/std", "trie-db/std", - "xcm/std", "xcm-builder/std", + "xcm/std", ] runtime-benchmarks = [ diff --git a/cumulus/pallets/xcmp-queue/Cargo.toml b/cumulus/pallets/xcmp-queue/Cargo.toml index 1b4b37c909ba..e3530ef7bf0e 100644 --- a/cumulus/pallets/xcmp-queue/Cargo.toml +++ b/cumulus/pallets/xcmp-queue/Cargo.toml @@ -69,9 +69,9 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "xcm-builder/std", "xcm-executor/std", "xcm/std", - "xcm-builder/std", ] runtime-benchmarks = [ diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index 9fd1b51b46b8..4ebb95f26cf6 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -135,8 +135,8 @@ std = [ "substrate-wasm-builder", "xcm-builder/std", "xcm-executor/std", - "xcm/std", "xcm-fee-payment-runtime-api/std", + "xcm/std", ] runtime-benchmarks = [ From 29a0b8a90cb1de922dffb85dd5f33989003f095f Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 13:21:51 +0200 Subject: [PATCH 46/61] Update cumulus/parachains/runtimes/testing/penpal/src/lib.rs Co-authored-by: Adrian Catangiu --- cumulus/parachains/runtimes/testing/penpal/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 16ab0136bdea..64d028ebb198 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -848,7 +848,6 @@ impl_runtime_apis! { if !matches!(xcm_version, 3 | 4) { return Err(XcmPaymentApiError::UnhandledXcmVersion); } - // TODO: For now only Relay tokens. Ok([VersionedAssetId::V4(xcm_config::RelayLocation::get().into())] .into_iter() .filter_map(|asset| asset.into_version(xcm_version).ok()) From 6d785f397f73541c34acd33ef4cabef6b5db4de0 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 16:43:32 +0200 Subject: [PATCH 47/61] feat: implement InspectMessageQueues on the bridge router --- .../modules/xcm-bridge-hub-router/src/lib.rs | 22 ++++++++--- .../modules/xcm-bridge-hub-router/src/mock.rs | 38 +++++++++++++++---- cumulus/pallets/xcmp-queue/src/lib.rs | 1 + .../xcm/xcm-builder/src/universal_exports.rs | 9 +++++ 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index e79eaf7890f0..2a236a680a30 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -96,7 +96,7 @@ pub mod pallet { /// Origin of the sibling bridge hub that is allowed to report bridge status. type BridgeHubOrigin: EnsureOrigin; /// Actual message sender (`HRMP` or `DMP`) to the sibling bridge hub location. - type ToBridgeHubSender: SendXcm; + type ToBridgeHubSender: SendXcm + InspectMessageQueues; /// Underlying channel with the sibling bridge hub. It must match the channel, used /// by the `Self::ToBridgeHubSender`. type WithBridgeHubChannel: XcmChannelStatusProvider; @@ -399,8 +399,7 @@ impl, I: 'static> SendXcm for Pallet { impl, I: 'static> InspectMessageQueues for Pallet { fn get_messages() -> Vec<(VersionedLocation, Vec>)> { - // TODO: Get messages queued for bridging. - Vec::new() + ViaBridgeHubExporter::::get_messages() } } @@ -651,8 +650,21 @@ mod tests { (Parent, Parent, GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)).into(), vec![ClearOrigin].into() )); - // TODO: Get messages queued for bridging. - assert_eq!(XcmBridgeHubRouter::get_messages(), vec![]); + assert_eq!( + XcmBridgeHubRouter::get_messages(), + vec![ + ( + VersionedLocation::V4((Parent, Parachain(1002)).into()), + vec![VersionedXcm::V4(Xcm::builder() + .withdraw_asset((Parent, 1_002_000)) + .buy_execution((Parent, 1_002_000), Unlimited) + .set_appendix(Xcm::builder_unsafe().deposit_asset(AllCounted(1), (Parent, Parachain(1000))).build()) + .export_message(Kusama, Parachain(1000), Xcm::builder_unsafe().clear_origin().build()) + .build() + )], + ), + ], + ); }); } } diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index 20c86d1da9a2..28403feb8ac0 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/mock.rs @@ -18,6 +18,8 @@ use crate as pallet_xcm_bridge_hub_router; +use sp_std::cell::RefCell; +use codec::Encode; use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; use frame_support::{ construct_runtime, derive_impl, parameter_types, @@ -26,7 +28,7 @@ use frame_support::{ use frame_system::EnsureRoot; use sp_runtime::{traits::ConstU128, BuildStorage}; use xcm::prelude::*; -use xcm_builder::{NetworkExportTable, NetworkExportTableItem}; +use xcm_builder::{NetworkExportTable, NetworkExportTableItem, InspectMessageQueues}; pub type AccountId = u64; type Block = frame_system::mocking::MockBlock; @@ -106,19 +108,37 @@ impl TestToBridgeHubSender { } } +thread_local! { + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); +} + impl SendXcm for TestToBridgeHubSender { - type Ticket = (); + type Ticket = (Location, Xcm<()>); fn validate( - _destination: &mut Option, - _message: &mut Option>, + destination: &mut Option, + message: &mut Option>, ) -> SendResult { - Ok(((), (BridgeFeeAsset::get(), HRMP_FEE).into())) + let pair = (destination.take().unwrap(), message.take().unwrap()); + Ok((pair, (BridgeFeeAsset::get(), HRMP_FEE).into())) } - fn deliver(_ticket: Self::Ticket) -> Result { + fn deliver(pair: Self::Ticket) -> Result { frame_support::storage::unhashed::put(b"TestToBridgeHubSender.Sent", &true); - Ok([0u8; 32]) + let hash = fake_message_hash(&pair.1); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); + Ok(hash) + } +} + +impl InspectMessageQueues for TestToBridgeHubSender { + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + SENT_XCM.with(|q| (*q.borrow()).clone().iter().map(|(location, message)| { + ( + VersionedLocation::V4(location.clone()), + vec![VersionedXcm::V4(message.clone())], + ) + }).collect()) } } @@ -146,3 +166,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { pub fn run_test(test: impl FnOnce() -> T) -> T { new_test_ext().execute_with(test) } + +pub(crate) fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(sp_io::hashing::blake2_256) +} diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 7846cae1d9ea..cc785b66150e 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -970,6 +970,7 @@ impl InspectMessageQueues for Pallet { .unwrap(); decoded_messages.push(decoded_message); } + ( VersionedLocation::V4((Parent, Parachain(para_id.into())).into()), decoded_messages, diff --git a/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs index d0e3ef3032ea..6f5929b29e20 100644 --- a/polkadot/xcm/xcm-builder/src/universal_exports.rs +++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs @@ -21,6 +21,7 @@ use parity_scale_codec::{Decode, Encode}; use sp_std::{convert::TryInto, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::{validate_export, ExportXcm}; +use crate::InspectMessageQueues; use SendError::*; /// Returns the network ID and consensus location within that network of the remote @@ -335,6 +336,14 @@ impl InspectMessageQueues + for SovereignPaidRemoteExporter +{ + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + Router::get_messages() + } +} + pub trait DispatchBlob { /// Takes an incoming blob from over some point-to-point link (usually from some sort of /// inter-consensus bridge) and then does what needs to be done with it. Usually this means From 3298ff2c2497b37b318510bec8ac9a8147b0948e Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 17:27:22 +0200 Subject: [PATCH 48/61] feat(xcm-dry-run-api): remove unnecessary weight parameter --- .../assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs | 2 +- .../parachains/runtimes/assets/asset-hub-westend/src/lib.rs | 4 ++-- cumulus/parachains/runtimes/testing/penpal/src/lib.rs | 4 ++-- polkadot/node/service/src/fake_runtime_api.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 4 ++-- polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs | 2 +- .../xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs | 1 - 7 files changed, 9 insertions(+), 10 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index 28c38c20cf81..cd3677058396 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -183,7 +183,7 @@ fn multi_hop_works() { // Now we get the delivery fees to the final destination. let result = - Runtime::dry_run_xcm(sender_as_seen_by_relay.clone().into(), xcm_program, weight) + Runtime::dry_run_xcm(sender_as_seen_by_relay.clone().into(), xcm_program) .unwrap(); let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; // There's actually two messages here. 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 5b0cd2c8b48e..6f81a43b5785 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1373,7 +1373,7 @@ impl_runtime_apis! { }) } - fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm, weight: Weight) -> Result, XcmDryRunApiError> { + fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm) -> Result, XcmDryRunApiError> { use xcm_builder::InspectMessageQueues; use xcm::prelude::*; @@ -1398,7 +1398,7 @@ impl_runtime_apis! { origin_location, program, &mut hash, - weight, + Weight::MAX, // Max limit available for execution. Weight::zero(), ); let forwarded_messages = xcm_config::XcmRouter::get_messages(); diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 64d028ebb198..fa78cd36eb48 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -899,7 +899,7 @@ impl_runtime_apis! { }) } - fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm, weight: Weight) -> Result, XcmDryRunApiError> { + fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm) -> Result, XcmDryRunApiError> { use xcm_builder::InspectMessageQueues; use xcm::prelude::*; @@ -924,7 +924,7 @@ impl_runtime_apis! { origin_location, program, &mut hash, - weight, + Weight::MAX, // Max limit. Weight::zero(), ); let forwarded_messages = xcm_config::XcmRouter::get_messages(); diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index 21367acd5976..45f770cdf168 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -421,7 +421,7 @@ sp_api::impl_runtime_apis! { unimplemented!() } - fn dry_run_xcm(_: VersionedLocation, _: VersionedXcm<()>, _: Weight) -> Result, xcm_fee_payment_runtime_api::dry_run::Error> { + fn dry_run_xcm(_: VersionedLocation, _: VersionedXcm<()>) -> Result, xcm_fee_payment_runtime_api::dry_run::Error> { unimplemented!() } } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 516e776bb0c1..804c9122f3cd 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2250,7 +2250,7 @@ sp_api::impl_runtime_apis! { }) } - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result, XcmDryRunApiError> { + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { use xcm_builder::InspectMessageQueues; let origin_location: Location = origin_location.try_into().map_err(|error| { log::error!( @@ -2273,7 +2273,7 @@ sp_api::impl_runtime_apis! { origin_location, xcm, &mut hash, - weight, + Weight::MAX, // Max limit available for execution. Weight::zero(), ); let forwarded_messages = xcm_config::XcmRouter::get_messages(); diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index 1125dbff4642..293a038b5e72 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -63,7 +63,7 @@ sp_api::decl_runtime_apis! { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, Error>; /// Dry run XCM program - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, weight: Weight) -> Result, Error>; + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, Error>; } } diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index e7884c9ad152..f4e2ec10fb3b 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -335,7 +335,6 @@ fn dry_run_xcm() { H256::zero(), VersionedLocation::V4(AccountIndex64 { index: 1, network: None }.into()), VersionedXcm::V4(xcm), - xcm_weight, ) .unwrap() .unwrap(); From f1538755a63d430e296ae00e60ef62e98bb50abc Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 17:48:34 +0200 Subject: [PATCH 49/61] feat(record-xcm): add getters and setters to trait --- .../runtimes/assets/asset-hub-westend/src/lib.rs | 5 +++-- .../parachains/runtimes/testing/penpal/src/lib.rs | 5 +++-- polkadot/runtime/westend/src/lib.rs | 5 +++-- polkadot/xcm/pallet-xcm/src/lib.rs | 12 ++++++++++-- polkadot/xcm/xcm-executor/src/lib.rs | 4 +++- polkadot/xcm/xcm-executor/src/traits/record_xcm.rs | 10 ++++++++++ .../xcm/xcm-fee-payment-runtime-api/tests/mock.rs | 4 ++-- 7 files changed, 34 insertions(+), 11 deletions(-) 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 6f81a43b5785..ab61492bfcaa 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1351,9 +1351,10 @@ impl_runtime_apis! { impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { use xcm_builder::InspectMessageQueues; + use xcm_executor::RecordXcm; use xcm::prelude::*; - pallet_xcm::ShouldRecordXcm::::put(true); + pallet_xcm::Pallet::::set_record_xcm(true); let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_extrinsic", @@ -1362,7 +1363,7 @@ impl_runtime_apis! { ); XcmDryRunApiError::InvalidExtrinsic })?; - let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); let forwarded_messages = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index fa78cd36eb48..a19c57717058 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -877,9 +877,10 @@ impl_runtime_apis! { impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { use xcm_builder::InspectMessageQueues; + use xcm_executor::RecordXcm; use xcm::prelude::*; - pallet_xcm::ShouldRecordXcm::::put(true); + pallet_xcm::Pallet::::set_record_xcm(true); let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_extrinsic", @@ -888,7 +889,7 @@ impl_runtime_apis! { ); XcmDryRunApiError::InvalidExtrinsic })?; - let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); let forwarded_messages = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 804c9122f3cd..2ec4a98d0e99 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2230,7 +2230,8 @@ sp_api::impl_runtime_apis! { impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { use xcm_builder::InspectMessageQueues; - pallet_xcm::ShouldRecordXcm::::put(true); + use xcm_executor::RecordXcm; + pallet_xcm::Pallet::::set_record_xcm(true); let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_extrinsic", @@ -2239,7 +2240,7 @@ sp_api::impl_runtime_apis! { ); XcmDryRunApiError::InvalidExtrinsic })?; - let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); let forwarded_messages = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index f330da54a4ed..1563da88aea3 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -772,7 +772,7 @@ pub mod pallet { /// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] /// implementation in the XCM executor configuration. #[pallet::storage] - pub type ShouldRecordXcm = StorageValue<_, bool, ValueQuery>; + pub(crate) type ShouldRecordXcm = StorageValue<_, bool, ValueQuery>; /// If [`ShouldRecordXcm`] is set to true, then the last XCM program executed locally /// will be stored here. @@ -781,7 +781,7 @@ pub mod pallet { /// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`] /// implementation in the XCM executor configuration. #[pallet::storage] - pub type RecordedXcm = StorageValue<_, Xcm<()>>; + pub(crate) type RecordedXcm = StorageValue<_, Xcm<()>>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -3131,6 +3131,14 @@ impl xcm_executor::traits::RecordXcm for Pallet { ShouldRecordXcm::::get() } + fn set_record_xcm(enabled: bool) { + ShouldRecordXcm::::put(enabled); + } + + fn recorded_xcm() -> Xcm<()> { + RecordedXcm::::get().unwrap_or(Xcm::default()) + } + fn record(xcm: Xcm<()>) { RecordedXcm::::put(xcm); } diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 68c1dc630b49..54a50ddb7dab 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -33,10 +33,12 @@ use traits::{ validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, Enact, ExportXcm, FeeManager, FeeReason, HandleHrmpChannelAccepted, HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, - Properties, RecordXcm, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, + Properties, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, XcmAssetTransfers, }; +pub use traits::RecordXcm; + mod assets; pub use assets::AssetsInHolding; mod config; diff --git a/polkadot/xcm/xcm-executor/src/traits/record_xcm.rs b/polkadot/xcm/xcm-executor/src/traits/record_xcm.rs index 5fb34615bf42..6090b27d9263 100644 --- a/polkadot/xcm/xcm-executor/src/traits/record_xcm.rs +++ b/polkadot/xcm/xcm-executor/src/traits/record_xcm.rs @@ -22,6 +22,10 @@ use xcm::latest::Xcm; pub trait RecordXcm { /// Whether or not we should record incoming XCMs. fn should_record() -> bool; + /// Enable or disable recording. + fn set_record_xcm(enabled: bool); + /// Get recorded XCM. + fn recorded_xcm() -> Xcm<()>; /// Record `xcm`. fn record(xcm: Xcm<()>); } @@ -31,5 +35,11 @@ impl RecordXcm for () { false } + fn set_record_xcm(_: bool) {} + + fn recorded_xcm() -> Xcm<()> { + Xcm::default() + } + fn record(_: Xcm<()>) {} } diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 12db53cbc27e..2673aa50b803 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -456,7 +456,7 @@ sp_api::mock_impl_runtime_apis! { impl XcmDryRunApi for RuntimeApi { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { // We want to record the XCM that's executed, so we can return it. - pallet_xcm::ShouldRecordXcm::::put(true); + pallet_xcm::set_record_xcm(true); let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_extrinsic", @@ -466,7 +466,7 @@ sp_api::mock_impl_runtime_apis! { XcmDryRunApiError::InvalidExtrinsic })?; // Nothing gets committed to storage in runtime APIs, so there's no harm in leaving the flag as true. - let local_xcm = pallet_xcm::RecordedXcm::::get().unwrap_or(Xcm(Vec::new())); + let local_xcm = pallet_xcm::recorded_xcm(); let forwarded_messages = sent_xcm() .into_iter() .map(|(location, message)| ( From 9be11462d80230a4065118245ac034acda87a136 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 17:52:21 +0200 Subject: [PATCH 50/61] chore(xcm-dry-run-api): change program and message to xcm --- .../src/tests/xcm_fee_estimation.rs | 6 +++--- .../assets/asset-hub-westend/src/lib.rs | 10 +++++----- .../runtimes/testing/penpal/src/lib.rs | 10 +++++----- polkadot/runtime/westend/src/lib.rs | 10 +++++----- .../xcm-fee-payment-runtime-api/src/dry_run.rs | 8 ++++---- .../tests/fee_estimation.rs | 18 +++++++++--------- .../xcm-fee-payment-runtime-api/tests/mock.rs | 10 +++++----- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index cd3677058396..e3e863aae474 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -56,7 +56,7 @@ fn teleport_relay_system_para_works() { let sender = Alice; // Is the same as `WestendSender`. let extrinsic = construct_extrinsic_westend(sender, call); let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); - let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; + let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; remote_message = messages_to_query[0].clone(); let delivery_fees = Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) @@ -155,7 +155,7 @@ fn multi_hop_works() { let sender = Alice; // Same as `PenpalASender`. let extrinsic = construct_extrinsic_penpal(sender, call); let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); - let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; + let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; remote_message = messages_to_query[0].clone(); let delivery_fees = Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) @@ -185,7 +185,7 @@ fn multi_hop_works() { let result = Runtime::dry_run_xcm(sender_as_seen_by_relay.clone().into(), xcm_program) .unwrap(); - let (destination_to_query, messages_to_query) = &result.forwarded_messages[0]; + let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; // There's actually two messages here. // One created when the message we sent from PenpalA arrived and was executed. // The second one when we dry-run the xcm. 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 ab61492bfcaa..171bb6e160e8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1364,11 +1364,11 @@ impl_runtime_apis! { XcmDryRunApiError::InvalidExtrinsic })?; let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); - let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_program: VersionedXcm::<()>::V4(local_xcm), - forwarded_messages, + local_xcm: VersionedXcm::<()>::V4(local_xcm), + forwarded_xcms, emitted_events: events, execution_result: result, }) @@ -1402,10 +1402,10 @@ impl_runtime_apis! { Weight::MAX, // Max limit available for execution. Weight::zero(), ); - let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(XcmDryRunEffects { - forwarded_messages, + forwarded_xcms, emitted_events: events, execution_result: result, }) diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index a19c57717058..e20541a35e17 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -890,11 +890,11 @@ impl_runtime_apis! { XcmDryRunApiError::InvalidExtrinsic })?; let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); - let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_program: VersionedXcm::<()>::V4(local_xcm), - forwarded_messages, + local_xcm: VersionedXcm::<()>::V4(local_xcm), + forwarded_xcms, emitted_events: events, execution_result: result, }) @@ -928,10 +928,10 @@ impl_runtime_apis! { Weight::MAX, // Max limit. Weight::zero(), ); - let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(XcmDryRunEffects { - forwarded_messages, + forwarded_xcms, emitted_events: events, execution_result: result, }) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 2ec4a98d0e99..e36d6f5c216c 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2241,11 +2241,11 @@ sp_api::impl_runtime_apis! { XcmDryRunApiError::InvalidExtrinsic })?; let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); - let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_program: VersionedXcm::<()>::V4(local_xcm), - forwarded_messages, + local_xcm: VersionedXcm::<()>::V4(local_xcm), + forwarded_xcms, emitted_events: events, execution_result: result, }) @@ -2277,10 +2277,10 @@ sp_api::impl_runtime_apis! { Weight::MAX, // Max limit available for execution. Weight::zero(), ); - let forwarded_messages = xcm_config::XcmRouter::get_messages(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(XcmDryRunEffects { - forwarded_messages, + forwarded_xcms, emitted_events: events, execution_result: result, }) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index 293a038b5e72..71a89807250d 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -31,10 +31,10 @@ pub struct ExtrinsicDryRunEffects { pub execution_result: DispatchResult, /// The list of events fired by the extrinsic. pub emitted_events: Vec, - /// The local XCM program that was attempted to be executed, if any. - pub local_program: VersionedXcm<()>, + /// The local XCM that was attempted to be executed, if any. + pub local_xcm: VersionedXcm<()>, /// The list of XCMs that were queued for sending. - pub forwarded_messages: Vec<(VersionedLocation, Vec>)>, + pub forwarded_xcms: Vec<(VersionedLocation, Vec>)>, } /// Effects of dry-running an XCM program. @@ -45,7 +45,7 @@ pub struct XcmDryRunEffects { /// List of events fired by the XCM program execution. pub emitted_events: Vec, /// List of queued messages for sending. - pub forwarded_messages: Vec<(VersionedLocation, Vec>)>, + pub forwarded_xcms: Vec<(VersionedLocation, Vec>)>, } sp_api::decl_runtime_apis! { diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index f4e2ec10fb3b..2da85c9406a7 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -68,7 +68,7 @@ fn fee_estimation_for_teleport() { runtime_api.dry_run_extrinsic(H256::zero(), extrinsic).unwrap().unwrap(); assert_eq!( - dry_run_effects.local_program, + dry_run_effects.local_xcm, VersionedXcm::V4( Xcm::builder_unsafe() .withdraw_asset((Parent, 20u128)) @@ -87,7 +87,7 @@ fn fee_estimation_for_teleport() { .deposit_asset(AllCounted(2), [0u8; 32]) .build(); assert_eq!( - dry_run_effects.forwarded_messages, + dry_run_effects.forwarded_xcms, vec![( VersionedLocation::V4(send_destination.clone()), vec![VersionedXcm::V4(send_message.clone())], @@ -142,12 +142,12 @@ fn fee_estimation_for_teleport() { // Weighing the local program is not relevant for extrinsics that already // take this weight into account. // In this case, we really only care about delivery fees. - let local_program = dry_run_effects.local_program; + let local_xcm = dry_run_effects.local_xcm; // We get a double result since the actual call returns a result and the runtime api returns // results. let weight = runtime_api - .query_xcm_weight(H256::zero(), local_program.clone()) + .query_xcm_weight(H256::zero(), local_xcm.clone()) .unwrap() .unwrap(); assert_eq!(weight, Weight::from_parts(400, 40)); @@ -161,9 +161,9 @@ fn fee_estimation_for_teleport() { .unwrap(); assert_eq!(execution_fees, 440); - let mut forwarded_messages_iter = dry_run_effects.forwarded_messages.into_iter(); + let mut forwarded_xcms_iter = dry_run_effects.forwarded_xcms.into_iter(); - let (destination, remote_messages) = forwarded_messages_iter.next().unwrap(); + let (destination, remote_messages) = forwarded_xcms_iter.next().unwrap(); let remote_message = &remote_messages[0]; let delivery_fees = runtime_api @@ -232,7 +232,7 @@ fn dry_run_reserve_asset_transfer() { runtime_api.dry_run_extrinsic(H256::zero(), extrinsic).unwrap().unwrap(); assert_eq!( - dry_run_effects.local_program, + dry_run_effects.local_xcm, VersionedXcm::V4( Xcm::builder_unsafe() .withdraw_asset((Parent, 100u128)) @@ -251,7 +251,7 @@ fn dry_run_reserve_asset_transfer() { .deposit_asset(AllCounted(1), [0u8; 32]) .build(); assert_eq!( - dry_run_effects.forwarded_messages, + dry_run_effects.forwarded_xcms, vec![( VersionedLocation::V4(send_destination.clone()), vec![VersionedXcm::V4(send_message.clone())], @@ -339,7 +339,7 @@ fn dry_run_xcm() { .unwrap() .unwrap(); assert_eq!( - dry_run_effects.forwarded_messages, + dry_run_effects.forwarded_xcms, vec![( VersionedLocation::V4((Parent, Parachain(2100)).into()), vec![VersionedXcm::V4( diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 2673aa50b803..44983deaee0c 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -467,7 +467,7 @@ sp_api::mock_impl_runtime_apis! { })?; // Nothing gets committed to storage in runtime APIs, so there's no harm in leaving the flag as true. let local_xcm = pallet_xcm::recorded_xcm(); - let forwarded_messages = sent_xcm() + let forwarded_xcms = sent_xcm() .into_iter() .map(|(location, message)| ( VersionedLocation::V4(location), @@ -475,8 +475,8 @@ sp_api::mock_impl_runtime_apis! { )).collect(); let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_program: VersionedXcm::<()>::V4(local_xcm), - forwarded_messages, + local_xcm: VersionedXcm::<()>::V4(local_xcm), + forwarded_xcms, emitted_events: events, execution_result: result, }) @@ -507,7 +507,7 @@ sp_api::mock_impl_runtime_apis! { max_weight, Weight::zero(), ); - let forwarded_messages = sent_xcm() + let forwarded_xcms = sent_xcm() .into_iter() .map(|(location, message)| ( VersionedLocation::V4(location), @@ -515,7 +515,7 @@ sp_api::mock_impl_runtime_apis! { )).collect(); let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); Ok(XcmDryRunEffects { - forwarded_messages, + forwarded_xcms, emitted_events: events, execution_result: result, }) From b71536e8db1a2f9392f51898221975ef6c70f024 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 17:57:13 +0200 Subject: [PATCH 51/61] fix(xcm-fee-payment-runtime-api): remove no longer used parameter --- polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 44983deaee0c..0f4df97708cc 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -455,8 +455,9 @@ sp_api::mock_impl_runtime_apis! { impl XcmDryRunApi for RuntimeApi { fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_executor::RecordXcm; // We want to record the XCM that's executed, so we can return it. - pallet_xcm::set_record_xcm(true); + pallet_xcm::Pallet::::set_record_xcm(true); let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_extrinsic", @@ -466,7 +467,7 @@ sp_api::mock_impl_runtime_apis! { XcmDryRunApiError::InvalidExtrinsic })?; // Nothing gets committed to storage in runtime APIs, so there's no harm in leaving the flag as true. - let local_xcm = pallet_xcm::recorded_xcm(); + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); let forwarded_xcms = sent_xcm() .into_iter() .map(|(location, message)| ( @@ -482,7 +483,7 @@ sp_api::mock_impl_runtime_apis! { }) } - fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm, max_weight: Weight) -> Result, XcmDryRunApiError> { + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { let origin_location: Location = origin_location.try_into().map_err(|error| { log::error!( target: "xcm::XcmDryRunApi::dry_run_xcm", @@ -504,7 +505,7 @@ sp_api::mock_impl_runtime_apis! { origin_location, xcm, &mut hash, - max_weight, + Weight::MAX, // Max limit available for execution. Weight::zero(), ); let forwarded_xcms = sent_xcm() From 6080edbe1f3ce428f77047dd57e3751fa2cb6baa Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 30 Apr 2024 18:02:07 +0200 Subject: [PATCH 52/61] fix: fmt --- .../modules/xcm-bridge-hub-router/src/lib.rs | 25 ++++++++++++------- .../modules/xcm-bridge-hub-router/src/mock.rs | 24 +++++++++++------- .../src/tests/xcm_fee_estimation.rs | 3 +-- .../xcm/xcm-builder/src/universal_exports.rs | 2 +- polkadot/xcm/xcm-executor/src/lib.rs | 4 +-- .../tests/fee_estimation.rs | 6 ++--- 6 files changed, 37 insertions(+), 27 deletions(-) diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index 2a236a680a30..607394603466 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -652,18 +652,25 @@ mod tests { )); assert_eq!( XcmBridgeHubRouter::get_messages(), - vec![ - ( - VersionedLocation::V4((Parent, Parachain(1002)).into()), - vec![VersionedXcm::V4(Xcm::builder() + vec![( + VersionedLocation::V4((Parent, Parachain(1002)).into()), + vec![VersionedXcm::V4( + Xcm::builder() .withdraw_asset((Parent, 1_002_000)) .buy_execution((Parent, 1_002_000), Unlimited) - .set_appendix(Xcm::builder_unsafe().deposit_asset(AllCounted(1), (Parent, Parachain(1000))).build()) - .export_message(Kusama, Parachain(1000), Xcm::builder_unsafe().clear_origin().build()) + .set_appendix( + Xcm::builder_unsafe() + .deposit_asset(AllCounted(1), (Parent, Parachain(1000))) + .build() + ) + .export_message( + Kusama, + Parachain(1000), + Xcm::builder_unsafe().clear_origin().build() + ) .build() - )], - ), - ], + )], + ),], ); }); } diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index 28403feb8ac0..805a28dfb95f 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/mock.rs @@ -18,17 +18,17 @@ use crate as pallet_xcm_bridge_hub_router; -use sp_std::cell::RefCell; -use codec::Encode; use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; +use codec::Encode; use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{Contains, Equals}, }; use frame_system::EnsureRoot; use sp_runtime::{traits::ConstU128, BuildStorage}; +use sp_std::cell::RefCell; use xcm::prelude::*; -use xcm_builder::{NetworkExportTable, NetworkExportTableItem, InspectMessageQueues}; +use xcm_builder::{InspectMessageQueues, NetworkExportTable, NetworkExportTableItem}; pub type AccountId = u64; type Block = frame_system::mocking::MockBlock; @@ -133,12 +133,18 @@ impl SendXcm for TestToBridgeHubSender { impl InspectMessageQueues for TestToBridgeHubSender { fn get_messages() -> Vec<(VersionedLocation, Vec>)> { - SENT_XCM.with(|q| (*q.borrow()).clone().iter().map(|(location, message)| { - ( - VersionedLocation::V4(location.clone()), - vec![VersionedXcm::V4(message.clone())], - ) - }).collect()) + SENT_XCM.with(|q| { + (*q.borrow()) + .clone() + .iter() + .map(|(location, message)| { + ( + VersionedLocation::V4(location.clone()), + vec![VersionedXcm::V4(message.clone())], + ) + }) + .collect() + }) } } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index e3e863aae474..57b8b5570e64 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -183,8 +183,7 @@ fn multi_hop_works() { // Now we get the delivery fees to the final destination. let result = - Runtime::dry_run_xcm(sender_as_seen_by_relay.clone().into(), xcm_program) - .unwrap(); + Runtime::dry_run_xcm(sender_as_seen_by_relay.clone().into(), xcm_program).unwrap(); let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; // There's actually two messages here. // One created when the message we sent from PenpalA arrived and was executed. diff --git a/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs index 6f5929b29e20..04ceb7e51688 100644 --- a/polkadot/xcm/xcm-builder/src/universal_exports.rs +++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs @@ -16,12 +16,12 @@ //! Traits and utilities to help with origin mutation and bridging. +use crate::InspectMessageQueues; use frame_support::{ensure, traits::Get}; use parity_scale_codec::{Decode, Encode}; use sp_std::{convert::TryInto, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::{validate_export, ExportXcm}; -use crate::InspectMessageQueues; use SendError::*; /// Returns the network ID and consensus location within that network of the remote diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 54a50ddb7dab..e0b8a8a9c73e 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -33,8 +33,8 @@ use traits::{ validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, Enact, ExportXcm, FeeManager, FeeReason, HandleHrmpChannelAccepted, HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, - Properties, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, - WeightTrader, XcmAssetTransfers, + Properties, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, + XcmAssetTransfers, }; pub use traits::RecordXcm; diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 2da85c9406a7..76407751cc0d 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -146,10 +146,8 @@ fn fee_estimation_for_teleport() { // We get a double result since the actual call returns a result and the runtime api returns // results. - let weight = runtime_api - .query_xcm_weight(H256::zero(), local_xcm.clone()) - .unwrap() - .unwrap(); + let weight = + runtime_api.query_xcm_weight(H256::zero(), local_xcm.clone()).unwrap().unwrap(); assert_eq!(weight, Weight::from_parts(400, 40)); let execution_fees = runtime_api .query_weight_to_asset_fee( From 8c9a090346ba211c436f3ba0cfdb28273184b6e4 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 1 May 2024 10:44:35 +0200 Subject: [PATCH 53/61] Update cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs --- cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs | 1 - 1 file changed, 1 deletion(-) 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 171bb6e160e8..e2f04095fb90 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1321,7 +1321,6 @@ impl_runtime_apis! { if !matches!(xcm_version, 3 | 4) { return Err(XcmPaymentApiError::UnhandledXcmVersion); } - // TODO: For now only WND. Ok([VersionedAssetId::V4(xcm_config::WestendLocation::get().into())] .into_iter() .filter_map(|asset| asset.into_version(xcm_version).ok()) From 720f0c0fc302a70a30eeb3aa302cf11d6b8f14e6 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Mon, 6 May 2024 19:12:33 +0200 Subject: [PATCH 54/61] chore: address feedback --- .../modules/xcm-bridge-hub-router/src/mock.rs | 3 +-- .../src/tests/xcm_fee_estimation.rs | 17 +++++++---------- .../assets/asset-hub-westend/src/lib.rs | 2 +- .../runtimes/testing/penpal/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- polkadot/xcm/pallet-xcm/src/lib.rs | 4 ++-- .../xcm/xcm-executor/src/traits/record_xcm.rs | 7 ++++--- .../xcm-fee-payment-runtime-api/src/dry_run.rs | 2 +- 8 files changed, 18 insertions(+), 21 deletions(-) diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index 805a28dfb95f..3e2c1bb369cb 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/mock.rs @@ -104,7 +104,7 @@ pub struct TestToBridgeHubSender; impl TestToBridgeHubSender { pub fn is_message_sent() -> bool { - frame_support::storage::unhashed::get_or_default(b"TestToBridgeHubSender.Sent") + !Self::get_messages().is_empty() } } @@ -124,7 +124,6 @@ impl SendXcm for TestToBridgeHubSender { } fn deliver(pair: Self::Ticket) -> Result { - frame_support::storage::unhashed::put(b"TestToBridgeHubSender.Sent", &true); let hash = fake_message_hash(&pair.1); SENT_XCM.with(|q| q.borrow_mut().push(pair)); Ok(hash) diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index 57b8b5570e64..aeec9b44dab4 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -13,8 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Tests to ensure correct XCM fee estimation between Asset Hub Westend and the Westend relay -//! chain. +//! Tests to ensure correct XCM fee estimation for cross-chain asset transfers. use crate::imports::*; @@ -25,8 +24,6 @@ use xcm_fee_payment_runtime_api::{ fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, }; -type RelayToAssetHubTest = Test; - /// We are able to dry-run and estimate the fees for a teleport between relay and system para. /// Scenario: Alice on Westend relay chain wants to teleport WND to Asset Hub. /// We want to know the fees using the `XcmDryRunApi` and `XcmPaymentApi`. @@ -56,13 +53,14 @@ fn teleport_relay_system_para_works() { let sender = Alice; // Is the same as `WestendSender`. let extrinsic = construct_extrinsic_westend(sender, call); let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); + assert_eq!(result.forwarded_xcms.len(), 1); let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; + assert_eq!(messages_to_query.len(), 1); remote_message = messages_to_query[0].clone(); let delivery_fees = Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) .unwrap(); delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); - assert_eq!(delivery_fees_amount, 39_700_000_000); }); // This is set in the AssetHubWestend closure. @@ -81,7 +79,7 @@ fn teleport_relay_system_para_works() { receiver: AssetHubWestendReceiver::get(), // Bob in Asset Hub. args: TestArgs::new_relay(destination, beneficiary_id, teleport_amount), }; - let mut test = RelayToAssetHubTest::new(test_args); + let mut test = RelayToSystemParaTest::new(test_args); let sender_balance_before = test.sender.balance; let receiver_balance_before = test.receiver.balance; @@ -155,13 +153,14 @@ fn multi_hop_works() { let sender = Alice; // Same as `PenpalASender`. let extrinsic = construct_extrinsic_penpal(sender, call); let result = Runtime::dry_run_extrinsic(extrinsic).unwrap(); + assert_eq!(result.forwarded_xcms.len(), 1); let (destination_to_query, messages_to_query) = &result.forwarded_xcms[0]; + assert_eq!(messages_to_query.len(), 1); remote_message = messages_to_query[0].clone(); let delivery_fees = Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) .unwrap(); delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); - assert_eq!(delivery_fees_amount, 31_180_000_000); }); // This is set in the Westend closure. @@ -197,7 +196,6 @@ fn multi_hop_works() { ) .unwrap(); intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); - assert_eq!(intermediate_delivery_fees_amount, 39_700_000_000); }); // Get the final execution fees in the destination. @@ -209,7 +207,6 @@ fn multi_hop_works() { final_execution_fees = Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) .unwrap(); - assert_eq!(final_execution_fees, 3_276_800_000); }); // Dry-running is done. @@ -286,7 +283,7 @@ fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 { amount } -fn transfer_assets(test: RelayToAssetHubTest) -> DispatchResult { +fn transfer_assets(test: RelayToSystemParaTest) -> DispatchResult { ::XcmPallet::transfer_assets( test.signed_origin, bx!(test.args.dest.into()), 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 e2f04095fb90..da05b7803ed0 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1366,7 +1366,7 @@ impl_runtime_apis! { let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: VersionedXcm::<()>::V4(local_xcm), + local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), forwarded_xcms, emitted_events: events, execution_result: result, diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index e20541a35e17..663037ab9ec5 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -893,7 +893,7 @@ impl_runtime_apis! { let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: VersionedXcm::<()>::V4(local_xcm), + local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), forwarded_xcms, emitted_events: events, execution_result: result, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index aa5b2050214f..31d389764bdd 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2244,7 +2244,7 @@ sp_api::impl_runtime_apis! { let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: VersionedXcm::<()>::V4(local_xcm), + local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), forwarded_xcms, emitted_events: events, execution_result: result, diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 1563da88aea3..37fc121ba217 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -3135,8 +3135,8 @@ impl xcm_executor::traits::RecordXcm for Pallet { ShouldRecordXcm::::put(enabled); } - fn recorded_xcm() -> Xcm<()> { - RecordedXcm::::get().unwrap_or(Xcm::default()) + fn recorded_xcm() -> Option> { + RecordedXcm::::get() } fn record(xcm: Xcm<()>) { diff --git a/polkadot/xcm/xcm-executor/src/traits/record_xcm.rs b/polkadot/xcm/xcm-executor/src/traits/record_xcm.rs index 6090b27d9263..d0b5bf92d795 100644 --- a/polkadot/xcm/xcm-executor/src/traits/record_xcm.rs +++ b/polkadot/xcm/xcm-executor/src/traits/record_xcm.rs @@ -25,7 +25,8 @@ pub trait RecordXcm { /// Enable or disable recording. fn set_record_xcm(enabled: bool); /// Get recorded XCM. - fn recorded_xcm() -> Xcm<()>; + /// Returns `None` if no message was sent, or if recording was off. + fn recorded_xcm() -> Option>; /// Record `xcm`. fn record(xcm: Xcm<()>); } @@ -37,8 +38,8 @@ impl RecordXcm for () { fn set_record_xcm(_: bool) {} - fn recorded_xcm() -> Xcm<()> { - Xcm::default() + fn recorded_xcm() -> Option> { + None } fn record(_: Xcm<()>) {} diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs index 71a89807250d..62a422d6efeb 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/src/dry_run.rs @@ -32,7 +32,7 @@ pub struct ExtrinsicDryRunEffects { /// The list of events fired by the extrinsic. pub emitted_events: Vec, /// The local XCM that was attempted to be executed, if any. - pub local_xcm: VersionedXcm<()>, + pub local_xcm: Option>, /// The list of XCMs that were queued for sending. pub forwarded_xcms: Vec<(VersionedLocation, Vec>)>, } From ccf6a70b6d1945e3cbccab71655650f5d0260e33 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 7 May 2024 21:24:48 +0200 Subject: [PATCH 55/61] fix: merge conflicts --- .../assets/asset-hub-westend/src/lib.rs | 77 +++++-------------- 1 file changed, 20 insertions(+), 57 deletions(-) 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 0464e634396e..300e050bb53e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -98,18 +98,12 @@ use xcm::latest::prelude::{ Asset, Fungible, Here, InteriorLocation, Junction, Junction::*, Location, NetworkId, NonFungible, Parent, ParentThen, Response, XCM_VERSION, }; -use xcm::{ - latest::prelude::AssetId, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, - VersionedXcm, -}; -use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; use xcm_fee_payment_runtime_api::{ dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, fees::Error as XcmPaymentApiError, }; -use crate::xcm_config::ForeignCreatorsSovereignAccountOf; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; impl_opaque_keys! { @@ -1326,24 +1320,32 @@ impl_runtime_apis! { impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { - if !matches!(xcm_version, 3 | 4) { - return Err(XcmPaymentApiError::UnhandledXcmVersion); - } - Ok([VersionedAssetId::V4(xcm_config::WestendLocation::get().into())] + let acceptable = vec![ + // native token + VersionedAssetId::from(AssetId(xcm_config::WestendLocation::get())) + ]; + + Ok(acceptable .into_iter() .filter_map(|asset| asset.into_version(xcm_version).ok()) .collect()) } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let local_asset = VersionedAssetId::V4(xcm_config::WestendLocation::get().into()); - let asset = asset - .into_version(4) - .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; - - if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); } - - Ok(WeightToFee::weight_to_fee(&weight)) + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::WestendLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { @@ -1482,45 +1484,6 @@ impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { - fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { - let acceptable = vec![ - // native token - VersionedAssetId::from(AssetId(xcm_config::WestendLocation::get())) - ]; - - Ok(acceptable - .into_iter() - .filter_map(|asset| asset.into_version(xcm_version).ok()) - .collect()) - } - - fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - match asset.try_as::() { - Ok(asset_id) if asset_id.0 == xcm_config::WestendLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } - } - - fn query_xcm_weight(message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_xcm_weight(message) - } - - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) - } - } - impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) From a30de0073ff3879e691ae0243f8e14af2e3c21f9 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 7 May 2024 21:58:09 +0200 Subject: [PATCH 56/61] fix(xcm-fee-payment-runtime-api): deal with local xcm being an option now --- .../tests/fee_estimation.rs | 10 +++++----- polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs index 76407751cc0d..7a9bfa4a7968 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/fee_estimation.rs @@ -69,14 +69,14 @@ fn fee_estimation_for_teleport() { assert_eq!( dry_run_effects.local_xcm, - VersionedXcm::V4( + Some(VersionedXcm::V4( Xcm::builder_unsafe() .withdraw_asset((Parent, 20u128)) .burn_asset((Parent, 20u128)) .withdraw_asset((Here, 100u128)) .burn_asset((Here, 100u128)) .build() - ), + )), ); let send_destination = Location::new(1, [Parachain(1000)]); let send_message = Xcm::<()>::builder_unsafe() @@ -142,7 +142,7 @@ fn fee_estimation_for_teleport() { // Weighing the local program is not relevant for extrinsics that already // take this weight into account. // In this case, we really only care about delivery fees. - let local_xcm = dry_run_effects.local_xcm; + let local_xcm = dry_run_effects.local_xcm.unwrap(); // We get a double result since the actual call returns a result and the runtime api returns // results. @@ -231,12 +231,12 @@ fn dry_run_reserve_asset_transfer() { assert_eq!( dry_run_effects.local_xcm, - VersionedXcm::V4( + Some(VersionedXcm::V4( Xcm::builder_unsafe() .withdraw_asset((Parent, 100u128)) .burn_asset((Parent, 100u128)) .build() - ), + )), ); // In this case, the transfer type is `DestinationReserve`, so the remote xcm just withdraws diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 0f4df97708cc..3e364b6f2227 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -476,7 +476,7 @@ sp_api::mock_impl_runtime_apis! { )).collect(); let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: VersionedXcm::<()>::V4(local_xcm), + local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), forwarded_xcms, emitted_events: events, execution_result: result, From 5a2cbf5610739b498c1c654c4d8cc19767598aeb Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 7 May 2024 21:58:48 +0200 Subject: [PATCH 57/61] feat(rococo-runtime): add XcmDryRunApi --- polkadot/runtime/rococo/src/lib.rs | 65 +++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index ede36eeacb61..bb4577d7171e 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -134,7 +134,10 @@ use governance::{ pallet_custom_origins, AuctionAdmin, Fellows, GeneralAdmin, LeaseAdmin, Treasurer, TreasurySpender, }; -use xcm_fee_payment_runtime_api::fees::Error as XcmPaymentApiError; +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; #[cfg(test)] mod tests; @@ -1803,6 +1806,66 @@ sp_api::impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm_executor::RecordXcm; + pallet_xcm::Pallet::::set_record_xcm(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let xcm: Xcm = xcm.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let result = xcm_executor::XcmExecutor::::prepare_and_execute( + origin_location, + xcm, + &mut hash, + Weight::MAX, // Max limit available for execution. + Weight::zero(), + ); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + } + impl sp_api::Metadata for Runtime { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) From a5c4c67e2483c7a8b0c0ffee9b71a5bca34f369d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 7 May 2024 21:59:02 +0200 Subject: [PATCH 58/61] feat(asset-hub-rococo-runtime): add XcmDryRunApi --- .../assets/asset-hub-rococo/src/lib.rs | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 383751578e57..6b70aac53f17 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -100,7 +100,10 @@ use xcm::{ latest::prelude::{AssetId, BodyId}, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, }; -use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError; +use xcm_fee_payment_runtime_api::{ + dry_run::{Error as XcmDryRunApiError, ExtrinsicDryRunEffects, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; @@ -1281,7 +1284,7 @@ impl_runtime_apis! { } } - impl xcm_fee_payment_runtime_api::XcmPaymentApi for Runtime { + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { let acceptable = vec![ // native token @@ -1320,6 +1323,70 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::dry_run::XcmDryRunApi for Runtime { + fn dry_run_extrinsic(extrinsic: ::Extrinsic) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm_executor::RecordXcm; + use xcm::prelude::*; + + pallet_xcm::Pallet::::set_record_xcm(true); + let result = Executive::apply_extrinsic(extrinsic).map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_extrinsic", + "Applying extrinsic failed with error {:?}", + error, + ); + XcmDryRunApiError::InvalidExtrinsic + })?; + let local_xcm = pallet_xcm::Pallet::::recorded_xcm(); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(ExtrinsicDryRunEffects { + local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + + fn dry_run_xcm(origin_location: VersionedLocation, program: VersionedXcm) -> Result, XcmDryRunApiError> { + use xcm_builder::InspectMessageQueues; + use xcm::prelude::*; + + let origin_location: Location = origin_location.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Location version conversion failed with error: {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let program: Xcm = program.try_into().map_err(|error| { + log::error!( + target: "xcm::XcmDryRunApi::dry_run_xcm", + "Xcm version conversion failed with error {:?}", + error, + ); + XcmDryRunApiError::VersionedConversionFailed + })?; + let mut hash = program.using_encoded(sp_core::hashing::blake2_256); + let result = xcm_executor::XcmExecutor::::prepare_and_execute( + origin_location, + program, + &mut hash, + Weight::MAX, // Max limit available for execution. + Weight::zero(), + ); + let forwarded_xcms = xcm_config::XcmRouter::get_messages(); + let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); + Ok(XcmDryRunEffects { + forwarded_xcms, + emitted_events: events, + execution_result: result, + }) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) From 0a42dea78b4ca3e9c953dcf21c52f2eb77817da8 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 7 May 2024 22:06:04 +0200 Subject: [PATCH 59/61] fix(asset-hub-rococo-runtime): feature propagation --- cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 64abedbaac78..88dfec014d72 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -138,6 +138,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks" ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", From 93c5b83517b51108f34e7d2814b3bfbff0c4e48d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 7 May 2024 22:08:50 +0200 Subject: [PATCH 60/61] fix(asset-hub-rococo): taplo --- cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 88dfec014d72..888193c5c6ea 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -138,7 +138,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", - "xcm-fee-payment-runtime-api/runtime-benchmarks" + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", From 7edc8b80c904e6d106898045d9d5ae93b6936351 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 8 May 2024 02:40:12 +0200 Subject: [PATCH 61/61] fix: clippy --- cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs | 2 +- cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs | 2 +- cumulus/parachains/runtimes/testing/penpal/src/lib.rs | 2 +- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 6b70aac53f17..f81a107fae05 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -1342,7 +1342,7 @@ impl_runtime_apis! { let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), forwarded_xcms, emitted_events: events, execution_result: result, 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 300e050bb53e..b5c3ed5053c4 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1376,7 +1376,7 @@ impl_runtime_apis! { let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), forwarded_xcms, emitted_events: events, execution_result: result, diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 663037ab9ec5..582154fec6d2 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -893,7 +893,7 @@ impl_runtime_apis! { let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), forwarded_xcms, emitted_events: events, execution_result: result, diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index bb4577d7171e..22e6183e5946 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1823,7 +1823,7 @@ sp_api::impl_runtime_apis! { let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), forwarded_xcms, emitted_events: events, execution_result: result, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 206dd975a157..cae12ab49c02 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2257,7 +2257,7 @@ sp_api::impl_runtime_apis! { let forwarded_xcms = xcm_config::XcmRouter::get_messages(); let events: Vec = System::read_events_no_consensus().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), forwarded_xcms, emitted_events: events, execution_result: result, diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index 3e364b6f2227..d7b18d90a501 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -476,7 +476,7 @@ sp_api::mock_impl_runtime_apis! { )).collect(); let events: Vec = System::events().iter().map(|record| record.event.clone()).collect(); Ok(ExtrinsicDryRunEffects { - local_xcm: local_xcm.map(|xcm| VersionedXcm::<()>::V4(xcm)), + local_xcm: local_xcm.map(VersionedXcm::<()>::V4), forwarded_xcms, emitted_events: events, execution_result: result,