mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 12:51:02 +00:00
XCM: Automatic Version Negotiation (#3736)
* XCM: Automatic Version Negotiation * Introduce the version instructions and subscription trait * Notification and subscription data migration * Version change subscriptions * Fixes * Formatting * Spelling * Fixes * Fixes * Automatic unsubscription * Formatting * Expose remote origin in VM and ensure it is unchanged from actual origin in subscription instructions. * Barrier * Unsubscription extrinsic * Remove top_level param * Formatting * Fixes * Automatic subscription * Formatting * Spelling * Unit tests for XCM executor * Formatting * Spellin * Unit test for XCM pallet subscriber side * Formatting * More tests * Formatting * Fixes * Subscription-side tests * Formatting * Unit tests for XCM pallet * Formatting * Update roadmap/implementers-guide/src/types/overseer-protocol.md Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Remove commented code * Grumbles * Multi-stage XCM version migration * Formatting * v1 subscriptions backport * Warning * Spelling * Fix grumbles * Formatting * Avoid running through old notifications * Formatting Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
@@ -441,7 +441,7 @@ enum DisputeCoordinatorMessage {
|
|||||||
pending_confirmation: oneshot::Sender<ImportStatementsResult>
|
pending_confirmation: oneshot::Sender<ImportStatementsResult>
|
||||||
},
|
},
|
||||||
/// Fetch a list of all recent disputes that the co-ordinator is aware of.
|
/// Fetch a list of all recent disputes that the co-ordinator is aware of.
|
||||||
/// These are disputes which have occured any time in recent sessions, which may have already concluded.
|
/// These are disputes which have occurred any time in recent sessions, which may have already concluded.
|
||||||
RecentDisputes(ResponseChannel<Vec<(SessionIndex, CandidateHash)>>),
|
RecentDisputes(ResponseChannel<Vec<(SessionIndex, CandidateHash)>>),
|
||||||
/// Fetch a list of all active disputes that the co-ordinator is aware of.
|
/// Fetch a list of all active disputes that the co-ordinator is aware of.
|
||||||
/// These disputes are either unconcluded or recently concluded.
|
/// These disputes are either unconcluded or recently concluded.
|
||||||
@@ -699,7 +699,7 @@ The Runtime API subsystem is responsible for providing an interface to the state
|
|||||||
|
|
||||||
This is fueled by an auxiliary type encapsulating all request types defined in the Runtime API section of the guide.
|
This is fueled by an auxiliary type encapsulating all request types defined in the Runtime API section of the guide.
|
||||||
|
|
||||||
> TODO: link to the Runtime API section. Not possible currently because of https://github.com/Michael-F-Bryan/mdbook-linkcheck/issues/25. Once v0.7.1 is released it will work.
|
> To do: link to the Runtime API section. Not possible currently because of https://github.com/Michael-F-Bryan/mdbook-linkcheck/issues/25. Once v0.7.1 is released it will work.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
enum RuntimeApiRequest {
|
enum RuntimeApiRequest {
|
||||||
|
|||||||
@@ -1307,6 +1307,7 @@ impl xcm_executor::Config for XcmConfig {
|
|||||||
type ResponseHandler = XcmPallet;
|
type ResponseHandler = XcmPallet;
|
||||||
type AssetTrap = XcmPallet;
|
type AssetTrap = XcmPallet;
|
||||||
type AssetClaims = XcmPallet;
|
type AssetClaims = XcmPallet;
|
||||||
|
type SubscriptionService = XcmPallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
@@ -1326,7 +1327,6 @@ pub type LocalOriginToLocation = (
|
|||||||
// And a usual Signed origin to be used in XCM as a corresponding AccountId32
|
// And a usual Signed origin to be used in XCM as a corresponding AccountId32
|
||||||
SignedToAccountId32<Origin, AccountId, KusamaNetwork>,
|
SignedToAccountId32<Origin, AccountId, KusamaNetwork>,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl pallet_xcm::Config for Runtime {
|
impl pallet_xcm::Config for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||||
@@ -1342,6 +1342,8 @@ impl pallet_xcm::Config for Runtime {
|
|||||||
type LocationInverter = LocationInverter<Ancestry>;
|
type LocationInverter = LocationInverter<Ancestry>;
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
|
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||||
|
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
|
|||||||
@@ -675,6 +675,7 @@ impl xcm_executor::Config for XcmConfig {
|
|||||||
type ResponseHandler = XcmPallet;
|
type ResponseHandler = XcmPallet;
|
||||||
type AssetTrap = XcmPallet;
|
type AssetTrap = XcmPallet;
|
||||||
type AssetClaims = XcmPallet;
|
type AssetClaims = XcmPallet;
|
||||||
|
type SubscriptionService = XcmPallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
@@ -706,6 +707,8 @@ impl pallet_xcm::Config for Runtime {
|
|||||||
type LocationInverter = LocationInverter<Ancestry>;
|
type LocationInverter = LocationInverter<Ancestry>;
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
|
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||||
|
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl parachains_session_info::Config for Runtime {}
|
impl parachains_session_info::Config for Runtime {}
|
||||||
|
|||||||
@@ -518,6 +518,8 @@ impl pallet_xcm::Config for Runtime {
|
|||||||
type XcmReserveTransferFilter = Everything;
|
type XcmReserveTransferFilter = Everything;
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
|
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||||
|
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl parachains_hrmp::Config for Runtime {
|
impl parachains_hrmp::Config for Runtime {
|
||||||
@@ -540,7 +542,7 @@ impl pallet_test_notifier::Config for Runtime {
|
|||||||
pub mod pallet_test_notifier {
|
pub mod pallet_test_notifier {
|
||||||
use frame_support::pallet_prelude::*;
|
use frame_support::pallet_prelude::*;
|
||||||
use frame_system::pallet_prelude::*;
|
use frame_system::pallet_prelude::*;
|
||||||
use pallet_xcm::{ensure_response, QueryId};
|
use pallet_xcm::ensure_response;
|
||||||
use sp_runtime::DispatchResult;
|
use sp_runtime::DispatchResult;
|
||||||
use xcm::latest::prelude::*;
|
use xcm::latest::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -88,4 +88,5 @@ impl xcm_executor::Config for XcmConfig {
|
|||||||
type ResponseHandler = super::Xcm;
|
type ResponseHandler = super::Xcm;
|
||||||
type AssetTrap = super::Xcm;
|
type AssetTrap = super::Xcm;
|
||||||
type AssetClaims = super::Xcm;
|
type AssetClaims = super::Xcm;
|
||||||
|
type SubscriptionService = super::Xcm;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -949,6 +949,7 @@ impl xcm_executor::Config for XcmConfig {
|
|||||||
type ResponseHandler = XcmPallet;
|
type ResponseHandler = XcmPallet;
|
||||||
type AssetTrap = XcmPallet;
|
type AssetTrap = XcmPallet;
|
||||||
type AssetClaims = XcmPallet;
|
type AssetClaims = XcmPallet;
|
||||||
|
type SubscriptionService = XcmPallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location
|
/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location
|
||||||
@@ -973,6 +974,8 @@ impl pallet_xcm::Config for Runtime {
|
|||||||
type LocationInverter = LocationInverter<Ancestry>;
|
type LocationInverter = LocationInverter<Ancestry>;
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
|
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||||
|
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
construct_runtime! {
|
construct_runtime! {
|
||||||
|
|||||||
@@ -23,9 +23,12 @@ mod mock;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode, EncodeLike};
|
||||||
use frame_support::traits::{Contains, EnsureOrigin, Get, OriginTrait};
|
use frame_support::traits::{Contains, EnsureOrigin, Get, OriginTrait};
|
||||||
use sp_runtime::{traits::BadOrigin, RuntimeDebug};
|
use sp_runtime::{
|
||||||
|
traits::{BadOrigin, Saturating},
|
||||||
|
RuntimeDebug,
|
||||||
|
};
|
||||||
use sp_std::{
|
use sp_std::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
@@ -34,10 +37,7 @@ use sp_std::{
|
|||||||
result::Result,
|
result::Result,
|
||||||
vec,
|
vec,
|
||||||
};
|
};
|
||||||
use xcm::{
|
use xcm::prelude::*;
|
||||||
latest::prelude::*, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse,
|
|
||||||
VersionedXcm,
|
|
||||||
};
|
|
||||||
use xcm_executor::traits::ConvertOrigin;
|
use xcm_executor::traits::ConvertOrigin;
|
||||||
|
|
||||||
use frame_support::PalletId;
|
use frame_support::PalletId;
|
||||||
@@ -49,15 +49,25 @@ pub mod pallet {
|
|||||||
use frame_support::{
|
use frame_support::{
|
||||||
dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo},
|
dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo},
|
||||||
pallet_prelude::*,
|
pallet_prelude::*,
|
||||||
|
parameter_types,
|
||||||
};
|
};
|
||||||
use frame_system::{pallet_prelude::*, Config as SysConfig};
|
use frame_system::{pallet_prelude::*, Config as SysConfig};
|
||||||
use sp_core::H256;
|
use sp_core::H256;
|
||||||
use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, BlockNumberProvider, Hash};
|
use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, BlockNumberProvider, Hash};
|
||||||
use xcm_executor::{
|
use xcm_executor::{
|
||||||
traits::{ClaimAssets, DropAssets, InvertLocation, OnResponse, WeightBounds},
|
traits::{
|
||||||
|
ClaimAssets, DropAssets, InvertLocation, OnResponse, VersionChangeNotifier,
|
||||||
|
WeightBounds,
|
||||||
|
},
|
||||||
Assets,
|
Assets,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
/// An implementation of `Get<u32>` which just returns the latest XCM version which we can
|
||||||
|
/// support.
|
||||||
|
pub const CurrentXcmVersion: u32 = XCM_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
#[pallet::pallet]
|
#[pallet::pallet]
|
||||||
#[pallet::generate_store(pub(super) trait Store)]
|
#[pallet::generate_store(pub(super) trait Store)]
|
||||||
pub struct Pallet<T>(_);
|
pub struct Pallet<T>(_);
|
||||||
@@ -106,6 +116,12 @@ pub mod pallet {
|
|||||||
+ GetDispatchInfo
|
+ GetDispatchInfo
|
||||||
+ IsType<<Self as frame_system::Config>::Call>
|
+ IsType<<Self as frame_system::Config>::Call>
|
||||||
+ Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>;
|
+ Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>;
|
||||||
|
|
||||||
|
const VERSION_DISCOVERY_QUEUE_SIZE: u32;
|
||||||
|
|
||||||
|
/// The latest supported version that we advertise. Generally just set it to
|
||||||
|
/// `pallet_xcm::CurrentXcmVersion`.
|
||||||
|
type AdvertisedXcmVersion: Get<XcmVersion>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum number of distinct assets allowed to be transferred in a single helper extrinsic.
|
/// The maximum number of distinct assets allowed to be transferred in a single helper extrinsic.
|
||||||
@@ -160,7 +176,7 @@ pub mod pallet {
|
|||||||
/// be received and acted upon.
|
/// be received and acted upon.
|
||||||
///
|
///
|
||||||
/// \[ origin location, id, expected location \]
|
/// \[ origin location, id, expected location \]
|
||||||
InvalidResponder(MultiLocation, QueryId, MultiLocation),
|
InvalidResponder(MultiLocation, QueryId, Option<MultiLocation>),
|
||||||
/// Expected query response has been received but the expected origin location placed in
|
/// Expected query response has been received but the expected origin location placed in
|
||||||
/// storate by this runtime previously cannot be decoded. The query remains registered.
|
/// storate by this runtime previously cannot be decoded. The query remains registered.
|
||||||
///
|
///
|
||||||
@@ -179,6 +195,25 @@ pub mod pallet {
|
|||||||
///
|
///
|
||||||
/// \[ hash, origin, assets \]
|
/// \[ hash, origin, assets \]
|
||||||
AssetsTrapped(H256, MultiLocation, VersionedMultiAssets),
|
AssetsTrapped(H256, MultiLocation, VersionedMultiAssets),
|
||||||
|
/// An XCM version change notification message has been attempted to be sent.
|
||||||
|
///
|
||||||
|
/// \[ destination, result \]
|
||||||
|
VersionChangeNotified(MultiLocation, XcmVersion),
|
||||||
|
/// The supported version of a location has been changed. This might be through an
|
||||||
|
/// automatic notification or a manual intervention.
|
||||||
|
///
|
||||||
|
/// \[ location, XCM version \]
|
||||||
|
SupportedVersionChanged(MultiLocation, XcmVersion),
|
||||||
|
/// A given location which had a version change subscription was dropped owing to an error
|
||||||
|
/// sending the notification to it.
|
||||||
|
///
|
||||||
|
/// \[ location, query ID, error \]
|
||||||
|
NotifyTargetSendFail(MultiLocation, QueryId, XcmError),
|
||||||
|
/// A given location which had a version change subscription was dropped owing to an error
|
||||||
|
/// migrating the location to our new XCM format.
|
||||||
|
///
|
||||||
|
/// \[ location, query ID \]
|
||||||
|
NotifyTargetMigrationFail(VersionedMultiLocation, QueryId),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::origin]
|
#[pallet::origin]
|
||||||
@@ -219,6 +254,13 @@ pub mod pallet {
|
|||||||
InvalidOrigin,
|
InvalidOrigin,
|
||||||
/// The version of the `Versioned` value used is not able to be interpreted.
|
/// The version of the `Versioned` value used is not able to be interpreted.
|
||||||
BadVersion,
|
BadVersion,
|
||||||
|
/// The given location could not be used (e.g. because it cannot be expressed in the
|
||||||
|
/// desired version of XCM).
|
||||||
|
BadLocation,
|
||||||
|
/// The referenced subscription could not be found.
|
||||||
|
NoSubscription,
|
||||||
|
/// The location is invalid since it already has a subscription from us.
|
||||||
|
AlreadySubscribed,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The status of a query.
|
/// The status of a query.
|
||||||
@@ -230,16 +272,41 @@ pub mod pallet {
|
|||||||
maybe_notify: Option<(u8, u8)>,
|
maybe_notify: Option<(u8, u8)>,
|
||||||
timeout: BlockNumber,
|
timeout: BlockNumber,
|
||||||
},
|
},
|
||||||
|
/// The query is for an ongoing version notification subscription.
|
||||||
|
VersionNotifier { origin: VersionedMultiLocation, is_active: bool },
|
||||||
/// A response has been received.
|
/// A response has been received.
|
||||||
Ready { response: VersionedResponse, at: BlockNumber },
|
Ready { response: VersionedResponse, at: BlockNumber },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Value of a query, must be unique for each query.
|
#[derive(Copy, Clone)]
|
||||||
pub type QueryId = u64;
|
pub(crate) struct LatestVersionedMultiLocation<'a>(pub(crate) &'a MultiLocation);
|
||||||
|
impl<'a> EncodeLike<VersionedMultiLocation> for LatestVersionedMultiLocation<'a> {}
|
||||||
|
impl<'a> Encode for LatestVersionedMultiLocation<'a> {
|
||||||
|
fn encode(&self) -> Vec<u8> {
|
||||||
|
let mut r = VersionedMultiLocation::from(MultiLocation::default()).encode();
|
||||||
|
r.truncate(1);
|
||||||
|
self.0.using_encoded(|d| r.extend_from_slice(d));
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
pub enum VersionMigrationStage {
|
||||||
|
MigrateSupportedVersion,
|
||||||
|
MigrateVersionNotifiers,
|
||||||
|
NotifyCurrentTargets(Option<Vec<u8>>),
|
||||||
|
MigrateAndNotifyOldTargets,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VersionMigrationStage {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::MigrateSupportedVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The latest available query index.
|
/// The latest available query index.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub(super) type QueryCount<T: Config> = StorageValue<_, QueryId, ValueQuery>;
|
pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;
|
||||||
|
|
||||||
/// The ongoing queries.
|
/// The ongoing queries.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
@@ -255,8 +322,131 @@ pub mod pallet {
|
|||||||
#[pallet::getter(fn asset_trap)]
|
#[pallet::getter(fn asset_trap)]
|
||||||
pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
|
pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
|
||||||
|
|
||||||
|
/// Default version to encode XCM when latest version of destination is unknown. If `None`,
|
||||||
|
/// then the destinations whose XCM version is unknown are considered unreachable.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;
|
||||||
|
|
||||||
|
/// Latest versions that we know various locations support.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
|
||||||
|
_,
|
||||||
|
Twox64Concat,
|
||||||
|
XcmVersion,
|
||||||
|
Blake2_128Concat,
|
||||||
|
VersionedMultiLocation,
|
||||||
|
XcmVersion,
|
||||||
|
OptionQuery,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// All locations that we have requested version notifications from.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
|
||||||
|
_,
|
||||||
|
Twox64Concat,
|
||||||
|
XcmVersion,
|
||||||
|
Blake2_128Concat,
|
||||||
|
VersionedMultiLocation,
|
||||||
|
QueryId,
|
||||||
|
OptionQuery,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// The target locations that are subscribed to our version changes, as well as the most recent
|
||||||
|
/// of our versions we informed them of.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
|
||||||
|
_,
|
||||||
|
Twox64Concat,
|
||||||
|
XcmVersion,
|
||||||
|
Blake2_128Concat,
|
||||||
|
VersionedMultiLocation,
|
||||||
|
(QueryId, u64, XcmVersion),
|
||||||
|
OptionQuery,
|
||||||
|
>;
|
||||||
|
|
||||||
|
pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
|
||||||
|
impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
|
||||||
|
fn get() -> u32 {
|
||||||
|
T::VERSION_DISCOVERY_QUEUE_SIZE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destinations whose latest XCM version we would like to know. Duplicates not allowed, and
|
||||||
|
/// the `u32` counter is the number of times that a send to the destination has been attempted,
|
||||||
|
/// which is used as a prioritization.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
|
||||||
|
_,
|
||||||
|
BoundedVec<(VersionedMultiLocation, u32), VersionDiscoveryQueueSize<T>>,
|
||||||
|
ValueQuery,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// The current migration's stage, if any.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub(super) type CurrentMigration<T: Config> =
|
||||||
|
StorageValue<_, VersionMigrationStage, OptionQuery>;
|
||||||
|
|
||||||
|
#[pallet::genesis_config]
|
||||||
|
pub struct GenesisConfig {
|
||||||
|
/// The default version to encode outgoing XCM messages with.
|
||||||
|
pub safe_xcm_version: Option<XcmVersion>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl Default for GenesisConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { safe_xcm_version: Some(XCM_VERSION) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::genesis_build]
|
||||||
|
impl<T: Config> GenesisBuild<T> for GenesisConfig {
|
||||||
|
fn build(&self) {
|
||||||
|
SafeXcmVersion::<T>::set(self.safe_xcm_version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[pallet::hooks]
|
#[pallet::hooks]
|
||||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||||
|
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
|
||||||
|
let mut weight_used = 0;
|
||||||
|
if let Some(migration) = CurrentMigration::<T>::get() {
|
||||||
|
// Consume 10% of block at most
|
||||||
|
let max_weight = T::BlockWeights::get().max_block / 10;
|
||||||
|
let (w, maybe_migration) = Self::check_xcm_version_change(migration, max_weight);
|
||||||
|
CurrentMigration::<T>::set(maybe_migration);
|
||||||
|
weight_used.saturating_accrue(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we aim to get one successful version negotiation request sent per block, ordered
|
||||||
|
// by the destinations being most sent to.
|
||||||
|
let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
|
||||||
|
// TODO: correct weights.
|
||||||
|
weight_used += T::DbWeight::get().read + T::DbWeight::get().write;
|
||||||
|
q.sort_by_key(|i| i.1);
|
||||||
|
while let Some((versioned_dest, _)) = q.pop() {
|
||||||
|
if let Ok(dest) = versioned_dest.try_into() {
|
||||||
|
if Self::request_version_notify(dest).is_ok() {
|
||||||
|
// TODO: correct weights.
|
||||||
|
weight_used += T::DbWeight::get().read + T::DbWeight::get().write;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Should never fail since we only removed items. But better safe than panicking as it's
|
||||||
|
// way better to drop the queue than panic on initialize.
|
||||||
|
if let Ok(q) = BoundedVec::try_from(q) {
|
||||||
|
VersionDiscoveryQueue::<T>::put(q);
|
||||||
|
}
|
||||||
|
weight_used
|
||||||
|
}
|
||||||
|
fn on_runtime_upgrade() -> Weight {
|
||||||
|
// Start a migration (this happens before on_initialize so it'll happen later in this
|
||||||
|
// block, which should be good enough)...
|
||||||
|
CurrentMigration::<T>::put(VersionMigrationStage::default());
|
||||||
|
T::DbWeight::get().write
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[pallet::call]
|
#[pallet::call]
|
||||||
impl<T: Config> Pallet<T> {
|
impl<T: Config> Pallet<T> {
|
||||||
@@ -448,9 +638,263 @@ pub mod pallet {
|
|||||||
Self::deposit_event(Event::Attempted(outcome));
|
Self::deposit_event(Event::Attempted(outcome));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extoll that a particular destination can be communicated with through a particular
|
||||||
|
/// version of XCM.
|
||||||
|
///
|
||||||
|
/// - `origin`: Must be Root.
|
||||||
|
/// - `location`: The destination that is being described.
|
||||||
|
/// - `xcm_version`: The latest version of XCM that `location` supports.
|
||||||
|
#[pallet::weight(100_000_000u64)]
|
||||||
|
pub fn force_xcm_version(
|
||||||
|
origin: OriginFor<T>,
|
||||||
|
location: Box<MultiLocation>,
|
||||||
|
xcm_version: XcmVersion,
|
||||||
|
) -> DispatchResult {
|
||||||
|
ensure_root(origin)?;
|
||||||
|
let location = *location;
|
||||||
|
SupportedVersion::<T>::insert(
|
||||||
|
XCM_VERSION,
|
||||||
|
LatestVersionedMultiLocation(&location),
|
||||||
|
xcm_version,
|
||||||
|
);
|
||||||
|
Self::deposit_event(Event::SupportedVersionChanged(location, xcm_version));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a safe XCM version (the version that XCM should be encoded with if the most recent
|
||||||
|
/// version a destination can accept is unknown).
|
||||||
|
///
|
||||||
|
/// - `origin`: Must be Root.
|
||||||
|
/// - `maybe_xcm_version`: The default XCM encoding version, or `None` to disable.
|
||||||
|
#[pallet::weight(100_000_000u64)]
|
||||||
|
pub fn force_default_xcm_version(
|
||||||
|
origin: OriginFor<T>,
|
||||||
|
maybe_xcm_version: Option<XcmVersion>,
|
||||||
|
) -> DispatchResult {
|
||||||
|
ensure_root(origin)?;
|
||||||
|
SafeXcmVersion::<T>::set(maybe_xcm_version);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ask a location to notify us regarding their XCM version and any changes to it.
|
||||||
|
///
|
||||||
|
/// - `origin`: Must be Root.
|
||||||
|
/// - `location`: The location to which we should subscribe for XCM version notifications.
|
||||||
|
#[pallet::weight(100_000_000u64)]
|
||||||
|
pub fn force_subscribe_version_notify(
|
||||||
|
origin: OriginFor<T>,
|
||||||
|
location: Box<VersionedMultiLocation>,
|
||||||
|
) -> DispatchResult {
|
||||||
|
ensure_root(origin)?;
|
||||||
|
let location = (*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
|
||||||
|
Self::request_version_notify(location).map_err(|e| {
|
||||||
|
match e {
|
||||||
|
XcmError::InvalidLocation => Error::<T>::AlreadySubscribed,
|
||||||
|
_ => Error::<T>::InvalidOrigin,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Require that a particular destination should no longer notify us regarding any XCM
|
||||||
|
/// version changes.
|
||||||
|
///
|
||||||
|
/// - `origin`: Must be Root.
|
||||||
|
/// - `location`: The location to which we are currently subscribed for XCM version
|
||||||
|
/// notifications which we no longer desire.
|
||||||
|
#[pallet::weight(100_000_000u64)]
|
||||||
|
pub fn force_unsubscribe_version_notify(
|
||||||
|
origin: OriginFor<T>,
|
||||||
|
location: Box<VersionedMultiLocation>,
|
||||||
|
) -> DispatchResult {
|
||||||
|
ensure_root(origin)?;
|
||||||
|
let location = (*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
|
||||||
|
Self::unrequest_version_notify(location).map_err(|e| {
|
||||||
|
match e {
|
||||||
|
XcmError::InvalidLocation => Error::<T>::NoSubscription,
|
||||||
|
_ => Error::<T>::InvalidOrigin,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> Pallet<T> {
|
impl<T: Config> Pallet<T> {
|
||||||
|
/// Will always make progress, and will do its best not to use much more than `weight_cutoff`
|
||||||
|
/// in doing so.
|
||||||
|
pub(crate) fn check_xcm_version_change(
|
||||||
|
mut stage: VersionMigrationStage,
|
||||||
|
weight_cutoff: Weight,
|
||||||
|
) -> (Weight, Option<VersionMigrationStage>) {
|
||||||
|
let mut weight_used = 0;
|
||||||
|
|
||||||
|
// TODO: Correct weights for the components of this:
|
||||||
|
let todo_sv_migrate_weight: Weight = T::DbWeight::get().read + T::DbWeight::get().write;
|
||||||
|
let todo_vn_migrate_weight: Weight = T::DbWeight::get().read + T::DbWeight::get().write;
|
||||||
|
let todo_vnt_already_notified_weight: Weight = T::DbWeight::get().read;
|
||||||
|
let todo_vnt_notify_weight: Weight =
|
||||||
|
T::DbWeight::get().read + T::DbWeight::get().write * 3;
|
||||||
|
let todo_vnt_migrate_weight: Weight =
|
||||||
|
T::DbWeight::get().read + T::DbWeight::get().write;
|
||||||
|
let todo_vnt_migrate_fail_weight: Weight =
|
||||||
|
T::DbWeight::get().read + T::DbWeight::get().write;
|
||||||
|
let todo_vnt_notify_migrate_weight: Weight =
|
||||||
|
T::DbWeight::get().read + T::DbWeight::get().write * 3;
|
||||||
|
|
||||||
|
use VersionMigrationStage::*;
|
||||||
|
|
||||||
|
if stage == MigrateSupportedVersion {
|
||||||
|
// We assume that supported XCM version only ever increases, so just cycle through lower
|
||||||
|
// XCM versioned from the current.
|
||||||
|
for v in 0..XCM_VERSION {
|
||||||
|
for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
|
||||||
|
if let Ok(new_key) = old_key.into_latest() {
|
||||||
|
SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
|
||||||
|
}
|
||||||
|
weight_used.saturating_accrue(todo_sv_migrate_weight);
|
||||||
|
if weight_used >= weight_cutoff {
|
||||||
|
return (weight_used, Some(stage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage = MigrateVersionNotifiers;
|
||||||
|
}
|
||||||
|
if stage == MigrateVersionNotifiers {
|
||||||
|
for v in 0..XCM_VERSION {
|
||||||
|
for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
|
||||||
|
if let Ok(new_key) = old_key.into_latest() {
|
||||||
|
VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
|
||||||
|
}
|
||||||
|
weight_used.saturating_accrue(todo_vn_migrate_weight);
|
||||||
|
if weight_used >= weight_cutoff {
|
||||||
|
return (weight_used, Some(stage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage = NotifyCurrentTargets(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let xcm_version = T::AdvertisedXcmVersion::get();
|
||||||
|
|
||||||
|
if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
|
||||||
|
let mut iter = match maybe_last_raw_key {
|
||||||
|
Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
|
||||||
|
None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
|
||||||
|
};
|
||||||
|
while let Some((key, value)) = iter.next() {
|
||||||
|
let (query_id, max_weight, target_xcm_version) = value;
|
||||||
|
let new_key: MultiLocation = match key.clone().try_into() {
|
||||||
|
Ok(k) if target_xcm_version != xcm_version => k,
|
||||||
|
_ => {
|
||||||
|
// We don't early return here since we need to be certain that we
|
||||||
|
// make some progress.
|
||||||
|
weight_used.saturating_accrue(todo_vnt_already_notified_weight);
|
||||||
|
continue
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let response = Response::Version(xcm_version);
|
||||||
|
let message = Xcm(vec![QueryResponse { query_id, response, max_weight }]);
|
||||||
|
let event = match T::XcmRouter::send_xcm(new_key.clone(), message) {
|
||||||
|
Ok(()) => {
|
||||||
|
let value = (query_id, max_weight, xcm_version);
|
||||||
|
VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
|
||||||
|
Event::VersionChangeNotified(new_key, xcm_version)
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
|
||||||
|
Event::NotifyTargetSendFail(new_key, query_id, e.into())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Self::deposit_event(event);
|
||||||
|
weight_used.saturating_accrue(todo_vnt_notify_weight);
|
||||||
|
if weight_used >= weight_cutoff {
|
||||||
|
let last = Some(iter.last_raw_key().into());
|
||||||
|
return (weight_used, Some(NotifyCurrentTargets(last)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage = MigrateAndNotifyOldTargets;
|
||||||
|
}
|
||||||
|
if stage == MigrateAndNotifyOldTargets {
|
||||||
|
for v in 0..XCM_VERSION {
|
||||||
|
for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
|
||||||
|
let (query_id, max_weight, target_xcm_version) = value;
|
||||||
|
let new_key = match MultiLocation::try_from(old_key.clone()) {
|
||||||
|
Ok(k) => k,
|
||||||
|
Err(()) => {
|
||||||
|
Self::deposit_event(Event::NotifyTargetMigrationFail(
|
||||||
|
old_key, value.0,
|
||||||
|
));
|
||||||
|
weight_used.saturating_accrue(todo_vnt_migrate_fail_weight);
|
||||||
|
if weight_used >= weight_cutoff {
|
||||||
|
return (weight_used, Some(stage))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let versioned_key = LatestVersionedMultiLocation(&new_key);
|
||||||
|
if target_xcm_version == xcm_version {
|
||||||
|
VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
|
||||||
|
weight_used.saturating_accrue(todo_vnt_migrate_weight);
|
||||||
|
} else {
|
||||||
|
// Need to notify target.
|
||||||
|
let response = Response::Version(xcm_version);
|
||||||
|
let message =
|
||||||
|
Xcm(vec![QueryResponse { query_id, response, max_weight }]);
|
||||||
|
let event = match T::XcmRouter::send_xcm(new_key.clone(), message) {
|
||||||
|
Ok(()) => {
|
||||||
|
VersionNotifyTargets::<T>::insert(
|
||||||
|
XCM_VERSION,
|
||||||
|
versioned_key,
|
||||||
|
(query_id, max_weight, xcm_version),
|
||||||
|
);
|
||||||
|
Event::VersionChangeNotified(new_key, xcm_version)
|
||||||
|
},
|
||||||
|
Err(e) => Event::NotifyTargetSendFail(new_key, query_id, e.into()),
|
||||||
|
};
|
||||||
|
Self::deposit_event(event);
|
||||||
|
weight_used.saturating_accrue(todo_vnt_notify_migrate_weight);
|
||||||
|
}
|
||||||
|
if weight_used >= weight_cutoff {
|
||||||
|
return (weight_used, Some(stage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(weight_used, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request that `dest` informs us of its version.
|
||||||
|
pub fn request_version_notify(dest: MultiLocation) -> XcmResult {
|
||||||
|
let versioned_dest = VersionedMultiLocation::from(dest.clone());
|
||||||
|
let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
|
||||||
|
ensure!(!already, XcmError::InvalidLocation);
|
||||||
|
let query_id = QueryCounter::<T>::mutate(|q| {
|
||||||
|
let r = *q;
|
||||||
|
q.saturating_inc();
|
||||||
|
r
|
||||||
|
});
|
||||||
|
// TODO #3735: Correct weight.
|
||||||
|
let instruction = SubscribeVersion { query_id, max_response_weight: 0 };
|
||||||
|
T::XcmRouter::send_xcm(dest, Xcm(vec![instruction]))?;
|
||||||
|
VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
|
||||||
|
let query_status =
|
||||||
|
QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
|
||||||
|
Queries::<T>::insert(query_id, query_status);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request that `dest` ceases informing us of its version.
|
||||||
|
pub fn unrequest_version_notify(dest: MultiLocation) -> XcmResult {
|
||||||
|
let versioned_dest = LatestVersionedMultiLocation(&dest);
|
||||||
|
let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
|
||||||
|
.ok_or(XcmError::InvalidLocation)?;
|
||||||
|
T::XcmRouter::send_xcm(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
|
||||||
|
Queries::<T>::remove(query_id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Relay an XCM `message` from a given `interior` location in this context to a given `dest`
|
/// Relay an XCM `message` from a given `interior` location in this context to a given `dest`
|
||||||
/// location. A null `dest` is not handled.
|
/// location. A null `dest` is not handled.
|
||||||
pub fn send_xcm(
|
pub fn send_xcm(
|
||||||
@@ -475,9 +919,9 @@ pub mod pallet {
|
|||||||
maybe_notify: Option<(u8, u8)>,
|
maybe_notify: Option<(u8, u8)>,
|
||||||
timeout: T::BlockNumber,
|
timeout: T::BlockNumber,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
QueryCount::<T>::mutate(|q| {
|
QueryCounter::<T>::mutate(|q| {
|
||||||
let r = *q;
|
let r = *q;
|
||||||
*q += 1;
|
q.saturating_inc();
|
||||||
Queries::<T>::insert(
|
Queries::<T>::insert(
|
||||||
r,
|
r,
|
||||||
QueryStatus::Pending { responder: responder.into(), maybe_notify, timeout },
|
QueryStatus::Pending { responder: responder.into(), maybe_notify, timeout },
|
||||||
@@ -579,8 +1023,68 @@ pub mod pallet {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Note that a particular destination to whom we would like to send a message is unknown
|
||||||
|
/// and queue it for version discovery.
|
||||||
|
fn note_unknown_version(dest: &MultiLocation) {
|
||||||
|
let versioned_dest = VersionedMultiLocation::from(dest.clone());
|
||||||
|
VersionDiscoveryQueue::<T>::mutate(|q| {
|
||||||
|
if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
|
||||||
|
// exists - just bump the count.
|
||||||
|
q[index].1.saturating_inc();
|
||||||
|
} else {
|
||||||
|
let _ = q.try_push((versioned_dest, 1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Config> WrapVersion for Pallet<T> {
|
||||||
|
fn wrap_version<Call>(
|
||||||
|
dest: &MultiLocation,
|
||||||
|
xcm: impl Into<VersionedXcm<Call>>,
|
||||||
|
) -> Result<VersionedXcm<Call>, ()> {
|
||||||
|
SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedMultiLocation(dest))
|
||||||
|
.or_else(|| {
|
||||||
|
Self::note_unknown_version(dest);
|
||||||
|
SafeXcmVersion::<T>::get()
|
||||||
|
})
|
||||||
|
.ok_or(())
|
||||||
|
.and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config> VersionChangeNotifier for Pallet<T> {
|
||||||
|
/// Start notifying `location` should the XCM version of this chain change.
|
||||||
|
///
|
||||||
|
/// When it does, this type should ensure a `QueryResponse` message is sent with the given
|
||||||
|
/// `query_id` & `max_weight` and with a `response` of `Repsonse::Version`. This should happen
|
||||||
|
/// until/unless `stop` is called with the correct `query_id`.
|
||||||
|
///
|
||||||
|
/// If the `location` has an ongoing notification and when this function is called, then an
|
||||||
|
/// error should be returned.
|
||||||
|
fn start(dest: &MultiLocation, query_id: QueryId, max_weight: u64) -> XcmResult {
|
||||||
|
let versioned_dest = LatestVersionedMultiLocation(dest);
|
||||||
|
let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
|
||||||
|
ensure!(!already, XcmError::InvalidLocation);
|
||||||
|
|
||||||
|
let xcm_version = T::AdvertisedXcmVersion::get();
|
||||||
|
let response = Response::Version(xcm_version);
|
||||||
|
let instruction = QueryResponse { query_id, response, max_weight };
|
||||||
|
T::XcmRouter::send_xcm(dest.clone(), Xcm(vec![instruction]))?;
|
||||||
|
|
||||||
|
let value = (query_id, max_weight, xcm_version);
|
||||||
|
VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop notifying `location` should the XCM change. This is a no-op if there was never a
|
||||||
|
/// subscription.
|
||||||
|
fn stop(dest: &MultiLocation) -> XcmResult {
|
||||||
|
VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedMultiLocation(dest));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
impl<T: Config> DropAssets for Pallet<T> {
|
impl<T: Config> DropAssets for Pallet<T> {
|
||||||
fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight {
|
fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight {
|
||||||
if assets.is_empty() {
|
if assets.is_empty() {
|
||||||
@@ -590,7 +1094,7 @@ pub mod pallet {
|
|||||||
let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
|
let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
|
||||||
AssetTraps::<T>::mutate(hash, |n| *n += 1);
|
AssetTraps::<T>::mutate(hash, |n| *n += 1);
|
||||||
Self::deposit_event(Event::AssetsTrapped(hash, origin.clone(), versioned));
|
Self::deposit_event(Event::AssetsTrapped(hash, origin.clone(), versioned));
|
||||||
// TODO: Put the real weight in there.
|
// TODO #3735: Put the real weight in there.
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -623,10 +1127,13 @@ pub mod pallet {
|
|||||||
|
|
||||||
impl<T: Config> OnResponse for Pallet<T> {
|
impl<T: Config> OnResponse for Pallet<T> {
|
||||||
fn expecting_response(origin: &MultiLocation, query_id: QueryId) -> bool {
|
fn expecting_response(origin: &MultiLocation, query_id: QueryId) -> bool {
|
||||||
if let Some(QueryStatus::Pending { responder, .. }) = Queries::<T>::get(query_id) {
|
match Queries::<T>::get(query_id) {
|
||||||
return MultiLocation::try_from(responder).map_or(false, |r| origin == &r)
|
Some(QueryStatus::Pending { responder, .. }) =>
|
||||||
|
MultiLocation::try_from(responder).map_or(false, |r| origin == &r),
|
||||||
|
Some(QueryStatus::VersionNotifier { origin: r, .. }) =>
|
||||||
|
MultiLocation::try_from(r).map_or(false, |r| origin == &r),
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_response(
|
fn on_response(
|
||||||
@@ -635,87 +1142,133 @@ pub mod pallet {
|
|||||||
response: Response,
|
response: Response,
|
||||||
max_weight: Weight,
|
max_weight: Weight,
|
||||||
) -> Weight {
|
) -> Weight {
|
||||||
if let Some(QueryStatus::Pending { responder, maybe_notify, .. }) =
|
match (response, Queries::<T>::get(query_id)) {
|
||||||
Queries::<T>::get(query_id)
|
(
|
||||||
{
|
Response::Version(v),
|
||||||
if let Ok(responder) = MultiLocation::try_from(responder) {
|
Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
|
||||||
if origin == &responder {
|
) => {
|
||||||
return match maybe_notify {
|
let origin: MultiLocation = match expected_origin.try_into() {
|
||||||
Some((pallet_index, call_index)) => {
|
Ok(o) if &o == origin => o,
|
||||||
// This is a bit horrible, but we happen to know that the `Call` will
|
Ok(o) => {
|
||||||
// be built by `(pallet_index: u8, call_index: u8, QueryId, Response)`.
|
Self::deposit_event(Event::InvalidResponder(
|
||||||
// So we just encode that and then re-encode to a real Call.
|
origin.clone(),
|
||||||
let bare = (pallet_index, call_index, query_id, response);
|
query_id,
|
||||||
if let Ok(call) = bare.using_encoded(|mut bytes| {
|
Some(o),
|
||||||
<T as Config>::Call::decode(&mut bytes)
|
));
|
||||||
}) {
|
return 0
|
||||||
Queries::<T>::remove(query_id);
|
},
|
||||||
let weight = call.get_dispatch_info().weight;
|
_ => {
|
||||||
if weight > max_weight {
|
Self::deposit_event(Event::InvalidResponder(
|
||||||
let e = Event::NotifyOverweight(
|
origin.clone(),
|
||||||
query_id,
|
query_id,
|
||||||
pallet_index,
|
None,
|
||||||
call_index,
|
));
|
||||||
weight,
|
// TODO #3735: Correct weight for this.
|
||||||
max_weight,
|
return 0
|
||||||
);
|
},
|
||||||
Self::deposit_event(e);
|
};
|
||||||
return 0
|
// TODO #3735: Check max_weight is correct.
|
||||||
}
|
if !is_active {
|
||||||
let dispatch_origin = Origin::Response(origin.clone()).into();
|
Queries::<T>::insert(
|
||||||
match call.dispatch(dispatch_origin) {
|
query_id,
|
||||||
Ok(post_info) => {
|
QueryStatus::VersionNotifier {
|
||||||
let e =
|
origin: origin.clone().into(),
|
||||||
Event::Notified(query_id, pallet_index, call_index);
|
is_active: true,
|
||||||
Self::deposit_event(e);
|
|
||||||
post_info.actual_weight
|
|
||||||
},
|
|
||||||
Err(error_and_info) => {
|
|
||||||
let e = Event::NotifyDispatchError(
|
|
||||||
query_id,
|
|
||||||
pallet_index,
|
|
||||||
call_index,
|
|
||||||
);
|
|
||||||
Self::deposit_event(e);
|
|
||||||
// Not much to do with the result as it is. It's up to the parachain to ensure that the
|
|
||||||
// message makes sense.
|
|
||||||
error_and_info.post_info.actual_weight
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.unwrap_or(weight)
|
|
||||||
} else {
|
|
||||||
let e = Event::NotifyDecodeFailed(
|
|
||||||
query_id,
|
|
||||||
pallet_index,
|
|
||||||
call_index,
|
|
||||||
);
|
|
||||||
Self::deposit_event(e);
|
|
||||||
0
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
None => {
|
);
|
||||||
let e = Event::ResponseReady(query_id, response.clone());
|
}
|
||||||
Self::deposit_event(e);
|
// We're being notified of a version change.
|
||||||
let at = frame_system::Pallet::<T>::current_block_number();
|
SupportedVersion::<T>::insert(
|
||||||
let response = response.into();
|
XCM_VERSION,
|
||||||
Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
|
LatestVersionedMultiLocation(&origin),
|
||||||
0
|
v,
|
||||||
},
|
);
|
||||||
}
|
Self::deposit_event(Event::SupportedVersionChanged(origin, v));
|
||||||
} else {
|
0
|
||||||
|
},
|
||||||
|
(response, Some(QueryStatus::Pending { responder, maybe_notify, .. })) => {
|
||||||
|
let responder = match MultiLocation::try_from(responder) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => {
|
||||||
|
Self::deposit_event(Event::InvalidResponderVersion(
|
||||||
|
origin.clone(),
|
||||||
|
query_id,
|
||||||
|
));
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if origin != &responder {
|
||||||
Self::deposit_event(Event::InvalidResponder(
|
Self::deposit_event(Event::InvalidResponder(
|
||||||
origin.clone(),
|
origin.clone(),
|
||||||
query_id,
|
query_id,
|
||||||
responder,
|
Some(responder),
|
||||||
));
|
));
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
} else {
|
return match maybe_notify {
|
||||||
Self::deposit_event(Event::InvalidResponderVersion(origin.clone(), query_id));
|
Some((pallet_index, call_index)) => {
|
||||||
}
|
// This is a bit horrible, but we happen to know that the `Call` will
|
||||||
} else {
|
// be built by `(pallet_index: u8, call_index: u8, QueryId, Response)`.
|
||||||
Self::deposit_event(Event::UnexpectedResponse(origin.clone(), query_id));
|
// So we just encode that and then re-encode to a real Call.
|
||||||
|
let bare = (pallet_index, call_index, query_id, response);
|
||||||
|
if let Ok(call) = bare
|
||||||
|
.using_encoded(|mut bytes| <T as Config>::Call::decode(&mut bytes))
|
||||||
|
{
|
||||||
|
Queries::<T>::remove(query_id);
|
||||||
|
let weight = call.get_dispatch_info().weight;
|
||||||
|
if weight > max_weight {
|
||||||
|
let e = Event::NotifyOverweight(
|
||||||
|
query_id,
|
||||||
|
pallet_index,
|
||||||
|
call_index,
|
||||||
|
weight,
|
||||||
|
max_weight,
|
||||||
|
);
|
||||||
|
Self::deposit_event(e);
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
let dispatch_origin = Origin::Response(origin.clone()).into();
|
||||||
|
match call.dispatch(dispatch_origin) {
|
||||||
|
Ok(post_info) => {
|
||||||
|
let e = Event::Notified(query_id, pallet_index, call_index);
|
||||||
|
Self::deposit_event(e);
|
||||||
|
post_info.actual_weight
|
||||||
|
},
|
||||||
|
Err(error_and_info) => {
|
||||||
|
let e = Event::NotifyDispatchError(
|
||||||
|
query_id,
|
||||||
|
pallet_index,
|
||||||
|
call_index,
|
||||||
|
);
|
||||||
|
Self::deposit_event(e);
|
||||||
|
// Not much to do with the result as it is. It's up to the parachain to ensure that the
|
||||||
|
// message makes sense.
|
||||||
|
error_and_info.post_info.actual_weight
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.unwrap_or(weight)
|
||||||
|
} else {
|
||||||
|
let e =
|
||||||
|
Event::NotifyDecodeFailed(query_id, pallet_index, call_index);
|
||||||
|
Self::deposit_event(e);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let e = Event::ResponseReady(query_id, response.clone());
|
||||||
|
Self::deposit_event(e);
|
||||||
|
let at = frame_system::Pallet::<T>::current_block_number();
|
||||||
|
let response = response.into();
|
||||||
|
Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
|
||||||
|
0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
Self::deposit_event(Event::UnexpectedResponse(origin.clone(), query_id));
|
||||||
|
return 0
|
||||||
|
},
|
||||||
}
|
}
|
||||||
0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32};
|
|||||||
pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData};
|
pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData};
|
||||||
use xcm::latest::prelude::*;
|
use xcm::latest::prelude::*;
|
||||||
use xcm_builder::{
|
use xcm_builder::{
|
||||||
AccountId32Aliases, AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, Case,
|
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
|
||||||
ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia,
|
||||||
CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete,
|
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible,
|
||||||
LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
|
FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative,
|
||||||
TakeWeightCredit,
|
SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
|
||||||
};
|
};
|
||||||
use xcm_executor::XcmExecutor;
|
use xcm_executor::XcmExecutor;
|
||||||
|
|
||||||
@@ -133,9 +133,16 @@ construct_runtime!(
|
|||||||
thread_local! {
|
thread_local! {
|
||||||
pub static SENT_XCM: RefCell<Vec<(MultiLocation, Xcm<()>)>> = RefCell::new(Vec::new());
|
pub static SENT_XCM: RefCell<Vec<(MultiLocation, Xcm<()>)>> = RefCell::new(Vec::new());
|
||||||
}
|
}
|
||||||
pub fn sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> {
|
pub(crate) fn sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> {
|
||||||
SENT_XCM.with(|q| (*q.borrow()).clone())
|
SENT_XCM.with(|q| (*q.borrow()).clone())
|
||||||
}
|
}
|
||||||
|
pub(crate) fn take_sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> {
|
||||||
|
SENT_XCM.with(|q| {
|
||||||
|
let mut r = Vec::new();
|
||||||
|
std::mem::swap(&mut r, &mut *q.borrow_mut());
|
||||||
|
r
|
||||||
|
})
|
||||||
|
}
|
||||||
/// Sender that never returns error, always sends
|
/// Sender that never returns error, always sends
|
||||||
pub struct TestSendXcm;
|
pub struct TestSendXcm;
|
||||||
impl SendXcm for TestSendXcm {
|
impl SendXcm for TestSendXcm {
|
||||||
@@ -236,6 +243,7 @@ pub type Barrier = (
|
|||||||
TakeWeightCredit,
|
TakeWeightCredit,
|
||||||
AllowTopLevelPaidExecutionFrom<Everything>,
|
AllowTopLevelPaidExecutionFrom<Everything>,
|
||||||
AllowKnownQueryResponses<XcmPallet>,
|
AllowKnownQueryResponses<XcmPallet>,
|
||||||
|
AllowSubscriptionsFrom<Everything>,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub struct XcmConfig;
|
pub struct XcmConfig;
|
||||||
@@ -253,10 +261,15 @@ impl xcm_executor::Config for XcmConfig {
|
|||||||
type ResponseHandler = XcmPallet;
|
type ResponseHandler = XcmPallet;
|
||||||
type AssetTrap = XcmPallet;
|
type AssetTrap = XcmPallet;
|
||||||
type AssetClaims = XcmPallet;
|
type AssetClaims = XcmPallet;
|
||||||
|
type SubscriptionService = XcmPallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, AnyNetwork>;
|
pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, AnyNetwork>;
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub static AdvertisedXcmVersion: pallet_xcm::XcmVersion = 2;
|
||||||
|
}
|
||||||
|
|
||||||
impl pallet_xcm::Config for Test {
|
impl pallet_xcm::Config for Test {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||||
@@ -270,6 +283,8 @@ impl pallet_xcm::Config for Test {
|
|||||||
type LocationInverter = LocationInverter<Ancestry>;
|
type LocationInverter = LocationInverter<Ancestry>;
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
|
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||||
|
type AdvertisedXcmVersion = AdvertisedXcmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl origin::Config for Test {}
|
impl origin::Config for Test {}
|
||||||
|
|||||||
@@ -14,13 +14,20 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::{mock::*, AssetTraps, QueryStatus};
|
use crate::{
|
||||||
use frame_support::{assert_noop, assert_ok, traits::Currency};
|
mock::*, AssetTraps, CurrentMigration, Error, LatestVersionedMultiLocation, Queries,
|
||||||
|
QueryStatus, VersionDiscoveryQueue, VersionNotifiers, VersionNotifyTargets,
|
||||||
|
};
|
||||||
|
use frame_support::{
|
||||||
|
assert_noop, assert_ok,
|
||||||
|
traits::{Currency, Hooks},
|
||||||
|
};
|
||||||
use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId};
|
use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId};
|
||||||
use sp_runtime::traits::{BlakeTwo256, Hash};
|
use sp_runtime::traits::{BlakeTwo256, Hash};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use xcm::{latest::prelude::*, VersionedMultiAssets, VersionedXcm};
|
use xcm::prelude::*;
|
||||||
use xcm_executor::XcmExecutor;
|
use xcm_builder::AllowKnownQueryResponses;
|
||||||
|
use xcm_executor::{traits::ShouldExecute, XcmExecutor};
|
||||||
|
|
||||||
const ALICE: AccountId = AccountId::new([0u8; 32]);
|
const ALICE: AccountId = AccountId::new([0u8; 32]);
|
||||||
const BOB: AccountId = AccountId::new([1u8; 32]);
|
const BOB: AccountId = AccountId::new([1u8; 32]);
|
||||||
@@ -375,3 +382,423 @@ fn trapped_assets_can_be_claimed() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fake_latest_versioned_multilocation_works() {
|
||||||
|
use codec::Encode;
|
||||||
|
let remote = Parachain(1000).into();
|
||||||
|
let versioned_remote = LatestVersionedMultiLocation(&remote);
|
||||||
|
assert_eq!(versioned_remote.encode(), VersionedMultiLocation::from(remote.clone()).encode());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_subscription_works() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
let remote = Parachain(1000).into();
|
||||||
|
assert_ok!(XcmPallet::force_subscribe_version_notify(
|
||||||
|
Origin::root(),
|
||||||
|
Box::new(remote.clone().into()),
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Queries::<Test>::iter().collect::<Vec<_>>(),
|
||||||
|
vec![(
|
||||||
|
0,
|
||||||
|
QueryStatus::VersionNotifier { origin: remote.clone().into(), is_active: false }
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
VersionNotifiers::<Test>::iter().collect::<Vec<_>>(),
|
||||||
|
vec![(2, remote.clone().into(), 0)]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
take_sent_xcm(),
|
||||||
|
vec![(
|
||||||
|
remote.clone(),
|
||||||
|
Xcm(vec![SubscribeVersion { query_id: 0, max_response_weight: 0 }]),
|
||||||
|
),]
|
||||||
|
);
|
||||||
|
|
||||||
|
let weight = BaseXcmWeight::get();
|
||||||
|
let mut message = Xcm::<()>(vec![
|
||||||
|
// Remote supports XCM v1
|
||||||
|
QueryResponse { query_id: 0, max_weight: 0, response: Response::Version(1) },
|
||||||
|
]);
|
||||||
|
assert_ok!(AllowKnownQueryResponses::<XcmPallet>::should_execute(
|
||||||
|
&remote,
|
||||||
|
&mut message,
|
||||||
|
weight,
|
||||||
|
&mut 0
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subscriptions_increment_id() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
let remote = Parachain(1000).into();
|
||||||
|
assert_ok!(XcmPallet::force_subscribe_version_notify(
|
||||||
|
Origin::root(),
|
||||||
|
Box::new(remote.clone().into()),
|
||||||
|
));
|
||||||
|
|
||||||
|
let remote2 = Parachain(1001).into();
|
||||||
|
assert_ok!(XcmPallet::force_subscribe_version_notify(
|
||||||
|
Origin::root(),
|
||||||
|
Box::new(remote2.clone().into()),
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
take_sent_xcm(),
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
remote.clone(),
|
||||||
|
Xcm(vec![SubscribeVersion { query_id: 0, max_response_weight: 0 }]),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
remote2.clone(),
|
||||||
|
Xcm(vec![SubscribeVersion { query_id: 1, max_response_weight: 0 }]),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn double_subscription_fails() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
let remote = Parachain(1000).into();
|
||||||
|
assert_ok!(XcmPallet::force_subscribe_version_notify(
|
||||||
|
Origin::root(),
|
||||||
|
Box::new(remote.clone().into()),
|
||||||
|
));
|
||||||
|
assert_noop!(
|
||||||
|
XcmPallet::force_subscribe_version_notify(
|
||||||
|
Origin::root(),
|
||||||
|
Box::new(remote.clone().into())
|
||||||
|
),
|
||||||
|
Error::<Test>::AlreadySubscribed,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unsubscribe_works() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
let remote = Parachain(1000).into();
|
||||||
|
assert_ok!(XcmPallet::force_subscribe_version_notify(
|
||||||
|
Origin::root(),
|
||||||
|
Box::new(remote.clone().into()),
|
||||||
|
));
|
||||||
|
assert_ok!(XcmPallet::force_unsubscribe_version_notify(
|
||||||
|
Origin::root(),
|
||||||
|
Box::new(remote.clone().into())
|
||||||
|
));
|
||||||
|
assert_noop!(
|
||||||
|
XcmPallet::force_unsubscribe_version_notify(
|
||||||
|
Origin::root(),
|
||||||
|
Box::new(remote.clone().into())
|
||||||
|
),
|
||||||
|
Error::<Test>::NoSubscription,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
take_sent_xcm(),
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
remote.clone(),
|
||||||
|
Xcm(vec![SubscribeVersion { query_id: 0, max_response_weight: 0 }]),
|
||||||
|
),
|
||||||
|
(remote.clone(), Xcm(vec![UnsubscribeVersion]),),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parachain 1000 is asking us for a version subscription.
|
||||||
|
#[test]
|
||||||
|
fn subscription_side_works() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
AdvertisedXcmVersion::set(1);
|
||||||
|
|
||||||
|
let remote = Parachain(1000).into();
|
||||||
|
let weight = BaseXcmWeight::get();
|
||||||
|
let message = Xcm(vec![SubscribeVersion { query_id: 0, max_response_weight: 0 }]);
|
||||||
|
let r = XcmExecutor::<XcmConfig>::execute_xcm(remote.clone(), message, weight);
|
||||||
|
assert_eq!(r, Outcome::Complete(weight));
|
||||||
|
|
||||||
|
let instr = QueryResponse { query_id: 0, max_weight: 0, response: Response::Version(1) };
|
||||||
|
assert_eq!(take_sent_xcm(), vec![(remote.clone(), Xcm(vec![instr]))]);
|
||||||
|
|
||||||
|
// A runtime upgrade which doesn't alter the version sends no notifications.
|
||||||
|
XcmPallet::on_runtime_upgrade();
|
||||||
|
XcmPallet::on_initialize(1);
|
||||||
|
assert_eq!(take_sent_xcm(), vec![]);
|
||||||
|
|
||||||
|
// New version.
|
||||||
|
AdvertisedXcmVersion::set(2);
|
||||||
|
|
||||||
|
// A runtime upgrade which alters the version does send notifications.
|
||||||
|
XcmPallet::on_runtime_upgrade();
|
||||||
|
XcmPallet::on_initialize(2);
|
||||||
|
let instr = QueryResponse { query_id: 0, max_weight: 0, response: Response::Version(2) };
|
||||||
|
assert_eq!(take_sent_xcm(), vec![(remote.clone(), Xcm(vec![instr]))]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subscription_side_upgrades_work_with_notify() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
AdvertisedXcmVersion::set(1);
|
||||||
|
|
||||||
|
// An entry from a previous runtime with v0 XCM.
|
||||||
|
let v0_location = xcm::v0::MultiLocation::X1(xcm::v0::Junction::Parachain(1000));
|
||||||
|
let v0_location = VersionedMultiLocation::from(v0_location);
|
||||||
|
VersionNotifyTargets::<Test>::insert(0, v0_location, (69, 0, 1));
|
||||||
|
let v1_location = Parachain(1001).into().versioned();
|
||||||
|
VersionNotifyTargets::<Test>::insert(1, v1_location, (70, 0, 1));
|
||||||
|
let v2_location = Parachain(1002).into().versioned();
|
||||||
|
VersionNotifyTargets::<Test>::insert(2, v2_location, (71, 0, 1));
|
||||||
|
|
||||||
|
// New version.
|
||||||
|
AdvertisedXcmVersion::set(2);
|
||||||
|
|
||||||
|
// A runtime upgrade which alters the version does send notifications.
|
||||||
|
XcmPallet::on_runtime_upgrade();
|
||||||
|
XcmPallet::on_initialize(1);
|
||||||
|
|
||||||
|
let instr0 = QueryResponse { query_id: 69, max_weight: 0, response: Response::Version(2) };
|
||||||
|
let instr1 = QueryResponse { query_id: 70, max_weight: 0, response: Response::Version(2) };
|
||||||
|
let instr2 = QueryResponse { query_id: 71, max_weight: 0, response: Response::Version(2) };
|
||||||
|
let mut sent = take_sent_xcm();
|
||||||
|
sent.sort_by_key(|k| match (k.1).0[0] {
|
||||||
|
QueryResponse { query_id: q, .. } => q,
|
||||||
|
_ => 0,
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
sent,
|
||||||
|
vec![
|
||||||
|
(Parachain(1000).into(), Xcm(vec![instr0])),
|
||||||
|
(Parachain(1001).into(), Xcm(vec![instr1])),
|
||||||
|
(Parachain(1002).into(), Xcm(vec![instr2])),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut contents = VersionNotifyTargets::<Test>::iter().collect::<Vec<_>>();
|
||||||
|
contents.sort_by_key(|k| k.2);
|
||||||
|
assert_eq!(
|
||||||
|
contents,
|
||||||
|
vec![
|
||||||
|
(2, Parachain(1000).into().versioned(), (69, 0, 2)),
|
||||||
|
(2, Parachain(1001).into().versioned(), (70, 0, 2)),
|
||||||
|
(2, Parachain(1002).into().versioned(), (71, 0, 2)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subscription_side_upgrades_work_without_notify() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
// An entry from a previous runtime with v0 XCM.
|
||||||
|
let v0_location = xcm::v0::MultiLocation::X1(xcm::v0::Junction::Parachain(1000));
|
||||||
|
let v0_location = VersionedMultiLocation::from(v0_location);
|
||||||
|
VersionNotifyTargets::<Test>::insert(0, v0_location, (69, 0, 2));
|
||||||
|
let v1_location = Parachain(1001).into().versioned();
|
||||||
|
VersionNotifyTargets::<Test>::insert(1, v1_location, (70, 0, 2));
|
||||||
|
let v2_location = Parachain(1002).into().versioned();
|
||||||
|
VersionNotifyTargets::<Test>::insert(2, v2_location, (71, 0, 2));
|
||||||
|
|
||||||
|
// A runtime upgrade which alters the version does send notifications.
|
||||||
|
XcmPallet::on_runtime_upgrade();
|
||||||
|
XcmPallet::on_initialize(1);
|
||||||
|
|
||||||
|
let mut contents = VersionNotifyTargets::<Test>::iter().collect::<Vec<_>>();
|
||||||
|
contents.sort_by_key(|k| k.2);
|
||||||
|
assert_eq!(
|
||||||
|
contents,
|
||||||
|
vec![
|
||||||
|
(2, Parachain(1000).into().versioned(), (69, 0, 2)),
|
||||||
|
(2, Parachain(1001).into().versioned(), (70, 0, 2)),
|
||||||
|
(2, Parachain(1002).into().versioned(), (71, 0, 2)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subscriber_side_subscription_works() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
let remote = Parachain(1000).into();
|
||||||
|
assert_ok!(XcmPallet::force_subscribe_version_notify(
|
||||||
|
Origin::root(),
|
||||||
|
Box::new(remote.clone().into()),
|
||||||
|
));
|
||||||
|
take_sent_xcm();
|
||||||
|
|
||||||
|
// Assume subscription target is working ok.
|
||||||
|
|
||||||
|
let weight = BaseXcmWeight::get();
|
||||||
|
let message = Xcm(vec![
|
||||||
|
// Remote supports XCM v1
|
||||||
|
QueryResponse { query_id: 0, max_weight: 0, response: Response::Version(1) },
|
||||||
|
]);
|
||||||
|
let r = XcmExecutor::<XcmConfig>::execute_xcm(remote.clone(), message, weight);
|
||||||
|
assert_eq!(r, Outcome::Complete(weight));
|
||||||
|
assert_eq!(take_sent_xcm(), vec![]);
|
||||||
|
|
||||||
|
// This message cannot be sent to a v1 remote.
|
||||||
|
let v2_msg = Xcm::<()>(vec![Trap(0)]);
|
||||||
|
assert_eq!(XcmPallet::wrap_version(&remote, v2_msg.clone()), Err(()));
|
||||||
|
|
||||||
|
let message = Xcm(vec![
|
||||||
|
// Remote upgraded to XCM v2
|
||||||
|
QueryResponse { query_id: 0, max_weight: 0, response: Response::Version(2) },
|
||||||
|
]);
|
||||||
|
let r = XcmExecutor::<XcmConfig>::execute_xcm(remote.clone(), message, weight);
|
||||||
|
assert_eq!(r, Outcome::Complete(weight));
|
||||||
|
|
||||||
|
// This message can now be sent to remote as it's v2.
|
||||||
|
assert_eq!(
|
||||||
|
XcmPallet::wrap_version(&remote, v2_msg.clone()),
|
||||||
|
Ok(VersionedXcm::from(v2_msg))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We should autosubscribe when we don't know the remote's version.
|
||||||
|
#[test]
|
||||||
|
fn auto_subscription_works() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
let remote0 = Parachain(1000).into();
|
||||||
|
let remote1 = Parachain(1001).into();
|
||||||
|
|
||||||
|
assert_ok!(XcmPallet::force_default_xcm_version(Origin::root(), Some(1)));
|
||||||
|
|
||||||
|
// Wrapping a version for a destination we don't know elicits a subscription.
|
||||||
|
let v1_msg = xcm::v1::Xcm::<()>::QueryResponse {
|
||||||
|
query_id: 1,
|
||||||
|
response: xcm::v1::Response::Assets(vec![].into()),
|
||||||
|
};
|
||||||
|
let v2_msg = Xcm::<()>(vec![Trap(0)]);
|
||||||
|
assert_eq!(
|
||||||
|
XcmPallet::wrap_version(&remote0, v1_msg.clone()),
|
||||||
|
Ok(VersionedXcm::from(v1_msg.clone())),
|
||||||
|
);
|
||||||
|
assert_eq!(XcmPallet::wrap_version(&remote0, v2_msg.clone()), Err(()));
|
||||||
|
let expected = vec![(remote0.clone().into(), 2)];
|
||||||
|
assert_eq!(VersionDiscoveryQueue::<Test>::get().into_inner(), expected);
|
||||||
|
|
||||||
|
assert_eq!(XcmPallet::wrap_version(&remote0, v2_msg.clone()), Err(()));
|
||||||
|
assert_eq!(XcmPallet::wrap_version(&remote1, v2_msg.clone()), Err(()));
|
||||||
|
let expected = vec![(remote0.clone().into(), 3), (remote1.clone().into(), 1)];
|
||||||
|
assert_eq!(VersionDiscoveryQueue::<Test>::get().into_inner(), expected);
|
||||||
|
|
||||||
|
XcmPallet::on_initialize(1);
|
||||||
|
assert_eq!(
|
||||||
|
take_sent_xcm(),
|
||||||
|
vec![(
|
||||||
|
remote0.clone(),
|
||||||
|
Xcm(vec![SubscribeVersion { query_id: 0, max_response_weight: 0 }]),
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assume remote0 is working ok and XCM version 2.
|
||||||
|
|
||||||
|
let weight = BaseXcmWeight::get();
|
||||||
|
let message = Xcm(vec![
|
||||||
|
// Remote supports XCM v2
|
||||||
|
QueryResponse { query_id: 0, max_weight: 0, response: Response::Version(2) },
|
||||||
|
]);
|
||||||
|
let r = XcmExecutor::<XcmConfig>::execute_xcm(remote0.clone(), message, weight);
|
||||||
|
assert_eq!(r, Outcome::Complete(weight));
|
||||||
|
|
||||||
|
// This message can now be sent to remote0 as it's v2.
|
||||||
|
assert_eq!(
|
||||||
|
XcmPallet::wrap_version(&remote0, v2_msg.clone()),
|
||||||
|
Ok(VersionedXcm::from(v2_msg.clone()))
|
||||||
|
);
|
||||||
|
|
||||||
|
XcmPallet::on_initialize(2);
|
||||||
|
assert_eq!(
|
||||||
|
take_sent_xcm(),
|
||||||
|
vec![(
|
||||||
|
remote1.clone(),
|
||||||
|
Xcm(vec![SubscribeVersion { query_id: 1, max_response_weight: 0 }]),
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assume remote1 is working ok and XCM version 1.
|
||||||
|
|
||||||
|
let weight = BaseXcmWeight::get();
|
||||||
|
let message = Xcm(vec![
|
||||||
|
// Remote supports XCM v1
|
||||||
|
QueryResponse { query_id: 1, max_weight: 0, response: Response::Version(1) },
|
||||||
|
]);
|
||||||
|
let r = XcmExecutor::<XcmConfig>::execute_xcm(remote1.clone(), message, weight);
|
||||||
|
assert_eq!(r, Outcome::Complete(weight));
|
||||||
|
|
||||||
|
// v2 messages cannot be sent to remote1...
|
||||||
|
assert_eq!(XcmPallet::wrap_version(&remote1, v1_msg.clone()), Ok(VersionedXcm::V1(v1_msg)));
|
||||||
|
assert_eq!(XcmPallet::wrap_version(&remote1, v2_msg.clone()), Err(()));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subscription_side_upgrades_work_with_multistage_notify() {
|
||||||
|
new_test_ext_with_balances(vec![]).execute_with(|| {
|
||||||
|
AdvertisedXcmVersion::set(1);
|
||||||
|
|
||||||
|
// An entry from a previous runtime with v0 XCM.
|
||||||
|
let v0_location = xcm::v0::MultiLocation::X1(xcm::v0::Junction::Parachain(1000));
|
||||||
|
let v0_location = VersionedMultiLocation::from(v0_location);
|
||||||
|
VersionNotifyTargets::<Test>::insert(0, v0_location, (69, 0, 1));
|
||||||
|
let v1_location = Parachain(1001).into().versioned();
|
||||||
|
VersionNotifyTargets::<Test>::insert(1, v1_location, (70, 0, 1));
|
||||||
|
let v2_location = Parachain(1002).into().versioned();
|
||||||
|
VersionNotifyTargets::<Test>::insert(2, v2_location, (71, 0, 1));
|
||||||
|
|
||||||
|
// New version.
|
||||||
|
AdvertisedXcmVersion::set(2);
|
||||||
|
|
||||||
|
// A runtime upgrade which alters the version does send notifications.
|
||||||
|
XcmPallet::on_runtime_upgrade();
|
||||||
|
let mut maybe_migration = CurrentMigration::<Test>::take();
|
||||||
|
let mut counter = 0;
|
||||||
|
while let Some(migration) = maybe_migration.take() {
|
||||||
|
counter += 1;
|
||||||
|
let (_, m) = XcmPallet::check_xcm_version_change(migration, 0);
|
||||||
|
maybe_migration = m;
|
||||||
|
}
|
||||||
|
assert_eq!(counter, 4);
|
||||||
|
|
||||||
|
let instr0 = QueryResponse { query_id: 69, max_weight: 0, response: Response::Version(2) };
|
||||||
|
let instr1 = QueryResponse { query_id: 70, max_weight: 0, response: Response::Version(2) };
|
||||||
|
let instr2 = QueryResponse { query_id: 71, max_weight: 0, response: Response::Version(2) };
|
||||||
|
let mut sent = take_sent_xcm();
|
||||||
|
sent.sort_by_key(|k| match (k.1).0[0] {
|
||||||
|
QueryResponse { query_id: q, .. } => q,
|
||||||
|
_ => 0,
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
sent,
|
||||||
|
vec![
|
||||||
|
(Parachain(1000).into(), Xcm(vec![instr0])),
|
||||||
|
(Parachain(1001).into(), Xcm(vec![instr1])),
|
||||||
|
(Parachain(1002).into(), Xcm(vec![instr2])),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut contents = VersionNotifyTargets::<Test>::iter().collect::<Vec<_>>();
|
||||||
|
contents.sort_by_key(|k| k.2);
|
||||||
|
assert_eq!(
|
||||||
|
contents,
|
||||||
|
vec![
|
||||||
|
(2, Parachain(1000).into().versioned(), (69, 0, 2)),
|
||||||
|
(2, Parachain(1001).into().versioned(), (70, 0, 2)),
|
||||||
|
(2, Parachain(1002).into().versioned(), (71, 0, 2)),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
+66
-2
@@ -45,6 +45,9 @@ pub use double_encoded::DoubleEncoded;
|
|||||||
/// Maximum nesting level for XCM decoding.
|
/// Maximum nesting level for XCM decoding.
|
||||||
pub const MAX_XCM_DECODE_DEPTH: u32 = 8;
|
pub const MAX_XCM_DECODE_DEPTH: u32 = 8;
|
||||||
|
|
||||||
|
/// A version of XCM.
|
||||||
|
pub type Version = u32;
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
pub enum Unsupported {}
|
pub enum Unsupported {}
|
||||||
impl Encode for Unsupported {}
|
impl Encode for Unsupported {}
|
||||||
@@ -54,6 +57,17 @@ impl Decode for Unsupported {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to convert `self` into a particular version of itself.
|
||||||
|
pub trait IntoVersion: Sized {
|
||||||
|
/// Consume `self` and return same value expressed in some particular `version` of XCM.
|
||||||
|
fn into_version(self, version: Version) -> Result<Self, ()>;
|
||||||
|
|
||||||
|
/// Consume `self` and return same value expressed the latest version of XCM.
|
||||||
|
fn into_latest(self) -> Result<Self, ()> {
|
||||||
|
self.into_version(latest::VERSION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A single `MultiLocation` value, together with its version code.
|
/// A single `MultiLocation` value, together with its version code.
|
||||||
#[derive(Derivative, Encode, Decode)]
|
#[derive(Derivative, Encode, Decode)]
|
||||||
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
|
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
|
||||||
@@ -64,6 +78,16 @@ pub enum VersionedMultiLocation {
|
|||||||
V1(v1::MultiLocation),
|
V1(v1::MultiLocation),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoVersion for VersionedMultiLocation {
|
||||||
|
fn into_version(self, n: Version) -> Result<Self, ()> {
|
||||||
|
Ok(match n {
|
||||||
|
0 => Self::V0(self.try_into()?),
|
||||||
|
1 | 2 => Self::V1(self.try_into()?),
|
||||||
|
_ => return Err(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<v0::MultiLocation> for VersionedMultiLocation {
|
impl From<v0::MultiLocation> for VersionedMultiLocation {
|
||||||
fn from(x: v0::MultiLocation) -> Self {
|
fn from(x: v0::MultiLocation) -> Self {
|
||||||
VersionedMultiLocation::V0(x)
|
VersionedMultiLocation::V0(x)
|
||||||
@@ -109,6 +133,17 @@ pub enum VersionedResponse {
|
|||||||
V2(v2::Response),
|
V2(v2::Response),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoVersion for VersionedResponse {
|
||||||
|
fn into_version(self, n: Version) -> Result<Self, ()> {
|
||||||
|
Ok(match n {
|
||||||
|
0 => Self::V0(self.try_into()?),
|
||||||
|
1 => Self::V1(self.try_into()?),
|
||||||
|
2 => Self::V2(self.try_into()?),
|
||||||
|
_ => return Err(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<v0::Response> for VersionedResponse {
|
impl From<v0::Response> for VersionedResponse {
|
||||||
fn from(x: v0::Response) -> Self {
|
fn from(x: v0::Response) -> Self {
|
||||||
VersionedResponse::V0(x)
|
VersionedResponse::V0(x)
|
||||||
@@ -173,6 +208,16 @@ pub enum VersionedMultiAsset {
|
|||||||
V1(v1::MultiAsset),
|
V1(v1::MultiAsset),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoVersion for VersionedMultiAsset {
|
||||||
|
fn into_version(self, n: Version) -> Result<Self, ()> {
|
||||||
|
Ok(match n {
|
||||||
|
0 => Self::V0(self.try_into()?),
|
||||||
|
1 | 2 => Self::V1(self.try_into()?),
|
||||||
|
_ => return Err(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<v0::MultiAsset> for VersionedMultiAsset {
|
impl From<v0::MultiAsset> for VersionedMultiAsset {
|
||||||
fn from(x: v0::MultiAsset) -> Self {
|
fn from(x: v0::MultiAsset) -> Self {
|
||||||
VersionedMultiAsset::V0(x)
|
VersionedMultiAsset::V0(x)
|
||||||
@@ -217,8 +262,8 @@ pub enum VersionedMultiAssets {
|
|||||||
V1(v1::MultiAssets),
|
V1(v1::MultiAssets),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VersionedMultiAssets {
|
impl IntoVersion for VersionedMultiAssets {
|
||||||
pub fn into_version(self, n: u32) -> Result<Self, ()> {
|
fn into_version(self, n: Version) -> Result<Self, ()> {
|
||||||
Ok(match n {
|
Ok(match n {
|
||||||
0 => Self::V0(self.try_into()?),
|
0 => Self::V0(self.try_into()?),
|
||||||
1 | 2 => Self::V1(self.try_into()?),
|
1 | 2 => Self::V1(self.try_into()?),
|
||||||
@@ -272,6 +317,17 @@ pub enum VersionedXcm<Call> {
|
|||||||
V2(v2::Xcm<Call>),
|
V2(v2::Xcm<Call>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<C> IntoVersion for VersionedXcm<C> {
|
||||||
|
fn into_version(self, n: Version) -> Result<Self, ()> {
|
||||||
|
Ok(match n {
|
||||||
|
0 => Self::V0(self.try_into()?),
|
||||||
|
1 => Self::V1(self.try_into()?),
|
||||||
|
2 => Self::V2(self.try_into()?),
|
||||||
|
_ => return Err(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Call> From<v0::Xcm<Call>> for VersionedXcm<Call> {
|
impl<Call> From<v0::Xcm<Call>> for VersionedXcm<Call> {
|
||||||
fn from(x: v0::Xcm<Call>) -> Self {
|
fn from(x: v0::Xcm<Call>) -> Self {
|
||||||
VersionedXcm::V0(x)
|
VersionedXcm::V0(x)
|
||||||
@@ -383,6 +439,14 @@ pub type AlwaysLatest = AlwaysV1;
|
|||||||
/// `WrapVersion` implementation which attempts to always convert the XCM to the release version before wrapping it.
|
/// `WrapVersion` implementation which attempts to always convert the XCM to the release version before wrapping it.
|
||||||
pub type AlwaysRelease = AlwaysV0;
|
pub type AlwaysRelease = AlwaysV0;
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use super::{
|
||||||
|
latest::prelude::*, AlwaysLatest, AlwaysRelease, AlwaysV0, AlwaysV1, AlwaysV2, IntoVersion,
|
||||||
|
Unsupported, Version as XcmVersion, VersionedMultiAsset, VersionedMultiAssets,
|
||||||
|
VersionedMultiLocation, VersionedResponse, VersionedXcm, WrapVersion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub mod opaque {
|
pub mod opaque {
|
||||||
pub mod v0 {
|
pub mod v0 {
|
||||||
// Everything from v0
|
// Everything from v0
|
||||||
|
|||||||
@@ -326,6 +326,7 @@ impl TryFrom<Response1> for Response {
|
|||||||
fn try_from(new_response: Response1) -> result::Result<Self, ()> {
|
fn try_from(new_response: Response1) -> result::Result<Self, ()> {
|
||||||
Ok(match new_response {
|
Ok(match new_response {
|
||||||
Response1::Assets(assets) => Self::Assets(assets.try_into()?),
|
Response1::Assets(assets) => Self::Assets(assets.try_into()?),
|
||||||
|
Response1::Version(..) => return Err(()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,6 +380,7 @@ impl<Call> TryFrom<Xcm1<Call>> for Xcm<Call> {
|
|||||||
who: MultiLocation1 { interior: who, parents: 0 }.try_into()?,
|
who: MultiLocation1 { interior: who, parents: 0 }.try_into()?,
|
||||||
message: alloc::boxed::Box::new((*message).try_into()?),
|
message: alloc::boxed::Box::new((*message).try_into()?),
|
||||||
},
|
},
|
||||||
|
Xcm1::SubscribeVersion { .. } | Xcm1::UnsubscribeVersion => return Err(()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ pub mod prelude {
|
|||||||
pub enum Response {
|
pub enum Response {
|
||||||
/// Some assets.
|
/// Some assets.
|
||||||
Assets(MultiAssets),
|
Assets(MultiAssets),
|
||||||
|
/// An XCM version.
|
||||||
|
Version(super::Version),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cross-Consensus Message: A message from one consensus system to another.
|
/// Cross-Consensus Message: A message from one consensus system to another.
|
||||||
@@ -270,6 +272,25 @@ pub enum Xcm<Call> {
|
|||||||
/// Errors:
|
/// Errors:
|
||||||
#[codec(index = 10)]
|
#[codec(index = 10)]
|
||||||
RelayedFrom { who: InteriorMultiLocation, message: alloc::boxed::Box<Xcm<Call>> },
|
RelayedFrom { who: InteriorMultiLocation, message: alloc::boxed::Box<Xcm<Call>> },
|
||||||
|
|
||||||
|
/// Ask the destination system to respond with the most recent version of XCM that they
|
||||||
|
/// support in a `QueryResponse` instruction. Any changes to this should also elicit similar
|
||||||
|
/// responses when they happen.
|
||||||
|
///
|
||||||
|
/// Kind: *Instruction*
|
||||||
|
#[codec(index = 11)]
|
||||||
|
SubscribeVersion {
|
||||||
|
#[codec(compact)]
|
||||||
|
query_id: u64,
|
||||||
|
#[codec(compact)]
|
||||||
|
max_response_weight: u64,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Cancel the effect of a previous `SubscribeVersion` instruction.
|
||||||
|
///
|
||||||
|
/// Kind: *Instruction*
|
||||||
|
#[codec(index = 12)]
|
||||||
|
UnsubscribeVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Call> Xcm<Call> {
|
impl<Call> Xcm<Call> {
|
||||||
@@ -302,6 +323,9 @@ impl<Call> Xcm<Call> {
|
|||||||
Transact { origin_type, require_weight_at_most, call: call.into() },
|
Transact { origin_type, require_weight_at_most, call: call.into() },
|
||||||
RelayedFrom { who, message } =>
|
RelayedFrom { who, message } =>
|
||||||
RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) },
|
RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) },
|
||||||
|
SubscribeVersion { query_id, max_response_weight } =>
|
||||||
|
SubscribeVersion { query_id, max_response_weight },
|
||||||
|
UnsubscribeVersion => UnsubscribeVersion,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,6 +451,9 @@ impl<Call> TryFrom<NewXcm<Call>> for Xcm<Call> {
|
|||||||
HrmpChannelClosing { initiator, sender, recipient },
|
HrmpChannelClosing { initiator, sender, recipient },
|
||||||
Instruction::Transact { origin_type, require_weight_at_most, call } =>
|
Instruction::Transact { origin_type, require_weight_at_most, call } =>
|
||||||
Transact { origin_type, require_weight_at_most, call },
|
Transact { origin_type, require_weight_at_most, call },
|
||||||
|
Instruction::SubscribeVersion { query_id, max_response_weight } =>
|
||||||
|
SubscribeVersion { query_id, max_response_weight },
|
||||||
|
Instruction::UnsubscribeVersion => UnsubscribeVersion,
|
||||||
_ => return Err(()),
|
_ => return Err(()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -438,6 +465,7 @@ impl TryFrom<NewResponse> for Response {
|
|||||||
fn try_from(response: NewResponse) -> result::Result<Self, ()> {
|
fn try_from(response: NewResponse) -> result::Result<Self, ()> {
|
||||||
match response {
|
match response {
|
||||||
NewResponse::Assets(assets) => Ok(Self::Assets(assets)),
|
NewResponse::Assets(assets) => Ok(Self::Assets(assets)),
|
||||||
|
NewResponse::Version(version) => Ok(Self::Version(version)),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,23 +54,28 @@ pub struct MultiLocation {
|
|||||||
pub interior: Junctions,
|
pub interior: Junctions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for MultiLocation {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { parents: 0, interior: Junctions::Here }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A relative location which is constrained to be an interior location of the context.
|
/// A relative location which is constrained to be an interior location of the context.
|
||||||
///
|
///
|
||||||
/// See also `MultiLocation`.
|
/// See also `MultiLocation`.
|
||||||
pub type InteriorMultiLocation = Junctions;
|
pub type InteriorMultiLocation = Junctions;
|
||||||
|
|
||||||
impl Default for MultiLocation {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::here()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MultiLocation {
|
impl MultiLocation {
|
||||||
/// Creates a new `MultiLocation` with the given number of parents and interior junctions.
|
/// Creates a new `MultiLocation` with the given number of parents and interior junctions.
|
||||||
pub fn new(parents: u8, junctions: Junctions) -> MultiLocation {
|
pub fn new(parents: u8, junctions: Junctions) -> MultiLocation {
|
||||||
MultiLocation { parents, interior: junctions }
|
MultiLocation { parents, interior: junctions }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consume `self` and return the equivalent `VersionedMultiLocation` value.
|
||||||
|
pub fn versioned(self) -> crate::VersionedMultiLocation {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new `MultiLocation` with 0 parents and a `Here` interior.
|
/// Creates a new `MultiLocation` with 0 parents and a `Here` interior.
|
||||||
///
|
///
|
||||||
/// The resulting `MultiLocation` can be interpreted as the "current consensus system".
|
/// The resulting `MultiLocation` can be interpreted as the "current consensus system".
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ pub use super::v1::{
|
|||||||
MultiLocation, NetworkId, OriginKind, Parent, ParentThen, WildFungibility, WildMultiAsset,
|
MultiLocation, NetworkId, OriginKind, Parent, ParentThen, WildFungibility, WildMultiAsset,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This module's XCM version.
|
||||||
|
pub const VERSION: super::Version = 2;
|
||||||
|
|
||||||
|
/// An identifier for a query.
|
||||||
|
pub type QueryId = u64;
|
||||||
|
|
||||||
#[derive(Derivative, Default, Encode, Decode)]
|
#[derive(Derivative, Default, Encode, Decode)]
|
||||||
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
|
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
|
||||||
#[codec(encode_bound())]
|
#[codec(encode_bound())]
|
||||||
@@ -116,11 +122,12 @@ pub mod prelude {
|
|||||||
MultiAssetFilter::{self, *},
|
MultiAssetFilter::{self, *},
|
||||||
MultiAssets, MultiLocation,
|
MultiAssets, MultiLocation,
|
||||||
NetworkId::{self, *},
|
NetworkId::{self, *},
|
||||||
OriginKind, Outcome, Parent, ParentThen, Response, Result as XcmResult, SendError,
|
OriginKind, Outcome, Parent, ParentThen, QueryId, Response, Result as XcmResult,
|
||||||
SendResult, SendXcm,
|
SendError, SendResult, SendXcm,
|
||||||
WeightLimit::{self, *},
|
WeightLimit::{self, *},
|
||||||
WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
|
WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
|
||||||
WildMultiAsset::{self, *},
|
WildMultiAsset::{self, *},
|
||||||
|
VERSION as XCM_VERSION,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub use super::{Instruction, Xcm};
|
pub use super::{Instruction, Xcm};
|
||||||
@@ -142,6 +149,8 @@ pub enum Response {
|
|||||||
Assets(MultiAssets),
|
Assets(MultiAssets),
|
||||||
/// The outcome of an XCM instruction.
|
/// The outcome of an XCM instruction.
|
||||||
ExecutionResult(result::Result<(), (u32, Error)>),
|
ExecutionResult(result::Result<(), (u32, Error)>),
|
||||||
|
/// An XCM version.
|
||||||
|
Version(super::Version),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Response {
|
impl Default for Response {
|
||||||
@@ -239,7 +248,7 @@ pub enum Instruction<Call> {
|
|||||||
/// Errors:
|
/// Errors:
|
||||||
QueryResponse {
|
QueryResponse {
|
||||||
#[codec(compact)]
|
#[codec(compact)]
|
||||||
query_id: u64,
|
query_id: QueryId,
|
||||||
response: Response,
|
response: Response,
|
||||||
#[codec(compact)]
|
#[codec(compact)]
|
||||||
max_weight: u64,
|
max_weight: u64,
|
||||||
@@ -291,7 +300,12 @@ pub enum Instruction<Call> {
|
|||||||
/// Kind: *Instruction*.
|
/// Kind: *Instruction*.
|
||||||
///
|
///
|
||||||
/// Errors:
|
/// Errors:
|
||||||
Transact { origin_type: OriginKind, require_weight_at_most: u64, call: DoubleEncoded<Call> },
|
Transact {
|
||||||
|
origin_type: OriginKind,
|
||||||
|
#[codec(compact)]
|
||||||
|
require_weight_at_most: u64,
|
||||||
|
call: DoubleEncoded<Call>,
|
||||||
|
},
|
||||||
|
|
||||||
/// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the
|
/// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the
|
||||||
/// relay-chain to a para.
|
/// relay-chain to a para.
|
||||||
@@ -377,7 +391,7 @@ pub enum Instruction<Call> {
|
|||||||
/// Errors:
|
/// Errors:
|
||||||
ReportError {
|
ReportError {
|
||||||
#[codec(compact)]
|
#[codec(compact)]
|
||||||
query_id: u64,
|
query_id: QueryId,
|
||||||
dest: MultiLocation,
|
dest: MultiLocation,
|
||||||
#[codec(compact)]
|
#[codec(compact)]
|
||||||
max_response_weight: u64,
|
max_response_weight: u64,
|
||||||
@@ -395,7 +409,12 @@ pub enum Instruction<Call> {
|
|||||||
/// Kind: *Instruction*
|
/// Kind: *Instruction*
|
||||||
///
|
///
|
||||||
/// Errors:
|
/// Errors:
|
||||||
DepositAsset { assets: MultiAssetFilter, max_assets: u32, beneficiary: MultiLocation },
|
DepositAsset {
|
||||||
|
assets: MultiAssetFilter,
|
||||||
|
#[codec(compact)]
|
||||||
|
max_assets: u32,
|
||||||
|
beneficiary: MultiLocation,
|
||||||
|
},
|
||||||
|
|
||||||
/// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets under
|
/// Remove the asset(s) (`assets`) from the Holding Register and place equivalent assets under
|
||||||
/// the ownership of `dest` within this consensus system (i.e. deposit them into its sovereign
|
/// the ownership of `dest` within this consensus system (i.e. deposit them into its sovereign
|
||||||
@@ -418,6 +437,7 @@ pub enum Instruction<Call> {
|
|||||||
/// Errors:
|
/// Errors:
|
||||||
DepositReserveAsset {
|
DepositReserveAsset {
|
||||||
assets: MultiAssetFilter,
|
assets: MultiAssetFilter,
|
||||||
|
#[codec(compact)]
|
||||||
max_assets: u32,
|
max_assets: u32,
|
||||||
dest: MultiLocation,
|
dest: MultiLocation,
|
||||||
xcm: Xcm<()>,
|
xcm: Xcm<()>,
|
||||||
@@ -487,7 +507,7 @@ pub enum Instruction<Call> {
|
|||||||
/// Errors:
|
/// Errors:
|
||||||
QueryHolding {
|
QueryHolding {
|
||||||
#[codec(compact)]
|
#[codec(compact)]
|
||||||
query_id: u64,
|
query_id: QueryId,
|
||||||
dest: MultiLocation,
|
dest: MultiLocation,
|
||||||
assets: MultiAssetFilter,
|
assets: MultiAssetFilter,
|
||||||
#[codec(compact)]
|
#[codec(compact)]
|
||||||
@@ -571,7 +591,24 @@ pub enum Instruction<Call> {
|
|||||||
///
|
///
|
||||||
/// Errors:
|
/// Errors:
|
||||||
/// - `Trap`: All circumstances, whose inner value is the same as this item's inner value.
|
/// - `Trap`: All circumstances, whose inner value is the same as this item's inner value.
|
||||||
Trap(u64),
|
Trap(#[codec(compact)] u64),
|
||||||
|
|
||||||
|
/// Ask the destination system to respond with the most recent version of XCM that they
|
||||||
|
/// support in a `QueryResponse` instruction. Any changes to this should also elicit similar
|
||||||
|
/// responses when they happen.
|
||||||
|
///
|
||||||
|
/// Kind: *Instruction*
|
||||||
|
SubscribeVersion {
|
||||||
|
#[codec(compact)]
|
||||||
|
query_id: QueryId,
|
||||||
|
#[codec(compact)]
|
||||||
|
max_response_weight: u64,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Cancel the effect of a previous `SubscribeVersion` instruction.
|
||||||
|
///
|
||||||
|
/// Kind: *Instruction*
|
||||||
|
UnsubscribeVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Call> Xcm<Call> {
|
impl<Call> Xcm<Call> {
|
||||||
@@ -626,6 +663,9 @@ impl<Call> Instruction<Call> {
|
|||||||
ClearError => ClearError,
|
ClearError => ClearError,
|
||||||
ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket },
|
ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket },
|
||||||
Trap(code) => Trap(code),
|
Trap(code) => Trap(code),
|
||||||
|
SubscribeVersion { query_id, max_response_weight } =>
|
||||||
|
SubscribeVersion { query_id, max_response_weight },
|
||||||
|
UnsubscribeVersion => UnsubscribeVersion,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -646,6 +686,7 @@ impl TryFrom<OldResponse> for Response {
|
|||||||
fn try_from(old_response: OldResponse) -> result::Result<Self, ()> {
|
fn try_from(old_response: OldResponse) -> result::Result<Self, ()> {
|
||||||
match old_response {
|
match old_response {
|
||||||
OldResponse::Assets(assets) => Ok(Self::Assets(assets)),
|
OldResponse::Assets(assets) => Ok(Self::Assets(assets)),
|
||||||
|
OldResponse::Version(version) => Ok(Self::Version(version)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -695,6 +736,9 @@ impl<Call> TryFrom<OldXcm<Call>> for Xcm<Call> {
|
|||||||
vec![Transact { origin_type, require_weight_at_most, call }],
|
vec![Transact { origin_type, require_weight_at_most, call }],
|
||||||
// We don't handle this one at all due to nested XCM.
|
// We don't handle this one at all due to nested XCM.
|
||||||
OldXcm::RelayedFrom { .. } => return Err(()),
|
OldXcm::RelayedFrom { .. } => return Err(()),
|
||||||
|
OldXcm::SubscribeVersion { query_id, max_response_weight } =>
|
||||||
|
vec![SubscribeVersion { query_id, max_response_weight }],
|
||||||
|
OldXcm::UnsubscribeVersion => vec![UnsubscribeVersion],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ pub enum Error {
|
|||||||
Trap(u64),
|
Trap(u64),
|
||||||
/// The given claim could not be recognized/found.
|
/// The given claim could not be recognized/found.
|
||||||
UnknownClaim,
|
UnknownClaim,
|
||||||
|
/// The location given was invalid for some reason specific to the operation at hand.
|
||||||
|
InvalidLocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<()> for Error {
|
impl From<()> for Error {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ pub struct TakeWeightCredit;
|
|||||||
impl ShouldExecute for TakeWeightCredit {
|
impl ShouldExecute for TakeWeightCredit {
|
||||||
fn should_execute<Call>(
|
fn should_execute<Call>(
|
||||||
_origin: &MultiLocation,
|
_origin: &MultiLocation,
|
||||||
_top_level: bool,
|
|
||||||
_message: &mut Xcm<Call>,
|
_message: &mut Xcm<Call>,
|
||||||
max_weight: Weight,
|
max_weight: Weight,
|
||||||
weight_credit: &mut Weight,
|
weight_credit: &mut Weight,
|
||||||
@@ -44,19 +43,17 @@ impl ShouldExecute for TakeWeightCredit {
|
|||||||
/// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking
|
/// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking
|
||||||
/// payments into account.
|
/// payments into account.
|
||||||
///
|
///
|
||||||
/// Only allows for `TeleportAsset`, `WithdrawAsset` and `ReserveAssetDeposit` XCMs because they are
|
/// Only allows for `TeleportAsset`, `WithdrawAsset`, `ClaimAsset` and `ReserveAssetDeposit` XCMs
|
||||||
/// the only ones that place assets in the Holding Register to pay for execution.
|
/// because they are the only ones that place assets in the Holding Register to pay for execution.
|
||||||
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
|
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
|
||||||
impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
|
impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
|
||||||
fn should_execute<Call>(
|
fn should_execute<Call>(
|
||||||
origin: &MultiLocation,
|
origin: &MultiLocation,
|
||||||
top_level: bool,
|
|
||||||
message: &mut Xcm<Call>,
|
message: &mut Xcm<Call>,
|
||||||
max_weight: Weight,
|
max_weight: Weight,
|
||||||
_weight_credit: &mut Weight,
|
_weight_credit: &mut Weight,
|
||||||
) -> Result<(), ()> {
|
) -> Result<(), ()> {
|
||||||
ensure!(T::contains(origin), ());
|
ensure!(T::contains(origin), ());
|
||||||
ensure!(top_level, ());
|
|
||||||
let mut iter = message.0.iter_mut();
|
let mut iter = message.0.iter_mut();
|
||||||
let i = iter.next().ok_or(())?;
|
let i = iter.next().ok_or(())?;
|
||||||
match i {
|
match i {
|
||||||
@@ -90,7 +87,6 @@ pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>);
|
|||||||
impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
|
impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
|
||||||
fn should_execute<Call>(
|
fn should_execute<Call>(
|
||||||
origin: &MultiLocation,
|
origin: &MultiLocation,
|
||||||
_top_level: bool,
|
|
||||||
_message: &mut Xcm<Call>,
|
_message: &mut Xcm<Call>,
|
||||||
_max_weight: Weight,
|
_max_weight: Weight,
|
||||||
_weight_credit: &mut Weight,
|
_weight_credit: &mut Weight,
|
||||||
@@ -117,7 +113,6 @@ pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler
|
|||||||
impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
|
impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
|
||||||
fn should_execute<Call>(
|
fn should_execute<Call>(
|
||||||
origin: &MultiLocation,
|
origin: &MultiLocation,
|
||||||
_top_level: bool,
|
|
||||||
message: &mut Xcm<Call>,
|
message: &mut Xcm<Call>,
|
||||||
_max_weight: Weight,
|
_max_weight: Weight,
|
||||||
_weight_credit: &mut Weight,
|
_weight_credit: &mut Weight,
|
||||||
@@ -130,3 +125,21 @@ impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<Res
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allows execution from `origin` if it is just a straight `SubscribeVerison` or
|
||||||
|
/// `UnsubscribeVersion` instruction.
|
||||||
|
pub struct AllowSubscriptionsFrom<T>(PhantomData<T>);
|
||||||
|
impl<T: Contains<MultiLocation>> ShouldExecute for AllowSubscriptionsFrom<T> {
|
||||||
|
fn should_execute<Call>(
|
||||||
|
origin: &MultiLocation,
|
||||||
|
message: &mut Xcm<Call>,
|
||||||
|
_max_weight: Weight,
|
||||||
|
_weight_credit: &mut Weight,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
ensure!(T::contains(origin), ());
|
||||||
|
match (message.0.len(), message.0.first()) {
|
||||||
|
(1, Some(SubscribeVersion { .. })) | (1, Some(UnsubscribeVersion)) => Ok(()),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ pub use origin_conversion::{
|
|||||||
|
|
||||||
mod barriers;
|
mod barriers;
|
||||||
pub use barriers::{
|
pub use barriers::{
|
||||||
AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
|
AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom,
|
||||||
IsChildSystemParachain, TakeWeightCredit,
|
AllowUnpaidExecutionFrom, IsChildSystemParachain, TakeWeightCredit,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod currency_adapter;
|
mod currency_adapter;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::barriers::AllowSubscriptionsFrom;
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
|
AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
|
||||||
FixedRateOfFungible, FixedWeightBounds, LocationInverter, TakeWeightCredit,
|
FixedRateOfFungible, FixedWeightBounds, LocationInverter, TakeWeightCredit,
|
||||||
@@ -35,7 +36,7 @@ pub use sp_std::{
|
|||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
pub use xcm::latest::prelude::*;
|
pub use xcm::latest::prelude::*;
|
||||||
use xcm_executor::traits::{ClaimAssets, DropAssets};
|
use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier};
|
||||||
pub use xcm_executor::{
|
pub use xcm_executor::{
|
||||||
traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset},
|
traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset},
|
||||||
Assets, Config,
|
Assets, Config,
|
||||||
@@ -258,6 +259,7 @@ parameter_types! {
|
|||||||
// Nothing is allowed to be paid/unpaid by default.
|
// Nothing is allowed to be paid/unpaid by default.
|
||||||
pub static AllowUnpaidFrom: Vec<MultiLocation> = vec![];
|
pub static AllowUnpaidFrom: Vec<MultiLocation> = vec![];
|
||||||
pub static AllowPaidFrom: Vec<MultiLocation> = vec![];
|
pub static AllowPaidFrom: Vec<MultiLocation> = vec![];
|
||||||
|
pub static AllowSubsFrom: Vec<MultiLocation> = vec![];
|
||||||
// 1_000_000_000_000 => 1 unit of asset for 1 unit of Weight.
|
// 1_000_000_000_000 => 1 unit of asset for 1 unit of Weight.
|
||||||
pub static WeightPrice: (AssetId, u128) = (From::from(Here), 1_000_000_000_000);
|
pub static WeightPrice: (AssetId, u128) = (From::from(Here), 1_000_000_000_000);
|
||||||
pub static MaxInstructions: u32 = 100;
|
pub static MaxInstructions: u32 = 100;
|
||||||
@@ -268,6 +270,7 @@ pub type TestBarrier = (
|
|||||||
AllowKnownQueryResponses<TestResponseHandler>,
|
AllowKnownQueryResponses<TestResponseHandler>,
|
||||||
AllowTopLevelPaidExecutionFrom<IsInVec<AllowPaidFrom>>,
|
AllowTopLevelPaidExecutionFrom<IsInVec<AllowPaidFrom>>,
|
||||||
AllowUnpaidExecutionFrom<IsInVec<AllowUnpaidFrom>>,
|
AllowUnpaidExecutionFrom<IsInVec<AllowUnpaidFrom>>,
|
||||||
|
AllowSubscriptionsFrom<IsInVec<AllowSubsFrom>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
@@ -301,6 +304,26 @@ impl ClaimAssets for TestAssetTrap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub static SubscriptionRequests: Vec<(MultiLocation, Option<(QueryId, u64)>)> = vec![];
|
||||||
|
}
|
||||||
|
pub struct TestSubscriptionService;
|
||||||
|
|
||||||
|
impl VersionChangeNotifier for TestSubscriptionService {
|
||||||
|
fn start(location: &MultiLocation, query_id: QueryId, max_weight: u64) -> XcmResult {
|
||||||
|
let mut r = SubscriptionRequests::get();
|
||||||
|
r.push((location.clone(), Some((query_id, max_weight))));
|
||||||
|
SubscriptionRequests::set(r);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn stop(location: &MultiLocation) -> XcmResult {
|
||||||
|
let mut r = SubscriptionRequests::get();
|
||||||
|
r.push((location.clone(), None));
|
||||||
|
SubscriptionRequests::set(r);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TestConfig;
|
pub struct TestConfig;
|
||||||
impl Config for TestConfig {
|
impl Config for TestConfig {
|
||||||
type Call = TestCall;
|
type Call = TestCall;
|
||||||
@@ -316,4 +339,5 @@ impl Config for TestConfig {
|
|||||||
type ResponseHandler = TestResponseHandler;
|
type ResponseHandler = TestResponseHandler;
|
||||||
type AssetTrap = TestAssetTrap;
|
type AssetTrap = TestAssetTrap;
|
||||||
type AssetClaims = TestAssetTrap;
|
type AssetClaims = TestAssetTrap;
|
||||||
|
type SubscriptionService = TestSubscriptionService;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
// Copyright 2020 Parity Technologies query_id: (), max_response_weight: () query_id: (), max_response_weight: () (UK) Ltd.
|
||||||
// This file is part of Polkadot.
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
// Polkadot is free software: you can redistribute it and/or modify
|
// Polkadot is free software: you can redistribute it and/or modify
|
||||||
@@ -57,23 +57,11 @@ fn take_weight_credit_barrier_should_work() {
|
|||||||
let mut message =
|
let mut message =
|
||||||
Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]);
|
Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]);
|
||||||
let mut weight_credit = 10;
|
let mut weight_credit = 10;
|
||||||
let r = TakeWeightCredit::should_execute(
|
let r = TakeWeightCredit::should_execute(&Parent.into(), &mut message, 10, &mut weight_credit);
|
||||||
&Parent.into(),
|
|
||||||
true,
|
|
||||||
&mut message,
|
|
||||||
10,
|
|
||||||
&mut weight_credit,
|
|
||||||
);
|
|
||||||
assert_eq!(r, Ok(()));
|
assert_eq!(r, Ok(()));
|
||||||
assert_eq!(weight_credit, 0);
|
assert_eq!(weight_credit, 0);
|
||||||
|
|
||||||
let r = TakeWeightCredit::should_execute(
|
let r = TakeWeightCredit::should_execute(&Parent.into(), &mut message, 10, &mut weight_credit);
|
||||||
&Parent.into(),
|
|
||||||
true,
|
|
||||||
&mut message,
|
|
||||||
10,
|
|
||||||
&mut weight_credit,
|
|
||||||
);
|
|
||||||
assert_eq!(r, Err(()));
|
assert_eq!(r, Err(()));
|
||||||
assert_eq!(weight_credit, 0);
|
assert_eq!(weight_credit, 0);
|
||||||
}
|
}
|
||||||
@@ -87,7 +75,6 @@ fn allow_unpaid_should_work() {
|
|||||||
|
|
||||||
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
||||||
&Parachain(1).into(),
|
&Parachain(1).into(),
|
||||||
true,
|
|
||||||
&mut message,
|
&mut message,
|
||||||
10,
|
10,
|
||||||
&mut 0,
|
&mut 0,
|
||||||
@@ -96,7 +83,6 @@ fn allow_unpaid_should_work() {
|
|||||||
|
|
||||||
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
||||||
&Parent.into(),
|
&Parent.into(),
|
||||||
true,
|
|
||||||
&mut message,
|
&mut message,
|
||||||
10,
|
10,
|
||||||
&mut 0,
|
&mut 0,
|
||||||
@@ -113,7 +99,6 @@ fn allow_paid_should_work() {
|
|||||||
|
|
||||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||||
&Parachain(1).into(),
|
&Parachain(1).into(),
|
||||||
true,
|
|
||||||
&mut message,
|
&mut message,
|
||||||
10,
|
10,
|
||||||
&mut 0,
|
&mut 0,
|
||||||
@@ -129,7 +114,6 @@ fn allow_paid_should_work() {
|
|||||||
|
|
||||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||||
&Parent.into(),
|
&Parent.into(),
|
||||||
true,
|
|
||||||
&mut underpaying_message,
|
&mut underpaying_message,
|
||||||
30,
|
30,
|
||||||
&mut 0,
|
&mut 0,
|
||||||
@@ -145,7 +129,6 @@ fn allow_paid_should_work() {
|
|||||||
|
|
||||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||||
&Parachain(1).into(),
|
&Parachain(1).into(),
|
||||||
true,
|
|
||||||
&mut paying_message,
|
&mut paying_message,
|
||||||
30,
|
30,
|
||||||
&mut 0,
|
&mut 0,
|
||||||
@@ -154,7 +137,6 @@ fn allow_paid_should_work() {
|
|||||||
|
|
||||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||||
&Parent.into(),
|
&Parent.into(),
|
||||||
true,
|
|
||||||
&mut paying_message,
|
&mut paying_message,
|
||||||
30,
|
30,
|
||||||
&mut 0,
|
&mut 0,
|
||||||
@@ -480,6 +462,120 @@ fn reserve_transfer_should_work() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_version_subscriptions_should_work() {
|
||||||
|
AllowSubsFrom::set(vec![Parent.into()]);
|
||||||
|
|
||||||
|
let origin = Parachain(1000).into();
|
||||||
|
let message = Xcm::<TestCall>(vec![
|
||||||
|
SetAppendix(Xcm(vec![])),
|
||||||
|
SubscribeVersion { query_id: 42, max_response_weight: 5000 },
|
||||||
|
]);
|
||||||
|
let weight_limit = 20;
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||||
|
assert_eq!(r, Outcome::Error(XcmError::Barrier));
|
||||||
|
|
||||||
|
let origin = Parachain(1000).into();
|
||||||
|
let message =
|
||||||
|
Xcm::<TestCall>(vec![SubscribeVersion { query_id: 42, max_response_weight: 5000 }]);
|
||||||
|
let weight_limit = 10;
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message.clone(), weight_limit);
|
||||||
|
assert_eq!(r, Outcome::Error(XcmError::Barrier));
|
||||||
|
|
||||||
|
let origin = Parent.into();
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||||
|
assert_eq!(r, Outcome::Complete(10));
|
||||||
|
|
||||||
|
assert_eq!(SubscriptionRequests::get(), vec![(Parent.into(), Some((42, 5000)))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_subscription_instruction_should_work() {
|
||||||
|
let origin = Parachain(1000).into();
|
||||||
|
let message = Xcm::<TestCall>(vec![
|
||||||
|
DescendOrigin(X1(AccountIndex64 { index: 1, network: Any })),
|
||||||
|
SubscribeVersion { query_id: 42, max_response_weight: 5000 },
|
||||||
|
]);
|
||||||
|
let weight_limit = 20;
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm_in_credit(
|
||||||
|
origin.clone(),
|
||||||
|
message.clone(),
|
||||||
|
weight_limit,
|
||||||
|
weight_limit,
|
||||||
|
);
|
||||||
|
assert_eq!(r, Outcome::Incomplete(20, XcmError::BadOrigin));
|
||||||
|
|
||||||
|
let message = Xcm::<TestCall>(vec![
|
||||||
|
SetAppendix(Xcm(vec![])),
|
||||||
|
SubscribeVersion { query_id: 42, max_response_weight: 5000 },
|
||||||
|
]);
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm_in_credit(
|
||||||
|
origin,
|
||||||
|
message.clone(),
|
||||||
|
weight_limit,
|
||||||
|
weight_limit,
|
||||||
|
);
|
||||||
|
assert_eq!(r, Outcome::Complete(20));
|
||||||
|
|
||||||
|
assert_eq!(SubscriptionRequests::get(), vec![(Parachain(1000).into(), Some((42, 5000)))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_version_unsubscriptions_should_work() {
|
||||||
|
AllowSubsFrom::set(vec![Parent.into()]);
|
||||||
|
|
||||||
|
let origin = Parachain(1000).into();
|
||||||
|
let message = Xcm::<TestCall>(vec![SetAppendix(Xcm(vec![])), UnsubscribeVersion]);
|
||||||
|
let weight_limit = 20;
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||||
|
assert_eq!(r, Outcome::Error(XcmError::Barrier));
|
||||||
|
|
||||||
|
let origin = Parachain(1000).into();
|
||||||
|
let message = Xcm::<TestCall>(vec![UnsubscribeVersion]);
|
||||||
|
let weight_limit = 10;
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message.clone(), weight_limit);
|
||||||
|
assert_eq!(r, Outcome::Error(XcmError::Barrier));
|
||||||
|
|
||||||
|
let origin = Parent.into();
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||||
|
assert_eq!(r, Outcome::Complete(10));
|
||||||
|
|
||||||
|
assert_eq!(SubscriptionRequests::get(), vec![(Parent.into(), None)]);
|
||||||
|
assert_eq!(sent_xcm(), vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_unsubscription_instruction_should_work() {
|
||||||
|
let origin = Parachain(1000).into();
|
||||||
|
|
||||||
|
// Not allowed to do it when origin has been changed.
|
||||||
|
let message = Xcm::<TestCall>(vec![
|
||||||
|
DescendOrigin(X1(AccountIndex64 { index: 1, network: Any })),
|
||||||
|
UnsubscribeVersion,
|
||||||
|
]);
|
||||||
|
let weight_limit = 20;
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm_in_credit(
|
||||||
|
origin.clone(),
|
||||||
|
message.clone(),
|
||||||
|
weight_limit,
|
||||||
|
weight_limit,
|
||||||
|
);
|
||||||
|
assert_eq!(r, Outcome::Incomplete(20, XcmError::BadOrigin));
|
||||||
|
|
||||||
|
// Fine to do it when origin is untouched.
|
||||||
|
let message = Xcm::<TestCall>(vec![SetAppendix(Xcm(vec![])), UnsubscribeVersion]);
|
||||||
|
let r = XcmExecutor::<TestConfig>::execute_xcm_in_credit(
|
||||||
|
origin,
|
||||||
|
message.clone(),
|
||||||
|
weight_limit,
|
||||||
|
weight_limit,
|
||||||
|
);
|
||||||
|
assert_eq!(r, Outcome::Complete(20));
|
||||||
|
|
||||||
|
assert_eq!(SubscriptionRequests::get(), vec![(Parachain(1000).into(), None)]);
|
||||||
|
assert_eq!(sent_xcm(), vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transacting_should_work() {
|
fn transacting_should_work() {
|
||||||
AllowUnpaidFrom::set(vec![Parent.into()]);
|
AllowUnpaidFrom::set(vec![Parent.into()]);
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ impl xcm_executor::Config for XcmConfig {
|
|||||||
type ResponseHandler = XcmPallet;
|
type ResponseHandler = XcmPallet;
|
||||||
type AssetTrap = XcmPallet;
|
type AssetTrap = XcmPallet;
|
||||||
type AssetClaims = XcmPallet;
|
type AssetClaims = XcmPallet;
|
||||||
|
type SubscriptionService = XcmPallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, KusamaNetwork>;
|
pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, KusamaNetwork>;
|
||||||
@@ -187,6 +188,8 @@ impl pallet_xcm::Config for Runtime {
|
|||||||
type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>;
|
type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
|
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||||
|
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl origin::Config for Runtime {}
|
impl origin::Config for Runtime {}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use crate::traits::{
|
use crate::traits::{
|
||||||
ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, InvertLocation, OnResponse,
|
ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, InvertLocation, OnResponse,
|
||||||
ShouldExecute, TransactAsset, WeightBounds, WeightTrader,
|
ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader,
|
||||||
};
|
};
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
dispatch::{Dispatchable, Parameter},
|
dispatch::{Dispatchable, Parameter},
|
||||||
@@ -65,4 +65,7 @@ pub trait Config {
|
|||||||
|
|
||||||
/// The handler for when there is an instruction to claim assets.
|
/// The handler for when there is an instruction to claim assets.
|
||||||
type AssetClaims: ClaimAssets;
|
type AssetClaims: ClaimAssets;
|
||||||
|
|
||||||
|
/// How we handle version subscription requests.
|
||||||
|
type SubscriptionService: VersionChangeNotifier;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ use xcm::latest::{
|
|||||||
pub mod traits;
|
pub mod traits;
|
||||||
use traits::{
|
use traits::{
|
||||||
ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, InvertLocation, OnResponse,
|
ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, InvertLocation, OnResponse,
|
||||||
ShouldExecute, TransactAsset, WeightBounds, WeightTrader,
|
ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
@@ -44,8 +44,9 @@ pub use config::Config;
|
|||||||
pub struct XcmExecutor<Config: config::Config> {
|
pub struct XcmExecutor<Config: config::Config> {
|
||||||
holding: Assets,
|
holding: Assets,
|
||||||
origin: Option<MultiLocation>,
|
origin: Option<MultiLocation>,
|
||||||
|
original_origin: MultiLocation,
|
||||||
trader: Config::Trader,
|
trader: Config::Trader,
|
||||||
/// The most recent error result and instruction index into the fragment in which it occured,
|
/// The most recent error result and instruction index into the fragment in which it occurred,
|
||||||
/// if any.
|
/// if any.
|
||||||
error: Option<(u32, XcmError)>,
|
error: Option<(u32, XcmError)>,
|
||||||
/// The surplus weight, defined as the amount by which `max_weight` is
|
/// The surplus weight, defined as the amount by which `max_weight` is
|
||||||
@@ -87,17 +88,13 @@ impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> {
|
|||||||
return Outcome::Error(XcmError::WeightLimitReached(xcm_weight))
|
return Outcome::Error(XcmError::WeightLimitReached(xcm_weight))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(_) = Config::Barrier::should_execute(
|
if let Err(_) =
|
||||||
&origin,
|
Config::Barrier::should_execute(&origin, &mut message, xcm_weight, &mut weight_credit)
|
||||||
true,
|
{
|
||||||
&mut message,
|
|
||||||
xcm_weight,
|
|
||||||
&mut weight_credit,
|
|
||||||
) {
|
|
||||||
return Outcome::Error(XcmError::Barrier)
|
return Outcome::Error(XcmError::Barrier)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut vm = Self::new(origin.clone());
|
let mut vm = Self::new(origin);
|
||||||
|
|
||||||
while !message.0.is_empty() {
|
while !message.0.is_empty() {
|
||||||
let result = vm.execute(message);
|
let result = vm.execute(message);
|
||||||
@@ -118,7 +115,8 @@ impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> {
|
|||||||
let mut weight_used = xcm_weight.saturating_sub(vm.total_surplus);
|
let mut weight_used = xcm_weight.saturating_sub(vm.total_surplus);
|
||||||
|
|
||||||
if !vm.holding.is_empty() {
|
if !vm.holding.is_empty() {
|
||||||
weight_used.saturating_accrue(Config::AssetTrap::drop_assets(&origin, vm.holding));
|
let trap_weight = Config::AssetTrap::drop_assets(&vm.original_origin, vm.holding);
|
||||||
|
weight_used.saturating_accrue(trap_weight);
|
||||||
};
|
};
|
||||||
|
|
||||||
match vm.error {
|
match vm.error {
|
||||||
@@ -134,7 +132,8 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
fn new(origin: MultiLocation) -> Self {
|
fn new(origin: MultiLocation) -> Self {
|
||||||
Self {
|
Self {
|
||||||
holding: Assets::new(),
|
holding: Assets::new(),
|
||||||
origin: Some(origin),
|
origin: Some(origin.clone()),
|
||||||
|
original_origin: origin,
|
||||||
trader: Config::Trader::new(),
|
trader: Config::Trader::new(),
|
||||||
error: None,
|
error: None,
|
||||||
total_surplus: 0,
|
total_surplus: 0,
|
||||||
@@ -419,6 +418,18 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
Trap(code) => Err(XcmError::Trap(code)),
|
Trap(code) => Err(XcmError::Trap(code)),
|
||||||
|
SubscribeVersion { query_id, max_response_weight } => {
|
||||||
|
let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone();
|
||||||
|
// We don't allow derivative origins to subscribe since it would otherwise pose a
|
||||||
|
// DoS risk.
|
||||||
|
ensure!(self.original_origin == origin, XcmError::BadOrigin);
|
||||||
|
Config::SubscriptionService::start(&origin, query_id, max_response_weight)
|
||||||
|
},
|
||||||
|
UnsubscribeVersion => {
|
||||||
|
let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;
|
||||||
|
ensure!(&self.original_origin == origin, XcmError::BadOrigin);
|
||||||
|
Config::SubscriptionService::stop(origin)
|
||||||
|
},
|
||||||
ExchangeAsset { .. } => Err(XcmError::Unimplemented),
|
ExchangeAsset { .. } => Err(XcmError::Unimplemented),
|
||||||
HrmpNewChannelOpenRequest { .. } => Err(XcmError::Unimplemented),
|
HrmpNewChannelOpenRequest { .. } => Err(XcmError::Unimplemented),
|
||||||
HrmpChannelAccepted { .. } => Err(XcmError::Unimplemented),
|
HrmpChannelAccepted { .. } => Err(XcmError::Unimplemented),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pub use matches_fungible::MatchesFungible;
|
|||||||
mod matches_fungibles;
|
mod matches_fungibles;
|
||||||
pub use matches_fungibles::{Error, MatchesFungibles};
|
pub use matches_fungibles::{Error, MatchesFungibles};
|
||||||
mod on_response;
|
mod on_response;
|
||||||
pub use on_response::OnResponse;
|
pub use on_response::{OnResponse, VersionChangeNotifier};
|
||||||
mod should_execute;
|
mod should_execute;
|
||||||
pub use should_execute::ShouldExecute;
|
pub use should_execute::ShouldExecute;
|
||||||
mod transact_asset;
|
mod transact_asset;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use frame_support::weights::Weight;
|
use frame_support::weights::Weight;
|
||||||
use xcm::latest::{MultiLocation, Response};
|
use xcm::latest::{Error as XcmError, MultiLocation, QueryId, Response, Result as XcmResult};
|
||||||
|
|
||||||
/// Define what needs to be done upon receiving a query response.
|
/// Define what needs to be done upon receiving a query response.
|
||||||
pub trait OnResponse {
|
pub trait OnResponse {
|
||||||
@@ -42,3 +42,29 @@ impl OnResponse for () {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait for a type which handles notifying a destination of XCM version changes.
|
||||||
|
pub trait VersionChangeNotifier {
|
||||||
|
/// Start notifying `location` should the XCM version of this chain change.
|
||||||
|
///
|
||||||
|
/// When it does, this type should ensure a `QueryResponse` message is sent with the given
|
||||||
|
/// `query_id` & `max_weight` and with a `response` of `Repsonse::Version`. This should happen
|
||||||
|
/// until/unless `stop` is called with the correct `query_id`.
|
||||||
|
///
|
||||||
|
/// If the `location` has an ongoing notification and when this function is called, then an
|
||||||
|
/// error should be returned.
|
||||||
|
fn start(location: &MultiLocation, query_id: QueryId, max_weight: u64) -> XcmResult;
|
||||||
|
|
||||||
|
/// Stop notifying `location` should the XCM change. Returns an error if there is no existing
|
||||||
|
/// notification set up.
|
||||||
|
fn stop(location: &MultiLocation) -> XcmResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VersionChangeNotifier for () {
|
||||||
|
fn start(_: &MultiLocation, _: QueryId, _: u64) -> XcmResult {
|
||||||
|
Err(XcmError::Unimplemented)
|
||||||
|
}
|
||||||
|
fn stop(_: &MultiLocation) -> XcmResult {
|
||||||
|
Err(XcmError::Unimplemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ pub trait ShouldExecute {
|
|||||||
/// Returns `true` if the given `message` may be executed.
|
/// Returns `true` if the given `message` may be executed.
|
||||||
///
|
///
|
||||||
/// - `origin`: The origin (sender) of the message.
|
/// - `origin`: The origin (sender) of the message.
|
||||||
/// - `top_level`: `true` indicates the initial XCM coming from the `origin`, `false` indicates
|
|
||||||
/// an embedded XCM executed internally as part of another message or an `Order`.
|
|
||||||
/// - `message`: The message itself.
|
/// - `message`: The message itself.
|
||||||
/// - `max_weight`: The (possibly over-) estimation of the weight of execution of the message.
|
/// - `max_weight`: The (possibly over-) estimation of the weight of execution of the message.
|
||||||
/// - `weight_credit`: The pre-established amount of weight that the system has determined this
|
/// - `weight_credit`: The pre-established amount of weight that the system has determined this
|
||||||
@@ -35,7 +33,6 @@ pub trait ShouldExecute {
|
|||||||
/// payment, but could in principle be due to other factors.
|
/// payment, but could in principle be due to other factors.
|
||||||
fn should_execute<Call>(
|
fn should_execute<Call>(
|
||||||
origin: &MultiLocation,
|
origin: &MultiLocation,
|
||||||
top_level: bool,
|
|
||||||
message: &mut Xcm<Call>,
|
message: &mut Xcm<Call>,
|
||||||
max_weight: Weight,
|
max_weight: Weight,
|
||||||
weight_credit: &mut Weight,
|
weight_credit: &mut Weight,
|
||||||
@@ -46,22 +43,20 @@ pub trait ShouldExecute {
|
|||||||
impl ShouldExecute for Tuple {
|
impl ShouldExecute for Tuple {
|
||||||
fn should_execute<Call>(
|
fn should_execute<Call>(
|
||||||
origin: &MultiLocation,
|
origin: &MultiLocation,
|
||||||
top_level: bool,
|
|
||||||
message: &mut Xcm<Call>,
|
message: &mut Xcm<Call>,
|
||||||
max_weight: Weight,
|
max_weight: Weight,
|
||||||
weight_credit: &mut Weight,
|
weight_credit: &mut Weight,
|
||||||
) -> Result<(), ()> {
|
) -> Result<(), ()> {
|
||||||
for_tuples!( #(
|
for_tuples!( #(
|
||||||
match Tuple::should_execute(origin, top_level, message, max_weight, weight_credit) {
|
match Tuple::should_execute(origin, message, max_weight, weight_credit) {
|
||||||
Ok(()) => return Ok(()),
|
Ok(()) => return Ok(()),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
)* );
|
)* );
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "xcm::should_execute",
|
target: "xcm::should_execute",
|
||||||
"did not pass barrier: origin: {:?}, top_level: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
"did not pass barrier: origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
||||||
origin,
|
origin,
|
||||||
top_level,
|
|
||||||
message,
|
message,
|
||||||
max_weight,
|
max_weight,
|
||||||
weight_credit,
|
weight_credit,
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ impl Config for XcmConfig {
|
|||||||
type ResponseHandler = ();
|
type ResponseHandler = ();
|
||||||
type AssetTrap = ();
|
type AssetTrap = ();
|
||||||
type AssetClaims = ();
|
type AssetClaims = ();
|
||||||
|
type SubscriptionService = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[frame_support::pallet]
|
#[frame_support::pallet]
|
||||||
@@ -305,6 +306,8 @@ impl pallet_xcm::Config for Runtime {
|
|||||||
type LocationInverter = LocationInverter<Ancestry>;
|
type LocationInverter = LocationInverter<Ancestry>;
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
|
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||||
|
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
|
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ impl Config for XcmConfig {
|
|||||||
type ResponseHandler = ();
|
type ResponseHandler = ();
|
||||||
type AssetTrap = ();
|
type AssetTrap = ();
|
||||||
type AssetClaims = ();
|
type AssetClaims = ();
|
||||||
|
type SubscriptionService = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, KusamaNetwork>;
|
pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, KusamaNetwork>;
|
||||||
@@ -153,6 +154,8 @@ impl pallet_xcm::Config for Runtime {
|
|||||||
type LocationInverter = LocationInverter<Ancestry>;
|
type LocationInverter = LocationInverter<Ancestry>;
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
|
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||||
|
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
|
|||||||
Reference in New Issue
Block a user