diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs index a41be6fa9ca3..a3cbd24128a8 100644 --- a/cumulus/pallets/xcmp-queue/src/mock.rs +++ b/cumulus/pallets/xcmp-queue/src/mock.rs @@ -32,7 +32,9 @@ use sp_runtime::{ use xcm::prelude::*; #[allow(deprecated)] use xcm_builder::CurrencyAdapter; -use xcm_builder::{FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset}; +use xcm_builder::{ + FixedWeightBounds, FrameTransactionalProcessor, IsConcrete, NativeAsset, ParentIsPreset, +}; use xcm_executor::traits::ConvertOrigin; type Block = frame_system::mocking::MockBlock; @@ -175,6 +177,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type XcmRouter = ( diff --git a/cumulus/parachain-template/runtime/src/xcm_config.rs b/cumulus/parachain-template/runtime/src/xcm_config.rs index 7d1a748819ce..2ebc6aa82b87 100644 --- a/cumulus/parachain-template/runtime/src/xcm_config.rs +++ b/cumulus/parachain-template/runtime/src/xcm_config.rs @@ -16,11 +16,11 @@ use xcm::latest::prelude::*; use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, - NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WithComputedOrigin, WithUniqueTopic, + DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, + FrameTransactionalProcessor, IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::XcmExecutor; @@ -139,6 +139,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. 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 a33fea6dffde..9ea71586174e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -1565,6 +1565,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(TokenLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } 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 2826c18f3f7c..a33ab747e0dc 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 @@ -55,7 +55,7 @@ use xcm_builder::{ SignedToAccountId32, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, - XcmFeeToAccount, + XcmFeeToAccount, FrameTransactionalProcessor, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -595,6 +595,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM multilocation. 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 eda6949bd3de..b1b850983e1f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1637,6 +1637,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(WestendLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } 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 cb2fedeb146f..2f9a9e8ebb8a 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 @@ -49,6 +49,7 @@ use xcm_builder::{ DenyThenTry, DescribeFamily, DescribePalletTerminal, EnsureXcmOrigin, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, NetworkExportTableItem, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + FrameTransactionalProcessor, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, @@ -611,6 +612,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index c1b176d40fc1..274d6bf11eb9 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -1201,6 +1201,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(TokenLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } 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 fc97a3ed89a6..92af7e65f61e 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 @@ -57,11 +57,12 @@ use xcm::latest::prelude::*; use xcm_builder::{ deposit_or_burn_fee, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, HandleFee, - IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeToAccount, + CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, + FrameTransactionalProcessor, HandleFee, IsConcrete, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeToAccount, }; use xcm_executor::{ traits::{FeeManager, FeeReason, FeeReason::Export, TransactAsset, WithOriginFilter}, @@ -326,6 +327,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type PriceForParentDelivery = diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 7de5b9af46cd..6a0a0b39441f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -918,6 +918,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(WestendLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } 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 397019190f3f..9a23a9297a5c 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 @@ -43,8 +43,8 @@ use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, IsConcrete, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, IsConcrete, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, @@ -265,6 +265,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } 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 2e64127d6a1d..aef9d2a68282 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs @@ -41,12 +41,12 @@ use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocatableAssetId, - OriginToPluralityVoice, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, - XcmFeeToAccount, + DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, IsConcrete, + LocatableAssetId, OriginToPluralityVoice, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -290,6 +290,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM multilocation. 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 569ca6e587c5..e02863f8e3f0 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -42,11 +42,11 @@ use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, XcmFeeToAccount, + DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, IsConcrete, + NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -198,6 +198,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM multilocation. diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index c381d78b57f5..b73a7b18eb02 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -804,6 +804,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(RocRelayLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } 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 00bbe5b5037f..3a3b089df694 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs @@ -42,8 +42,8 @@ use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, IsConcrete, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, IsConcrete, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, @@ -238,6 +238,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM multilocation. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 88f94b445a26..9c066e0654ed 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -795,6 +795,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(WndRelayLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } 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 59d76d10d902..472a79967bfc 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs @@ -42,8 +42,8 @@ use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, EnsureXcmOrigin, IsConcrete, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, IsConcrete, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, XcmFeeToAccount, @@ -239,6 +239,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM multilocation. 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 5ebb0ade1231..fb24162292e2 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/xcm_config.rs @@ -24,8 +24,8 @@ use frame_support::{ }; use xcm::latest::prelude::*; use xcm_builder::{ - AllowExplicitUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset, - SovereignSignedViaLocation, + AllowExplicitUnpaidExecutionFrom, FixedWeightBounds, FrameTransactionalProcessor, + ParentAsSuperuser, ParentIsPreset, SovereignSignedViaLocation, }; parameter_types! { @@ -84,6 +84,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index 64a03fdaf606..e9fc9bd96286 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -790,6 +790,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(RelayLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } 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 7a2f28aa813e..0cd33047334e 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs @@ -39,8 +39,8 @@ use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, DescribeTerminus, EnsureXcmOrigin, HashedDescription, IsConcrete, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + DenyThenTry, DescribeTerminus, EnsureXcmOrigin, FrameTransactionalProcessor, HashedDescription, + IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, @@ -266,6 +266,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM multilocation. Forms the basis for local origins diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 3c831e596b5b..27cada6b95c5 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -790,6 +790,13 @@ impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(RelayLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } 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 0a51cf72d5b4..7605801993ac 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs @@ -39,8 +39,8 @@ use xcm_builder::CurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, DescribeTerminus, EnsureXcmOrigin, HashedDescription, IsConcrete, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + DenyThenTry, DescribeTerminus, EnsureXcmOrigin, FrameTransactionalProcessor, HashedDescription, + IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, @@ -270,6 +270,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Converts a local signed origin into an XCM multilocation. 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 ff773ca78161..c878bb6a70cd 100644 --- a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -84,6 +84,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = (); } 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 ed405aeddb38..32c08032c137 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -50,11 +50,11 @@ use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, - EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, - NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WithComputedOrigin, WithUniqueTopic, + EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungiblesAdapter, IsConcrete, + LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::{traits::JustTry, XcmExecutor}; @@ -346,6 +346,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 31216943c74a..b3210d744ce4 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -75,7 +75,8 @@ use parachains_common::{ }; use xcm_builder::{ AllowKnownQueryResponses, AllowSubscriptionsFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, - FungiblesAdapter, LocalMint, TrailingSetTopicAsId, WithUniqueTopic, + FrameTransactionalProcessor, FungiblesAdapter, LocalMint, TrailingSetTopicAsId, + WithUniqueTopic, }; use xcm_executor::traits::JustTry; @@ -487,6 +488,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 03f827d7ee2f..9df70e3cd344 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -22,11 +22,13 @@ use codec::Encode; use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; use frame_support::{ + defensive, traits::{ tokens::{fungibles, fungibles::Inspect}, Get, }, weights::Weight, + CloneNoBound, }; use polkadot_runtime_common::xcm_sender::PriceForMessageDelivery; use sp_runtime::{traits::Saturating, SaturatedConversion}; @@ -101,6 +103,7 @@ struct AssetTraderRefunder { /// later refund purposes /// Important: Errors if the Trader is being called twice by 2 BuyExecution instructions /// Alternatively we could just return payment in the aforementioned case +#[derive(CloneNoBound)] pub struct TakeFirstAssetTrader< AccountId: Eq, FeeCharger: ChargeWeightInFungibles, diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index e492caddc8fc..864e54c3cac9 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -2398,6 +2398,13 @@ sp_api::impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(TokenLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { // Rococo doesn't support asset locking Err(BenchmarkError::Skip) diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index f3a2ca6f3a6c..e512baaed21c 100644 --- a/polkadot/runtime/rococo/src/xcm_config.rs +++ b/polkadot/runtime/rococo/src/xcm_config.rs @@ -42,10 +42,11 @@ use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, DescribeBodyTerminal, DescribeFamily, FixedWeightBounds, - HashedDescription, IsChildSystemParachain, IsConcrete, MintLocation, OriginToPluralityVoice, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, XcmFeeToAccount, + FrameTransactionalProcessor, HashedDescription, IsChildSystemParachain, IsConcrete, + MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -217,6 +218,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } parameter_types! { diff --git a/polkadot/runtime/test-runtime/src/xcm_config.rs b/polkadot/runtime/test-runtime/src/xcm_config.rs index ae4faecf7001..041bf9822a15 100644 --- a/polkadot/runtime/test-runtime/src/xcm_config.rs +++ b/polkadot/runtime/test-runtime/src/xcm_config.rs @@ -22,8 +22,8 @@ use frame_support::{ use frame_system::EnsureRoot; use xcm::latest::prelude::*; use xcm_builder::{ - AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, SignedAccountId32AsNative, - SignedToAccountId32, + AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, + SignedAccountId32AsNative, SignedToAccountId32, }; use xcm_executor::{ traits::{TransactAsset, WeightTrader}, @@ -78,6 +78,7 @@ impl TransactAsset for DummyAssetTransactor { } } +#[derive(Clone)] pub struct DummyWeightTrader; impl WeightTrader for DummyWeightTrader { fn new() -> Self { @@ -125,6 +126,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = super::RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } impl pallet_xcm::Config for crate::Runtime { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 167984fd9dfa..88c9b8cede34 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2420,6 +2420,13 @@ sp_api::impl_runtime_apis! { Ok((origin, ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { + id: Concrete(TokenLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { // Westend doesn't support asset locking Err(BenchmarkError::Skip) diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs index e4b4f50f663b..229dcbd88b1a 100644 --- a/polkadot/runtime/westend/src/xcm_config.rs +++ b/polkadot/runtime/westend/src/xcm_config.rs @@ -44,11 +44,11 @@ use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, - ChildParachainConvertsVia, DescribeBodyTerminal, DescribeFamily, HashedDescription, IsConcrete, - MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, - XcmFeeToAccount, + ChildParachainConvertsVia, DescribeBodyTerminal, DescribeFamily, FrameTransactionalProcessor, + HashedDescription, IsConcrete, MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -205,6 +205,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } parameter_types! { diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index 43892c31c7cd..e5c26812af4c 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -26,7 +26,7 @@ use frame_support::{ use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use xcm::latest::prelude::*; -use xcm_builder::{AllowUnpaidExecutionFrom, MintLocation}; +use xcm_builder::{AllowUnpaidExecutionFrom, FrameTransactionalProcessor, MintLocation}; type Block = frame_system::mocking::MockBlock; @@ -145,6 +145,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } impl crate::Config for Test { diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs index 50a7fe45e231..c0cb6b2f1f6d 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs @@ -125,11 +125,15 @@ benchmarks! { } refund_surplus { - let holding = T::worst_case_holding(0).into(); let mut executor = new_executor::(Default::default()); - executor.set_holding(holding); + let holding_assets = T::worst_case_holding(1); + // We can already buy execution since we'll load the holding register manually + let asset_for_fees = T::fee_asset().unwrap(); + let previous_xcm = Xcm(vec![BuyExecution { fees: asset_for_fees, weight_limit: Limited(Weight::from_parts(1337, 1337)) }]); + executor.set_holding(holding_assets.into()); executor.set_total_surplus(Weight::from_parts(1337, 1337)); executor.set_total_refunded(Weight::zero()); + executor.bench_process(previous_xcm).expect("Holding has been loaded, so we can buy execution here"); let instruction = Instruction::>::RefundSurplus; let xcm = Xcm(vec![instruction]); diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index 6efd2304e281..bb16893ea21d 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -30,7 +30,7 @@ use xcm_builder::{ Assets, TestAssetExchanger, TestAssetLocker, TestAssetTrap, TestSubscriptionService, TestUniversalAliases, }, - AliasForeignAccountId32, AllowUnpaidExecutionFrom, + AliasForeignAccountId32, AllowUnpaidExecutionFrom, FrameTransactionalProcessor, }; use xcm_executor::traits::ConvertOrigin; @@ -137,6 +137,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Aliasers; + type TransactionalProcessor = FrameTransactionalProcessor; } parameter_types! { @@ -199,6 +200,10 @@ impl generic::Config for Test { Ok((Default::default(), ticket, assets)) } + fn fee_asset() -> Result { + Ok(MultiAsset { id: Concrete(Here.into()), fun: Fungible(1_000_000) }) + } + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { let assets: MultiAsset = (Concrete(Here.into()), 100).into(); Ok((Default::default(), account_id_junction::(1).into(), assets)) diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs index 11f7bba19a98..f3741b8213c8 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs @@ -76,6 +76,10 @@ pub mod pallet { /// Return an origin, ticket, and assets that can be trapped and claimed. fn claimable_asset() -> Result<(MultiLocation, MultiLocation, MultiAssets), BenchmarkError>; + /// Asset used to pay for fees. Used to buy weight in benchmarks, for example in + /// `refund_surplus`. + fn fee_asset() -> Result; + /// Return an unlocker, owner and assets that can be locked and unlocked. fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError>; diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index 0ac4205ed949..76c41f113a7a 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -36,9 +36,9 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, DescribeAllTerminal, FixedRateOfFungible, FixedWeightBounds, - FungiblesAdapter, HashedDescription, IsConcrete, MatchedConvertedConcreteId, NoChecking, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - XcmFeeManagerFromComponents, XcmFeeToAccount, + FrameTransactionalProcessor, FungiblesAdapter, HashedDescription, IsConcrete, + MatchedConvertedConcreteId, NoChecking, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, XcmFeeManagerFromComponents, XcmFeeToAccount, }; use xcm_executor::{ traits::{Identity, JustTry}, @@ -514,6 +514,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/src/v3/multiasset.rs b/polkadot/xcm/src/v3/multiasset.rs index c8801f5a461d..971724b57cec 100644 --- a/polkadot/xcm/src/v3/multiasset.rs +++ b/polkadot/xcm/src/v3/multiasset.rs @@ -509,7 +509,7 @@ impl TryFrom for MultiAsset { /// A `Vec` of `MultiAsset`s. /// /// There are a number of invariants which the construction and mutation functions must ensure are -/// maintained: +/// maintained in order to maintain polynomial time complexity during iteration: /// - It may contain no items of duplicate asset class; /// - All items must be ordered; /// - The number of items should grow no larger than `MAX_ITEMS_IN_MULTIASSETS`. diff --git a/polkadot/xcm/src/v3/traits.rs b/polkadot/xcm/src/v3/traits.rs index 29bd40a6a2d8..6446d5ffd3cc 100644 --- a/polkadot/xcm/src/v3/traits.rs +++ b/polkadot/xcm/src/v3/traits.rs @@ -520,7 +520,7 @@ pub trait SendXcm { /// Intermediate value which connects the two phases of the send operation. type Ticket; - /// Check whether the given `_message` is deliverable to the given `_destination` and if so + /// Check whether the given `message` is deliverable to the given `destination` and if so /// determine the cost which will be paid by this chain to do so, returning a `Validated` token /// which can be used to enact delivery. /// diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index e7431ae02545..af2076c927d2 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -26,26 +26,6 @@ mod tests; #[cfg(feature = "std")] pub mod test_utils; -mod location_conversion; -#[allow(deprecated)] -pub use location_conversion::ForeignChainAliasAccount; -pub use location_conversion::{ - Account32Hash, AccountId32Aliases, AccountKey20Aliases, AliasesIntoAccountId32, - ChildParachainConvertsVia, DescribeAccountId32Terminal, DescribeAccountIdTerminal, - DescribeAccountKey20Terminal, DescribeAllTerminal, DescribeBodyTerminal, DescribeFamily, - DescribeLocation, DescribePalletTerminal, DescribeTerminus, DescribeTreasuryVoiceTerminal, - GlobalConsensusConvertsFor, GlobalConsensusParachainConvertsFor, HashedDescription, - LocalTreasuryVoiceConvertsVia, ParentIsPreset, SiblingParachainConvertsVia, -}; - -mod origin_conversion; -pub use origin_conversion::{ - BackingToPlurality, ChildParachainAsNative, ChildSystemParachainAsSuperuser, EnsureXcmOrigin, - OriginToPluralityVoice, ParentAsSuperuser, RelayChainAsNative, SiblingParachainAsNative, - SiblingSystemParachainAsSuperuser, SignedAccountId32AsNative, SignedAccountKey20AsNative, - SignedToAccountId32, SovereignSignedViaLocation, -}; - mod asset_conversion; pub use asset_conversion::{ AsPrefixedGeneralIndex, ConvertedAbstractId, ConvertedConcreteId, MatchedConvertedConcreteId, @@ -61,8 +41,11 @@ pub use barriers::{ WithComputedOrigin, }; -mod process_xcm_message; -pub use process_xcm_message::ProcessXcmMessage; +mod controller; +pub use controller::{ + Controller, ExecuteController, ExecuteControllerWeightInfo, QueryController, + QueryControllerWeightInfo, QueryHandler, SendController, SendControllerWeightInfo, +}; mod currency_adapter; #[allow(deprecated)] @@ -73,6 +56,9 @@ pub use fee_handling::{ deposit_or_burn_fee, HandleFee, XcmFeeManagerFromComponents, XcmFeeToAccount, }; +mod filter_asset_location; +pub use filter_asset_location::{AllAssets, Case, LocationWithAssetFilters, NativeAsset}; + mod fungible_adapter; pub use fungible_adapter::{FungibleAdapter, FungibleMutateAdapter, FungibleTransferAdapter}; @@ -82,14 +68,16 @@ pub use fungibles_adapter::{ LocalMint, MintLocation, NoChecking, NonLocalMint, }; -mod nonfungibles_adapter; -pub use nonfungibles_adapter::{ - NonFungiblesAdapter, NonFungiblesMutateAdapter, NonFungiblesTransferAdapter, -}; - -mod weight; -pub use weight::{ - FixedRateOfFungible, FixedWeightBounds, TakeRevenue, UsingComponents, WeightInfoBounds, +mod location_conversion; +#[allow(deprecated)] +pub use location_conversion::ForeignChainAliasAccount; +pub use location_conversion::{ + Account32Hash, AccountId32Aliases, AccountKey20Aliases, AliasesIntoAccountId32, + ChildParachainConvertsVia, DescribeAccountId32Terminal, DescribeAccountIdTerminal, + DescribeAccountKey20Terminal, DescribeAllTerminal, DescribeBodyTerminal, DescribeFamily, + DescribeLocation, DescribePalletTerminal, DescribeTerminus, DescribeTreasuryVoiceTerminal, + GlobalConsensusConvertsFor, GlobalConsensusParachainConvertsFor, HashedDescription, + LocalTreasuryVoiceConvertsVia, ParentIsPreset, SiblingParachainConvertsVia, }; mod matches_location; @@ -101,12 +89,34 @@ pub use matches_token::{IsAbstract, IsConcrete}; mod matcher; pub use matcher::{CreateMatcher, MatchXcm, Matcher}; -mod filter_asset_location; -pub use filter_asset_location::{AllAssets, Case, LocationWithAssetFilters, NativeAsset}; +mod nonfungibles_adapter; +pub use nonfungibles_adapter::{ + NonFungiblesAdapter, NonFungiblesMutateAdapter, NonFungiblesTransferAdapter, +}; + +mod origin_aliases; +pub use origin_aliases::AliasForeignAccountId32; + +mod origin_conversion; +pub use origin_conversion::{ + BackingToPlurality, ChildParachainAsNative, ChildSystemParachainAsSuperuser, EnsureXcmOrigin, + OriginToPluralityVoice, ParentAsSuperuser, RelayChainAsNative, SiblingParachainAsNative, + SiblingSystemParachainAsSuperuser, SignedAccountId32AsNative, SignedAccountKey20AsNative, + SignedToAccountId32, SovereignSignedViaLocation, +}; + +mod pay; +pub use pay::{FixedLocation, LocatableAssetId, PayAccountId32OnChainOverXcm, PayOverXcm}; + +mod process_xcm_message; +pub use process_xcm_message::ProcessXcmMessage; mod routing; pub use routing::{WithTopicSource, WithUniqueTopic}; +mod transactional; +pub use transactional::FrameTransactionalProcessor; + mod universal_exports; pub use universal_exports::{ ensure_is_remote, BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError, @@ -114,14 +124,7 @@ pub use universal_exports::{ NetworkExportTableItem, SovereignPaidRemoteExporter, UnpaidLocalExporter, UnpaidRemoteExporter, }; -mod origin_aliases; -pub use origin_aliases::AliasForeignAccountId32; - -mod pay; -pub use pay::{FixedLocation, LocatableAssetId, PayAccountId32OnChainOverXcm, PayOverXcm}; - -mod controller; -pub use controller::{ - Controller, ExecuteController, ExecuteControllerWeightInfo, QueryController, - QueryControllerWeightInfo, QueryHandler, SendController, SendControllerWeightInfo, +mod weight; +pub use weight::{ + FixedRateOfFungible, FixedWeightBounds, TakeRevenue, UsingComponents, WeightInfoBounds, }; diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs index c1b3f0cf224e..437d2df75606 100644 --- a/polkadot/xcm/xcm-builder/src/tests/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs @@ -741,6 +741,7 @@ impl Config for TestConfig { type CallDispatcher = TestCall; type SafeCallFilter = Everything; type Aliasers = AliasForeignAccountId32; + type TransactionalProcessor = (); } pub fn fungible_multi_asset(location: MultiLocation, amount: u128) -> MultiAsset { diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index 78b9284c689f..3be6a0aa9b1c 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -178,6 +178,7 @@ type OriginConverter = ( ); type Barrier = AllowUnpaidExecutionFrom; +#[derive(Clone)] pub struct DummyWeightTrader; impl WeightTrader for DummyWeightTrader { fn new() -> Self { @@ -220,6 +221,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = (); } parameter_types! { diff --git a/polkadot/xcm/xcm-builder/src/transactional.rs b/polkadot/xcm/xcm-builder/src/transactional.rs new file mode 100644 index 000000000000..ffe379b0ed41 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/transactional.rs @@ -0,0 +1,40 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use frame_support::storage::{with_transaction, TransactionOutcome}; +use sp_runtime::DispatchError; +use xcm::latest::prelude::*; +use xcm_executor::traits::ProcessTransaction; + +/// Transactional processor implementation using frame transactional layers. +pub struct FrameTransactionalProcessor; +impl ProcessTransaction for FrameTransactionalProcessor { + const IS_TRANSACTIONAL: bool = true; + + fn process(f: F) -> Result<(), XcmError> + where + F: FnOnce() -> Result<(), XcmError>, + { + with_transaction(|| -> TransactionOutcome> { + let output = f(); + match &output { + Ok(()) => TransactionOutcome::Commit(Ok(output)), + _ => TransactionOutcome::Rollback(Ok(output)), + } + }) + .map_err(|_| XcmError::ExceedsStackLimit)? + } +} diff --git a/polkadot/xcm/xcm-builder/src/weight.rs b/polkadot/xcm/xcm-builder/src/weight.rs index c16c52939a38..55e1b9694556 100644 --- a/polkadot/xcm/xcm-builder/src/weight.rs +++ b/polkadot/xcm/xcm-builder/src/weight.rs @@ -231,13 +231,14 @@ impl< Ok(unused) } - fn refund_weight(&mut self, weight: Weight, context: &XcmContext) -> Option { - log::trace!(target: "xcm::weight", "UsingComponents::refund_weight weight: {:?}, context: {:?}", weight, context); + fn refund_weight(&mut self, weight: Weight, context: &XcmContext) -> Option { + log::trace!(target: "xcm::weight", "UsingComponents::refund_weight weight: {:?}, context: {:?}, available weight: {:?}, available amount: {:?}", weight, context, self.0, self.1); let weight = weight.min(self.0); let amount = WeightToFee::weight_to_fee(&weight); self.0 -= weight; self.1 = self.1.saturating_sub(amount); let amount: u128 = amount.saturated_into(); + log::trace!(target: "xcm::weight", "UsingComponents::refund_weight amount to refund: {:?}", amount); if amount > 0 { Some((AssetId::get(), amount).into()) } else { diff --git a/polkadot/xcm/xcm-builder/tests/mock/mod.rs b/polkadot/xcm/xcm-builder/tests/mock/mod.rs index 6b4d893f73c7..5329fe3bb27e 100644 --- a/polkadot/xcm/xcm-builder/tests/mock/mod.rs +++ b/polkadot/xcm/xcm-builder/tests/mock/mod.rs @@ -210,6 +210,7 @@ impl xcm_executor::Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = (); } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/polkadot/xcm/xcm-executor/src/config.rs b/polkadot/xcm/xcm-executor/src/config.rs index 2ff12cd7a539..8f7e5e2d455c 100644 --- a/polkadot/xcm/xcm-executor/src/config.rs +++ b/polkadot/xcm/xcm-executor/src/config.rs @@ -16,8 +16,8 @@ use crate::traits::{ AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, - FeeManager, OnResponse, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, - WeightTrader, + FeeManager, OnResponse, ProcessTransaction, ShouldExecute, TransactAsset, + VersionChangeNotifier, WeightBounds, WeightTrader, }; use frame_support::{ dispatch::{GetDispatchInfo, Parameter, PostDispatchInfo}, @@ -111,4 +111,7 @@ pub trait Config { /// Use this type to explicitly whitelist calls that cannot undergo recursion. This is a /// temporary measure until we properly account for proof size weights for XCM instructions. type SafeCallFilter: Contains; + + /// Transactional processor for XCM instructions. + type TransactionalProcessor: ProcessTransaction; } diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 665b051c1306..0faae7f491cd 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -19,7 +19,7 @@ use frame_support::{ dispatch::GetDispatchInfo, ensure, - traits::{Contains, ContainsPair, Get, PalletsInfoAccess}, + traits::{Contains, ContainsPair, Defensive, Get, PalletsInfoAccess}, }; use parity_scale_codec::{Decode, Encode}; use sp_core::defer; @@ -31,8 +31,9 @@ use xcm::latest::prelude::*; pub mod traits; use traits::{ validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, - DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, Properties, ShouldExecute, - TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, XcmAssetTransfers, + DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, ProcessTransaction, + Properties, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, + XcmAssetTransfers, }; mod assets; @@ -373,33 +374,53 @@ impl XcmExecutor { r } - fn subsume_asset(&mut self, asset: MultiAsset) -> Result<(), XcmError> { + fn ensure_can_subsume_assets(&self, assets_length: usize) -> Result<(), XcmError> { // worst-case, holding.len becomes 2 * holding_limit. - ensure!(self.holding.len() < self.holding_limit * 2, XcmError::HoldingWouldOverflow); - self.holding.subsume(asset); - Ok(()) - } - - fn subsume_assets(&mut self, assets: Assets) -> Result<(), XcmError> { - // worst-case, holding.len becomes 2 * holding_limit. - // this guarantees that if holding.len() == holding_limit and you have holding_limit more - // items (which has a best case outcome of holding.len() == holding_limit), then you'll - // be guaranteed of making the operation. - let worst_case_holding_len = self.holding.len() + assets.len(); + // this guarantees that if holding.len() == holding_limit and you have more than + // `holding_limit` items (which has a best case outcome of holding.len() == holding_limit), + // then the operation is guaranteed to succeed. + let worst_case_holding_len = self.holding.len() + assets_length; + log::trace!(target: "xcm::ensure_can_subsume_assets", "worst_case_holding_len: {:?}, holding_limit: {:?}", worst_case_holding_len, self.holding_limit); ensure!(worst_case_holding_len <= self.holding_limit * 2, XcmError::HoldingWouldOverflow); - self.holding.subsume_assets(assets); Ok(()) } /// Refund any unused weight. fn refund_surplus(&mut self) -> Result<(), XcmError> { let current_surplus = self.total_surplus.saturating_sub(self.total_refunded); + log::trace!( + target: "xcm::refund_surplus", + "total_surplus: {:?}, total_refunded: {:?}, current_surplus: {:?}", + self.total_surplus, + self.total_refunded, + current_surplus, + ); if current_surplus.any_gt(Weight::zero()) { - self.total_refunded.saturating_accrue(current_surplus); if let Some(w) = self.trader.refund_weight(current_surplus, &self.context) { - self.subsume_asset(w)?; + if !self.holding.contains_asset(&(w.id.clone(), 1).into()) && + self.ensure_can_subsume_assets(1).is_err() + { + let _ = self + .trader + .buy_weight(current_surplus, w.into(), &self.context) + .defensive_proof( + "refund_weight returned an asset capable of buying weight; qed", + ); + log::error!( + target: "xcm::refund_surplus", + "error: HoldingWouldOverflow", + ); + return Err(XcmError::HoldingWouldOverflow) + } + self.total_refunded.saturating_accrue(current_surplus); + self.holding.subsume_assets(w.into()); } } + log::trace!( + target: "xcm::refund_surplus", + "total_refunded: {:?}", + self.total_refunded, + ); Ok(()) } @@ -563,74 +584,101 @@ impl XcmExecutor { ); match instr { WithdrawAsset(assets) => { - // Take `assets` from the origin account (on-chain) and place in holding. - let origin = *self.origin_ref().ok_or(XcmError::BadOrigin)?; - for asset in assets.into_inner().into_iter() { - Config::AssetTransactor::withdraw_asset(&asset, &origin, Some(&self.context))?; - self.subsume_asset(asset)?; - } - Ok(()) + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + self.ensure_can_subsume_assets(assets.len())?; + Config::TransactionalProcessor::process(|| { + // Take `assets` from the origin account (on-chain)... + for asset in assets.inner() { + Config::AssetTransactor::withdraw_asset( + asset, + origin, + Some(&self.context), + )?; + } + Ok(()) + }) + .and_then(|_| { + // ...and place into holding. + self.holding.subsume_assets(assets.into()); + Ok(()) + }) }, ReserveAssetDeposited(assets) => { // check whether we trust origin to be our reserve location for this asset. - let origin = *self.origin_ref().ok_or(XcmError::BadOrigin)?; - for asset in assets.into_inner().into_iter() { + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + self.ensure_can_subsume_assets(assets.len())?; + for asset in assets.inner() { // Must ensure that we recognise the asset as being managed by the origin. ensure!( - Config::IsReserve::contains(&asset, &origin), + Config::IsReserve::contains(asset, origin), XcmError::UntrustedReserveLocation ); - self.subsume_asset(asset)?; } + self.holding.subsume_assets(assets.into()); Ok(()) }, TransferAsset { assets, beneficiary } => { - // Take `assets` from the origin account (on-chain) and place into dest account. - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - for asset in assets.inner() { - Config::AssetTransactor::transfer_asset( - &asset, - origin, - &beneficiary, - &self.context, - )?; - } - Ok(()) + Config::TransactionalProcessor::process(|| { + // Take `assets` from the origin account (on-chain) and place into dest account. + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + for asset in assets.inner() { + Config::AssetTransactor::transfer_asset( + &asset, + origin, + &beneficiary, + &self.context, + )?; + } + Ok(()) + }) }, TransferReserveAsset { mut assets, dest, xcm } => { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - // Take `assets` from the origin account (on-chain) and place into dest account. - for asset in assets.inner() { - Config::AssetTransactor::transfer_asset(asset, origin, &dest, &self.context)?; - } - let reanchor_context = Config::UniversalLocation::get(); - assets.reanchor(&dest, reanchor_context).map_err(|()| XcmError::LocationFull)?; - let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(dest, Xcm(message), FeeReason::TransferReserveAsset)?; - Ok(()) + Config::TransactionalProcessor::process(|| { + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + // Take `assets` from the origin account (on-chain) and place into dest account. + for asset in assets.inner() { + Config::AssetTransactor::transfer_asset( + asset, + origin, + &dest, + &self.context, + )?; + } + let reanchor_context = Config::UniversalLocation::get(); + assets + .reanchor(&dest, &reanchor_context) + .map_err(|()| XcmError::LocationFull)?; + let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + self.send(dest, Xcm(message), FeeReason::TransferReserveAsset)?; + Ok(()) + }) }, ReceiveTeleportedAsset(assets) => { - let origin = *self.origin_ref().ok_or(XcmError::BadOrigin)?; - // check whether we trust origin to teleport this asset to us via config trait. - for asset in assets.inner() { - // We only trust the origin to send us assets that they identify as their - // sovereign assets. - ensure!( - Config::IsTeleporter::contains(asset, &origin), - XcmError::UntrustedTeleportLocation - ); - // We should check that the asset can actually be teleported in (for this to be - // in error, there would need to be an accounting violation by one of the - // trusted chains, so it's unlikely, but we don't want to punish a possibly - // innocent chain/user). - Config::AssetTransactor::can_check_in(&origin, asset, &self.context)?; - } - for asset in assets.into_inner().into_iter() { - Config::AssetTransactor::check_in(&origin, &asset, &self.context); - self.subsume_asset(asset)?; - } - Ok(()) + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + self.ensure_can_subsume_assets(assets.len())?; + Config::TransactionalProcessor::process(|| { + // check whether we trust origin to teleport this asset to us via config trait. + for asset in assets.inner() { + // We only trust the origin to send us assets that they identify as their + // sovereign assets. + ensure!( + Config::IsTeleporter::contains(asset, origin), + XcmError::UntrustedTeleportLocation + ); + // We should check that the asset can actually be teleported in (for this to + // be in error, there would need to be an accounting violation by one of the + // trusted chains, so it's unlikely, but we don't want to punish a possibly + // innocent chain/user). + Config::AssetTransactor::can_check_in(origin, asset, &self.context)?; + Config::AssetTransactor::check_in(origin, asset, &self.context); + } + Ok(()) + }) + .and_then(|_| { + self.holding.subsume_assets(assets.into()); + Ok(()) + }) }, Transact { origin_kind, require_weight_at_most, mut call } => { // We assume that the Relay-chain is allowed to use transact on this parachain. @@ -761,62 +809,92 @@ impl XcmExecutor { Ok(()) }, DepositAsset { assets, beneficiary } => { - let deposited = self.holding.saturating_take(assets); - for asset in deposited.into_assets_iter() { - Config::AssetTransactor::deposit_asset( - &asset, - &beneficiary, - Some(&self.context), - )?; + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + let deposited = self.holding.saturating_take(assets); + for asset in deposited.into_assets_iter() { + Config::AssetTransactor::deposit_asset( + &asset, + &beneficiary, + Some(&self.context), + )?; + } + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; } - Ok(()) + result }, DepositReserveAsset { assets, dest, xcm } => { - let deposited = self.holding.saturating_take(assets); - for asset in deposited.assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &dest, Some(&self.context))?; + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + let deposited = self.holding.saturating_take(assets); + for asset in deposited.assets_iter() { + Config::AssetTransactor::deposit_asset(&asset, &dest, Some(&self.context))?; + } + // Note that we pass `None` as `maybe_failed_bin` and drop any assets which + // cannot be reanchored because we have already called `deposit_asset` on all + // assets. + let assets = Self::reanchored(deposited, &dest, None); + let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?; + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; } - // Note that we pass `None` as `maybe_failed_bin` and drop any assets which cannot - // be reanchored because we have already called `deposit_asset` on all assets. - let assets = Self::reanchored(deposited, &dest, None); - let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?; - Ok(()) + result }, InitiateReserveWithdraw { assets, reserve, xcm } => { - // Note that here we are able to place any assets which could not be reanchored - // back into Holding. - let assets = Self::reanchored( - self.holding.saturating_take(assets), - &reserve, - Some(&mut self.holding), - ); - let mut message = vec![WithdrawAsset(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(reserve, Xcm(message), FeeReason::InitiateReserveWithdraw)?; - Ok(()) + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + // Note that here we are able to place any assets which could not be reanchored + // back into Holding. + let assets = Self::reanchored( + self.holding.saturating_take(assets), + &reserve, + Some(&mut self.holding), + ); + let mut message = vec![WithdrawAsset(assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + self.send(reserve, Xcm(message), FeeReason::InitiateReserveWithdraw)?; + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; + } + result }, InitiateTeleport { assets, dest, xcm } => { - // We must do this first in order to resolve wildcards. - let assets = self.holding.saturating_take(assets); - for asset in assets.assets_iter() { - // We should check that the asset can actually be teleported out (for this to - // be in error, there would need to be an accounting violation by ourselves, - // so it's unlikely, but we don't want to allow that kind of bug to leak into - // a trusted chain. - Config::AssetTransactor::can_check_out(&dest, &asset, &self.context)?; - } - for asset in assets.assets_iter() { - Config::AssetTransactor::check_out(&dest, &asset, &self.context); + let old_holding = self.holding.clone(); + let result = (|| -> Result<(), XcmError> { + // We must do this first in order to resolve wildcards. + let assets = self.holding.saturating_take(assets); + for asset in assets.assets_iter() { + // We should check that the asset can actually be teleported out (for this + // to be in error, there would need to be an accounting violation by + // ourselves, so it's unlikely, but we don't want to allow that kind of bug + // to leak into a trusted chain. + Config::AssetTransactor::can_check_out(&dest, &asset, &self.context)?; + } + // Note that we pass `None` as `maybe_failed_bin` and drop any assets which + // cannot be reanchored because we have already checked all assets out. + let reanchored_assets = Self::reanchored(assets.clone(), &dest, None); + let mut message = vec![ReceiveTeleportedAsset(reanchored_assets), ClearOrigin]; + message.extend(xcm.0.into_iter()); + self.send(dest.clone(), Xcm(message), FeeReason::InitiateTeleport)?; + + for asset in assets.assets_iter() { + Config::AssetTransactor::check_out(&dest, &asset, &self.context); + } + Ok(()) + })(); + if result.is_err() { + self.holding = old_holding; } - // Note that we pass `None` as `maybe_failed_bin` and drop any assets which cannot - // be reanchored because we have already checked all assets out. - let assets = Self::reanchored(assets, &dest, None); - let mut message = vec![ReceiveTeleportedAsset(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(dest, Xcm(message), FeeReason::InitiateTeleport)?; - Ok(()) + result }, ReportHolding { response_info, assets } => { // Note that we pass `None` as `maybe_failed_bin` since no assets were ever removed @@ -832,18 +910,24 @@ impl XcmExecutor { Ok(()) }, BuyExecution { fees, weight_limit } => { - // There is no need to buy any weight is `weight_limit` is `Unlimited` since it + // There is no need to buy any weight if `weight_limit` is `Unlimited` since it // would indicate that `AllowTopLevelPaidExecutionFrom` was unused for execution // and thus there is some other reason why it has been determined that this XCM // should be executed. - if let Some(weight) = Option::::from(weight_limit) { - // pay for `weight` using up to `fees` of the holding register. - let max_fee = - self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; + let Some(weight) = Option::::from(weight_limit) else { return Ok(()) }; + let old_holding = self.holding.clone(); + // pay for `weight` using up to `fees` of the holding register. + let max_fee = + self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; + let result = || -> Result<(), XcmError> { let unspent = self.trader.buy_weight(weight, max_fee, &self.context)?; - self.subsume_assets(unspent)?; + self.holding.subsume_assets(unspent); + Ok(()) + }(); + if result.is_err() { + self.holding = old_holding; } - Ok(()) + result }, RefundSurplus => self.refund_surplus(), SetErrorHandler(mut handler) => { @@ -868,11 +952,10 @@ impl XcmExecutor { }, ClaimAsset { assets, ticket } => { let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + self.ensure_can_subsume_assets(assets.len())?; let ok = Config::AssetClaims::claim_assets(origin, &ticket, &assets, &self.context); ensure!(ok, XcmError::UnknownClaim); - for asset in assets.into_inner().into_iter() { - self.subsume_asset(asset)?; - } + self.holding.subsume_assets(assets.into()); Ok(()) }, Trap(code) => Err(XcmError::Trap(code)), @@ -990,8 +1073,8 @@ impl XcmExecutor { let hash = (self.origin_ref(), &destination).using_encoded(blake2_128); let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); // Hash identifies the lane on the exporter which we use. We use the pairwise - // combination of the origin and destination to ensure origin/destination pairs will - // generally have their own lanes. + // combination of the origin and destination to ensure origin/destination pairs + // will generally have their own lanes. let (ticket, fee) = validate_export::( network, channel, @@ -999,22 +1082,41 @@ impl XcmExecutor { destination, xcm, )?; - self.take_fee(fee, FeeReason::Export { network, destination })?; - Config::MessageExporter::deliver(ticket)?; - Ok(()) + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + self.take_fee(fee, FeeReason::Export { network, destination })?; + let _ = Config::MessageExporter::deliver(ticket).defensive_proof( + "`deliver` called immediately after `validate_export`; \ + `take_fee` does not affect the validity of the ticket; qed", + ); + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; + } + result }, LockAsset { asset, unlocker } => { - let origin = *self.origin_ref().ok_or(XcmError::BadOrigin)?; - let (remote_asset, context) = Self::try_reanchor(asset.clone(), &unlocker)?; - let lock_ticket = Config::AssetLocker::prepare_lock(unlocker, asset, origin)?; - let owner = - origin.reanchored(&unlocker, context).map_err(|_| XcmError::ReanchorFailed)?; - let msg = Xcm::<()>(vec![NoteUnlockable { asset: remote_asset, owner }]); - let (ticket, price) = validate_send::(unlocker, msg)?; - self.take_fee(price, FeeReason::LockAsset)?; - lock_ticket.enact()?; - Config::XcmSender::deliver(ticket)?; - Ok(()) + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; + let (remote_asset, context) = Self::try_reanchor(asset.clone(), &unlocker)?; + let lock_ticket = + Config::AssetLocker::prepare_lock(unlocker.clone(), asset, origin.clone())?; + let owner = origin + .reanchored(&unlocker, &context) + .map_err(|_| XcmError::ReanchorFailed)?; + let msg = Xcm::<()>(vec![NoteUnlockable { asset: remote_asset, owner }]); + let (ticket, price) = validate_send::(unlocker, msg)?; + self.take_fee(price, FeeReason::LockAsset)?; + lock_ticket.enact()?; + Config::XcmSender::deliver(ticket)?; + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; + } + result }, UnlockAsset { asset, target } => { let origin = *self.origin_ref().ok_or(XcmError::BadOrigin)?; @@ -1035,25 +1137,40 @@ impl XcmExecutor { let msg = Xcm::<()>(vec![UnlockAsset { asset: remote_asset, target: remote_target }]); let (ticket, price) = validate_send::(locker, msg)?; - self.take_fee(price, FeeReason::RequestUnlock)?; - reduce_ticket.enact()?; - Config::XcmSender::deliver(ticket)?; - Ok(()) + let old_holding = self.holding.clone(); + let result = Config::TransactionalProcessor::process(|| { + self.take_fee(price, FeeReason::RequestUnlock)?; + reduce_ticket.enact()?; + Config::XcmSender::deliver(ticket)?; + Ok(()) + }); + if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { + self.holding = old_holding; + } + result }, ExchangeAsset { give, want, maximal } => { + let old_holding = self.holding.clone(); let give = self.holding.saturating_take(give); - let r = - Config::AssetExchanger::exchange_asset(self.origin_ref(), give, &want, maximal); - let completed = r.is_ok(); - let received = r.unwrap_or_else(|a| a); - for asset in received.into_assets_iter() { - self.holding.subsume(asset); - } - if completed { - Ok(()) - } else { - Err(XcmError::NoDeal) + let result = (|| -> Result<(), XcmError> { + self.ensure_can_subsume_assets(want.len())?; + let exchange_result = Config::AssetExchanger::exchange_asset( + self.origin_ref(), + give, + &want, + maximal, + ); + if let Ok(received) = exchange_result { + self.holding.subsume_assets(received.into()); + Ok(()) + } else { + Err(XcmError::NoDeal) + } + })(); + if result.is_err() { + self.holding = old_holding; } + result }, SetFeesMode { jit_withdraw } => { self.fees_mode = FeesMode { jit_withdraw }; diff --git a/polkadot/xcm/xcm-executor/src/traits/mod.rs b/polkadot/xcm/xcm-executor/src/traits/mod.rs index 71e75c77e939..b445e84d3912 100644 --- a/polkadot/xcm/xcm-executor/src/traits/mod.rs +++ b/polkadot/xcm/xcm-executor/src/traits/mod.rs @@ -39,6 +39,8 @@ pub use token_matching::{ }; mod on_response; pub use on_response::{OnResponse, QueryHandler, QueryResponseStatus, VersionChangeNotifier}; +mod process_transaction; +pub use process_transaction::ProcessTransaction; mod should_execute; pub use should_execute::{CheckSuspension, Properties, ShouldExecute}; mod transact_asset; @@ -52,8 +54,9 @@ pub mod prelude { pub use super::{ export_xcm, validate_export, AssetExchange, AssetLock, ClaimAssets, ConvertOrigin, DropAssets, Enact, Error, ExportXcm, FeeManager, FeeReason, LockError, MatchesFungible, - MatchesFungibles, MatchesNonFungible, MatchesNonFungibles, OnResponse, ShouldExecute, - TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, WithOriginFilter, + MatchesFungibles, MatchesNonFungible, MatchesNonFungibles, OnResponse, ProcessTransaction, + ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, + WithOriginFilter, }; #[allow(deprecated)] pub use super::{Identity, JustTry}; diff --git a/polkadot/xcm/xcm-executor/src/traits/process_transaction.rs b/polkadot/xcm/xcm-executor/src/traits/process_transaction.rs new file mode 100644 index 000000000000..22ad8755b9c9 --- /dev/null +++ b/polkadot/xcm/xcm-executor/src/traits/process_transaction.rs @@ -0,0 +1,57 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use xcm::latest::prelude::*; + +/// Provides mechanisms for transactional processing of XCM instructions. +/// +/// This trait defines the behavior required to process XCM instructions in a transactional +/// manner. Implementers of this trait can ensure that XCM instructions are executed +/// atomically, meaning they either fully succeed or fully fail without any partial effects. +/// +/// Implementers of this trait can also choose to not process XCM instructions transactionally. +/// This is useful for cases where the implementer is not able to provide transactional guarantees. +/// In this case the `IS_TRANSACTIONAL` constant should be set to `false`. +/// The `()` type implements this trait in a non-transactional manner. +pub trait ProcessTransaction { + /// Whether or not the implementor of the this trait is actually transactional. + const IS_TRANSACTIONAL: bool; + + /// Processes an XCM instruction encapsulated within the provided closure. Responsible for + /// processing an XCM instruction transactionally. If the closure returns an error, any + /// changes made during its execution should be rolled back. In the case where the + /// implementer is not able to provide transactional guarantees, the closure should be + /// executed as is. + /// # Parameters + /// - `f`: A closure that encapsulates the XCM instruction being processed. It will return a + /// `Result` indicating the success or failure of the instruction. + /// + /// # Returns + /// - A `Result` indicating the overall success or failure of the transactional process. + fn process(f: F) -> Result<(), XcmError> + where + F: FnOnce() -> Result<(), XcmError>; +} + +impl ProcessTransaction for () { + const IS_TRANSACTIONAL: bool = false; + fn process(f: F) -> Result<(), XcmError> + where + F: FnOnce() -> Result<(), XcmError>, + { + f() + } +} diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain.rs b/polkadot/xcm/xcm-simulator/example/src/parachain.rs index 69db81deff4f..d2aabc17555c 100644 --- a/polkadot/xcm/xcm-simulator/example/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/parachain.rs @@ -40,9 +40,10 @@ use polkadot_parachain_primitives::primitives::{ use xcm::{latest::prelude::*, VersionedXcm}; use xcm_builder::{ Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, ConvertedConcreteId, - EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, IsConcrete, - NativeAsset, NoChecking, NonFungiblesAdapter, ParentIsPreset, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, + EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FrameTransactionalProcessor, + FungibleAdapter, IsConcrete, NativeAsset, NoChecking, NonFungiblesAdapter, ParentIsPreset, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, }; use xcm_executor::{ traits::{ConvertLocation, JustTry}, @@ -250,6 +251,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } #[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 24fc56eb7174..84c729967092 100644 --- a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs @@ -36,9 +36,9 @@ use xcm::latest::prelude::*; use xcm_builder::{ Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, - ConvertedConcreteId, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, IsConcrete, - NoChecking, NonFungiblesAdapter, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, + ConvertedConcreteId, FixedRateOfFungible, FixedWeightBounds, FrameTransactionalProcessor, + FungibleAdapter, IsConcrete, NoChecking, NonFungiblesAdapter, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -196,6 +196,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } 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 2262d18e8604..1301af381485 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -41,8 +41,9 @@ use xcm::{latest::prelude::*, VersionedXcm}; use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedRateOfFungible, - FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, + FixedWeightBounds, FrameTransactionalProcessor, IsConcrete, NativeAsset, ParentIsPreset, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, }; use xcm_executor::{Config, XcmExecutor}; @@ -166,6 +167,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } #[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 bbf4f1e6cc5b..8d4e125ad7e4 100644 --- a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs @@ -38,8 +38,8 @@ use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, FixedRateOfFungible, - FixedWeightBounds, IsConcrete, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, + FixedWeightBounds, FrameTransactionalProcessor, IsConcrete, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, }; use xcm_executor::{Config, XcmExecutor}; @@ -163,6 +163,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type LocalOriginToLocation = SignedToAccountId32; diff --git a/prdoc/pr_1222.prdoc b/prdoc/pr_1222.prdoc new file mode 100644 index 000000000000..82eb341649bc --- /dev/null +++ b/prdoc/pr_1222.prdoc @@ -0,0 +1,33 @@ +title: Transactional processing for XCM + +doc: + - audience: Runtime Dev + description: | + Transactional processing was introduced for certain XCM instructions. They are: + - WithdrawAsset + - ReserveAssetDeposited + - TransferAsset + - TransferReserveAsset + - ReceiveTeleportedAsset + - DepositAsset + - DepositReserveAsset + - InitiateReserveWithdraw + - InitiateTeleport + - BuyExecution + - ClaimAsset + - ExportMessage + - LockAsset + - UnlockAsset + - RequestUnlock + Developers must specify a `TransactionalProcessor` when configuring their XCM executor. + FRAME-based runtimes would simply need to configure it with `FrameTransactionalProcessor` to + enable transactional processing. To disable transactional processing of XCMs, `()` may also be + specified as the type for `TransactionalProcessor`. + For runtimes that are not FRAME-based but would like to still harness transactional processing + of XCMs, a type implementing the `ProcessTransaction` trait must be specified as the type for + `TransactionalProcessor`. This trait is for the purpose of connecting the chain's runtime + transactional processor with the XCM executor -- any implementation of `ProcessTransaction` is + possible to be assigned as the `TransactionalProcessor` for the XCM executor. + +crates: + - name: staging-xcm-executor diff --git a/substrate/frame/contracts/mock-network/src/parachain.rs b/substrate/frame/contracts/mock-network/src/parachain.rs index a79b7e4e2d6d..ca6cfc099eee 100644 --- a/substrate/frame/contracts/mock-network/src/parachain.rs +++ b/substrate/frame/contracts/mock-network/src/parachain.rs @@ -41,9 +41,10 @@ use xcm::latest::prelude::*; use xcm_builder::CurrencyAdapter as XcmCurrencyAdapter; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, - ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin, + ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, + FrameTransactionalProcessor, FungiblesAdapter, IsConcrete, NativeAsset, NoChecking, + ParentAsSuperuser, ParentIsPreset, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, WithComputedOrigin, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -288,6 +289,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } 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 136cc2e3ed67..31c6f24c6383 100644 --- a/substrate/frame/contracts/mock-network/src/relay_chain.rs +++ b/substrate/frame/contracts/mock-network/src/relay_chain.rs @@ -35,8 +35,8 @@ use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, DescribeAllTerminal, DescribeFamily, FixedRateOfFungible, - FixedWeightBounds, HashedDescription, IsConcrete, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin, + FixedWeightBounds, FrameTransactionalProcessor, HashedDescription, IsConcrete, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin, }; use xcm_executor::{Config, XcmExecutor}; @@ -183,6 +183,7 @@ impl Config for XcmConfig { type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; } pub type LocalOriginToLocation = SignedToAccountId32;