mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 07:01:03 +00:00
XCM: Tools for uniquely referencing messages (#7234)
* Tools for unique topic references * Formatting * Naming * Repot into routing.rs. * More things done * Universal Exporter supports topic-as-reference * Some tests for the topic routing * More tests * Paid bridge tests * Add message ID to sending events * Formatting * fix and integrate into test nets * Move DenyThenTry and friend from Cumulus * Append SetTopic rather than prepend * Docs * Docs * Work with new ProcessMessage ID API * Formatting * Fix build * Fixes * Formatting * Update xcm/xcm-builder/src/barriers.rs Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> * Update xcm/xcm-builder/src/routing.rs Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> * Docs * Rename message_hash * Formatting * ".git/.scripts/commands/fmt/fmt.sh" * Rename * Another Rename * ".git/.scripts/commands/fmt/fmt.sh" * ".git/.scripts/commands/fmt/fmt.sh" * Update xcm/xcm-builder/src/routing.rs Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> --------- Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: command-bot <> Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
This commit is contained in:
@@ -41,8 +41,8 @@ use xcm_builder::{
|
||||
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
||||
CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, IsChildSystemParachain, IsConcrete,
|
||||
MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32,
|
||||
SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds,
|
||||
WithComputedOrigin,
|
||||
SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents,
|
||||
WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
|
||||
};
|
||||
use xcm_executor::traits::WithOriginFilter;
|
||||
|
||||
@@ -115,14 +115,14 @@ parameter_types! {
|
||||
|
||||
/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
|
||||
/// individual routers.
|
||||
pub type XcmRouter = (
|
||||
pub type XcmRouter = WithUniqueTopic<(
|
||||
// Only one router so far - use DMP to communicate with child parachains.
|
||||
ChildParachainRouter<
|
||||
Runtime,
|
||||
XcmPallet,
|
||||
ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
|
||||
>,
|
||||
);
|
||||
)>;
|
||||
|
||||
parameter_types! {
|
||||
pub const Ksm: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) });
|
||||
@@ -142,7 +142,7 @@ match_types! {
|
||||
}
|
||||
|
||||
/// The barriers one of which must be passed for an XCM message to be executed.
|
||||
pub type Barrier = (
|
||||
pub type Barrier = TrailingSetTopicAsId<(
|
||||
// Weight that is paid for may be consumed.
|
||||
TakeWeightCredit,
|
||||
// Expected responses are OK.
|
||||
@@ -159,7 +159,7 @@ pub type Barrier = (
|
||||
UniversalLocation,
|
||||
ConstU32<8>,
|
||||
>,
|
||||
);
|
||||
)>;
|
||||
|
||||
/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly
|
||||
/// account for proof size weights.
|
||||
|
||||
@@ -40,7 +40,8 @@ use xcm_builder::{
|
||||
ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
||||
CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, IsChildSystemParachain, IsConcrete,
|
||||
MintLocation, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
|
||||
TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin,
|
||||
TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin,
|
||||
WithUniqueTopic,
|
||||
};
|
||||
use xcm_executor::{traits::WithOriginFilter, XcmExecutor};
|
||||
|
||||
@@ -95,14 +96,14 @@ parameter_types! {
|
||||
|
||||
/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
|
||||
/// individual routers.
|
||||
pub type XcmRouter = (
|
||||
pub type XcmRouter = WithUniqueTopic<(
|
||||
// Only one router so far - use DMP to communicate with child parachains.
|
||||
ChildParachainRouter<
|
||||
Runtime,
|
||||
XcmPallet,
|
||||
ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
|
||||
>,
|
||||
);
|
||||
)>;
|
||||
|
||||
parameter_types! {
|
||||
pub const Roc: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) });
|
||||
@@ -137,7 +138,7 @@ match_types! {
|
||||
}
|
||||
|
||||
/// The barriers one of which must be passed for an XCM message to be executed.
|
||||
pub type Barrier = (
|
||||
pub type Barrier = TrailingSetTopicAsId<(
|
||||
// Weight that is paid for may be consumed.
|
||||
TakeWeightCredit,
|
||||
// Expected responses are OK.
|
||||
@@ -154,7 +155,7 @@ pub type Barrier = (
|
||||
UniversalLocation,
|
||||
ConstU32<8>,
|
||||
>,
|
||||
);
|
||||
)>;
|
||||
|
||||
/// A call filter for the XCM Transact instruction. This is a temporary measure until we
|
||||
/// properly account for proof size weights.
|
||||
|
||||
@@ -39,7 +39,7 @@ use xcm_builder::{
|
||||
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
||||
CurrencyAdapter as XcmCurrencyAdapter, IsChildSystemParachain, IsConcrete, MintLocation,
|
||||
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
|
||||
UsingComponents, WeightInfoBounds, WithComputedOrigin,
|
||||
TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
|
||||
};
|
||||
use xcm_executor::{traits::WithOriginFilter, XcmExecutor};
|
||||
|
||||
@@ -80,14 +80,14 @@ type LocalOriginConverter = (
|
||||
|
||||
/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
|
||||
/// individual routers.
|
||||
pub type XcmRouter = (
|
||||
pub type XcmRouter = WithUniqueTopic<(
|
||||
// Only one router so far - use DMP to communicate with child parachains.
|
||||
ChildParachainRouter<
|
||||
Runtime,
|
||||
XcmPallet,
|
||||
ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
|
||||
>,
|
||||
);
|
||||
)>;
|
||||
|
||||
parameter_types! {
|
||||
pub const Westmint: MultiLocation = Parachain(1000).into_location();
|
||||
@@ -108,7 +108,7 @@ pub type TrustedTeleporters =
|
||||
(xcm_builder::Case<WndForWestmint>, xcm_builder::Case<WndForCollectives>);
|
||||
|
||||
/// The barriers one of which must be passed for an XCM message to be executed.
|
||||
pub type Barrier = (
|
||||
pub type Barrier = TrailingSetTopicAsId<(
|
||||
// Weight that is paid for may be consumed.
|
||||
TakeWeightCredit,
|
||||
// Expected responses are OK.
|
||||
@@ -125,7 +125,7 @@ pub type Barrier = (
|
||||
UniversalLocation,
|
||||
ConstU32<8>,
|
||||
>,
|
||||
);
|
||||
)>;
|
||||
|
||||
/// A call filter for the XCM Transact instruction. This is a temporary measure until we
|
||||
/// properly account for proof size weights.
|
||||
|
||||
@@ -49,7 +49,7 @@ benchmarks_instance_pallet! {
|
||||
&sender_location,
|
||||
&XcmContext {
|
||||
origin: Some(sender_location.clone()),
|
||||
message_hash: [0; 32],
|
||||
message_id: [0; 32],
|
||||
topic: None,
|
||||
},
|
||||
).unwrap();
|
||||
@@ -82,7 +82,7 @@ benchmarks_instance_pallet! {
|
||||
&sender_location,
|
||||
&XcmContext {
|
||||
origin: Some(sender_location.clone()),
|
||||
message_hash: [0; 32],
|
||||
message_id: [0; 32],
|
||||
topic: None,
|
||||
},
|
||||
).unwrap();
|
||||
@@ -109,7 +109,7 @@ benchmarks_instance_pallet! {
|
||||
&sender_location,
|
||||
&XcmContext {
|
||||
origin: Some(sender_location.clone()),
|
||||
message_hash: [0; 32],
|
||||
message_id: [0; 32],
|
||||
topic: None,
|
||||
},
|
||||
).unwrap();
|
||||
|
||||
@@ -209,7 +209,7 @@ benchmarks! {
|
||||
assets.clone().into(),
|
||||
&XcmContext {
|
||||
origin: Some(origin.clone()),
|
||||
message_hash: [0; 32],
|
||||
message_id: [0; 32],
|
||||
topic: None,
|
||||
},
|
||||
);
|
||||
@@ -266,7 +266,7 @@ benchmarks! {
|
||||
max_response_weight,
|
||||
&XcmContext {
|
||||
origin: Some(origin.clone()),
|
||||
message_hash: [0; 32],
|
||||
message_id: [0; 32],
|
||||
topic: None,
|
||||
},
|
||||
).map_err(|_| "Could not start subscription")?;
|
||||
|
||||
+152
-124
@@ -40,7 +40,7 @@ use sp_runtime::{
|
||||
};
|
||||
use sp_std::{boxed::Box, marker::PhantomData, prelude::*, result::Result, vec};
|
||||
use xcm::{latest::QueryResponseInfo, prelude::*};
|
||||
use xcm_executor::traits::{Convert, ConvertOrigin};
|
||||
use xcm_executor::traits::{Convert, ConvertOrigin, Properties};
|
||||
|
||||
use frame_support::{
|
||||
dispatch::{Dispatchable, GetDispatchInfo},
|
||||
@@ -272,52 +272,49 @@ pub mod pallet {
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// Execution of an XCM message was attempted.
|
||||
///
|
||||
/// \[ outcome \]
|
||||
Attempted(xcm::latest::Outcome),
|
||||
Attempted { outcome: xcm::latest::Outcome },
|
||||
/// A XCM message was sent.
|
||||
///
|
||||
/// \[ origin, destination, message \]
|
||||
Sent(MultiLocation, MultiLocation, Xcm<()>),
|
||||
Sent {
|
||||
origin: MultiLocation,
|
||||
destination: MultiLocation,
|
||||
message: Xcm<()>,
|
||||
message_id: XcmHash,
|
||||
},
|
||||
/// Query response received which does not match a registered query. This may be because a
|
||||
/// matching query was never registered, it may be because it is a duplicate response, or
|
||||
/// because the query timed out.
|
||||
///
|
||||
/// \[ origin location, id \]
|
||||
UnexpectedResponse(MultiLocation, QueryId),
|
||||
UnexpectedResponse { origin: MultiLocation, query_id: QueryId },
|
||||
/// Query response has been received and is ready for taking with `take_response`. There is
|
||||
/// no registered notification call.
|
||||
///
|
||||
/// \[ id, response \]
|
||||
ResponseReady(QueryId, Response),
|
||||
ResponseReady { query_id: QueryId, response: Response },
|
||||
/// Query response has been received and query is removed. The registered notification has
|
||||
/// been dispatched and executed successfully.
|
||||
///
|
||||
/// \[ id, pallet index, call index \]
|
||||
Notified(QueryId, u8, u8),
|
||||
Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
|
||||
/// Query response has been received and query is removed. The registered notification could
|
||||
/// not be dispatched because the dispatch weight is greater than the maximum weight
|
||||
/// originally budgeted by this runtime for the query result.
|
||||
///
|
||||
/// \[ id, pallet index, call index, actual weight, max budgeted weight \]
|
||||
NotifyOverweight(QueryId, u8, u8, Weight, Weight),
|
||||
NotifyOverweight {
|
||||
query_id: QueryId,
|
||||
pallet_index: u8,
|
||||
call_index: u8,
|
||||
actual_weight: Weight,
|
||||
max_budgeted_weight: Weight,
|
||||
},
|
||||
/// Query response has been received and query is removed. There was a general error with
|
||||
/// dispatching the notification call.
|
||||
///
|
||||
/// \[ id, pallet index, call index \]
|
||||
NotifyDispatchError(QueryId, u8, u8),
|
||||
NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
|
||||
/// Query response has been received and query is removed. The dispatch was unable to be
|
||||
/// decoded into a `Call`; this might be due to dispatch function having a signature which
|
||||
/// is not `(origin, QueryId, Response)`.
|
||||
///
|
||||
/// \[ id, pallet index, call index \]
|
||||
NotifyDecodeFailed(QueryId, u8, u8),
|
||||
NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
|
||||
/// Expected query response has been received but the origin location of the response does
|
||||
/// not match that expected. The query remains registered for a later, valid, response to
|
||||
/// be received and acted upon.
|
||||
///
|
||||
/// \[ origin location, id, expected location \]
|
||||
InvalidResponder(MultiLocation, QueryId, Option<MultiLocation>),
|
||||
InvalidResponder {
|
||||
origin: MultiLocation,
|
||||
query_id: QueryId,
|
||||
expected_location: Option<MultiLocation>,
|
||||
},
|
||||
/// Expected query response has been received but the expected origin location placed in
|
||||
/// storage by this runtime previously cannot be decoded. The query remains registered.
|
||||
///
|
||||
@@ -325,38 +322,29 @@ pub mod pallet {
|
||||
/// runtime should be readable prior to query timeout) and dangerous since the possibly
|
||||
/// valid response will be dropped. Manual governance intervention is probably going to be
|
||||
/// needed.
|
||||
///
|
||||
/// \[ origin location, id \]
|
||||
InvalidResponderVersion(MultiLocation, QueryId),
|
||||
InvalidResponderVersion { origin: MultiLocation, query_id: QueryId },
|
||||
/// Received query response has been read and removed.
|
||||
///
|
||||
/// \[ id \]
|
||||
ResponseTaken(QueryId),
|
||||
ResponseTaken { query_id: QueryId },
|
||||
/// Some assets have been placed in an asset trap.
|
||||
///
|
||||
/// \[ hash, origin, assets \]
|
||||
AssetsTrapped(H256, MultiLocation, VersionedMultiAssets),
|
||||
AssetsTrapped { hash: H256, origin: MultiLocation, assets: VersionedMultiAssets },
|
||||
/// An XCM version change notification message has been attempted to be sent.
|
||||
///
|
||||
/// The cost of sending it (borne by the chain) is included.
|
||||
///
|
||||
/// \[ destination, result, cost \]
|
||||
VersionChangeNotified(MultiLocation, XcmVersion, MultiAssets),
|
||||
VersionChangeNotified {
|
||||
destination: MultiLocation,
|
||||
result: XcmVersion,
|
||||
cost: MultiAssets,
|
||||
message_id: XcmHash,
|
||||
},
|
||||
/// 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),
|
||||
SupportedVersionChanged { location: MultiLocation, version: 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),
|
||||
NotifyTargetSendFail { location: MultiLocation, query_id: QueryId, error: 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),
|
||||
NotifyTargetMigrationFail { location: VersionedMultiLocation, query_id: QueryId },
|
||||
/// Expected query response has been received but the expected querier location placed in
|
||||
/// storage by this runtime previously cannot be decoded. The query remains registered.
|
||||
///
|
||||
@@ -364,36 +352,35 @@ pub mod pallet {
|
||||
/// runtime should be readable prior to query timeout) and dangerous since the possibly
|
||||
/// valid response will be dropped. Manual governance intervention is probably going to be
|
||||
/// needed.
|
||||
///
|
||||
/// \[ origin location, id \]
|
||||
InvalidQuerierVersion(MultiLocation, QueryId),
|
||||
InvalidQuerierVersion { origin: MultiLocation, query_id: QueryId },
|
||||
/// Expected query response has been received but the querier location of the response does
|
||||
/// not match the expected. The query remains registered for a later, valid, response to
|
||||
/// be received and acted upon.
|
||||
///
|
||||
/// \[ origin location, id, expected querier, maybe actual querier \]
|
||||
InvalidQuerier(MultiLocation, QueryId, MultiLocation, Option<MultiLocation>),
|
||||
InvalidQuerier {
|
||||
origin: MultiLocation,
|
||||
query_id: QueryId,
|
||||
expected_querier: MultiLocation,
|
||||
maybe_actual_querier: Option<MultiLocation>,
|
||||
},
|
||||
/// A remote has requested XCM version change notification from us and we have honored it.
|
||||
/// A version information message is sent to them and its cost is included.
|
||||
///
|
||||
/// \[ destination location, cost \]
|
||||
VersionNotifyStarted(MultiLocation, MultiAssets),
|
||||
/// We have requested that a remote chain sends us XCM version change notifications.
|
||||
///
|
||||
/// \[ destination location, cost \]
|
||||
VersionNotifyRequested(MultiLocation, MultiAssets),
|
||||
VersionNotifyStarted { destination: MultiLocation, cost: MultiAssets, message_id: XcmHash },
|
||||
/// We have requested that a remote chain send us XCM version change notifications.
|
||||
VersionNotifyRequested {
|
||||
destination: MultiLocation,
|
||||
cost: MultiAssets,
|
||||
message_id: XcmHash,
|
||||
},
|
||||
/// We have requested that a remote chain stops sending us XCM version change notifications.
|
||||
///
|
||||
/// \[ destination location, cost \]
|
||||
VersionNotifyUnrequested(MultiLocation, MultiAssets),
|
||||
VersionNotifyUnrequested {
|
||||
destination: MultiLocation,
|
||||
cost: MultiAssets,
|
||||
message_id: XcmHash,
|
||||
},
|
||||
/// Fees were paid from a location for an operation (often for using `SendXcm`).
|
||||
///
|
||||
/// \[ paying location, fees \]
|
||||
FeesPaid(MultiLocation, MultiAssets),
|
||||
FeesPaid { paying: MultiLocation, fees: MultiAssets },
|
||||
/// Some assets have been claimed from an asset trap
|
||||
///
|
||||
/// \[ hash, origin, assets \]
|
||||
AssetsClaimed(H256, MultiLocation, VersionedMultiAssets),
|
||||
AssetsClaimed { hash: H256, origin: MultiLocation, assets: VersionedMultiAssets },
|
||||
}
|
||||
|
||||
#[pallet::origin]
|
||||
@@ -793,8 +780,10 @@ pub mod pallet {
|
||||
let dest = MultiLocation::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
|
||||
let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
|
||||
|
||||
Self::send_xcm(interior, dest, message.clone()).map_err(Error::<T>::from)?;
|
||||
Self::deposit_event(Event::Sent(origin_location, dest, message));
|
||||
let message_id =
|
||||
Self::send_xcm(interior, dest, message.clone()).map_err(Error::<T>::from)?;
|
||||
let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
|
||||
Self::deposit_event(e);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -927,7 +916,7 @@ pub mod pallet {
|
||||
);
|
||||
let result =
|
||||
Ok(Some(outcome.weight_used().saturating_add(T::WeightInfo::execute())).into());
|
||||
Self::deposit_event(Event::Attempted(outcome));
|
||||
Self::deposit_event(Event::Attempted { outcome });
|
||||
result
|
||||
}
|
||||
|
||||
@@ -942,16 +931,16 @@ pub mod pallet {
|
||||
pub fn force_xcm_version(
|
||||
origin: OriginFor<T>,
|
||||
location: Box<MultiLocation>,
|
||||
xcm_version: XcmVersion,
|
||||
version: XcmVersion,
|
||||
) -> DispatchResult {
|
||||
T::AdminOrigin::ensure_origin(origin)?;
|
||||
let location = *location;
|
||||
SupportedVersion::<T>::insert(
|
||||
XCM_VERSION,
|
||||
LatestVersionedMultiLocation(&location),
|
||||
xcm_version,
|
||||
version,
|
||||
);
|
||||
Self::deposit_event(Event::SupportedVersionChanged(location, xcm_version));
|
||||
Self::deposit_event(Event::SupportedVersionChanged { location, version });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1195,7 +1184,7 @@ impl<T: Config> Pallet<T> {
|
||||
let hash = message.using_encoded(sp_io::hashing::blake2_256);
|
||||
let outcome =
|
||||
T::XcmExecutor::execute_xcm_in_credit(origin_location, message, hash, weight, weight);
|
||||
Self::deposit_event(Event::Attempted(outcome));
|
||||
Self::deposit_event(Event::Attempted { outcome });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1256,7 +1245,7 @@ impl<T: Config> Pallet<T> {
|
||||
let hash = message.using_encoded(sp_io::hashing::blake2_256);
|
||||
let outcome =
|
||||
T::XcmExecutor::execute_xcm_in_credit(origin_location, message, hash, weight, weight);
|
||||
Self::deposit_event(Event::Attempted(outcome));
|
||||
Self::deposit_event(Event::Attempted { outcome });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1331,14 +1320,19 @@ impl<T: Config> Pallet<T> {
|
||||
let message =
|
||||
Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
|
||||
let event = match send_xcm::<T::XcmRouter>(new_key, message) {
|
||||
Ok((_hash, cost)) => {
|
||||
Ok((message_id, cost)) => {
|
||||
let value = (query_id, max_weight, xcm_version);
|
||||
VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
|
||||
Event::VersionChangeNotified(new_key, xcm_version, cost)
|
||||
Event::VersionChangeNotified {
|
||||
destination: new_key,
|
||||
result: xcm_version,
|
||||
cost,
|
||||
message_id,
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
|
||||
Event::NotifyTargetSendFail(new_key, query_id, e.into())
|
||||
Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
|
||||
},
|
||||
};
|
||||
Self::deposit_event(event);
|
||||
@@ -1357,7 +1351,10 @@ impl<T: Config> Pallet<T> {
|
||||
let new_key = match MultiLocation::try_from(old_key.clone()) {
|
||||
Ok(k) => k,
|
||||
Err(()) => {
|
||||
Self::deposit_event(Event::NotifyTargetMigrationFail(old_key, value.0));
|
||||
Self::deposit_event(Event::NotifyTargetMigrationFail {
|
||||
location: old_key,
|
||||
query_id: value.0,
|
||||
});
|
||||
weight_used.saturating_accrue(vnt_migrate_fail_weight);
|
||||
if weight_used.any_gte(weight_cutoff) {
|
||||
return (weight_used, Some(stage))
|
||||
@@ -1380,15 +1377,24 @@ impl<T: Config> Pallet<T> {
|
||||
querier: None,
|
||||
}]);
|
||||
let event = match send_xcm::<T::XcmRouter>(new_key, message) {
|
||||
Ok((_hash, cost)) => {
|
||||
Ok((message_id, cost)) => {
|
||||
VersionNotifyTargets::<T>::insert(
|
||||
XCM_VERSION,
|
||||
versioned_key,
|
||||
(query_id, max_weight, xcm_version),
|
||||
);
|
||||
Event::VersionChangeNotified(new_key, xcm_version, cost)
|
||||
Event::VersionChangeNotified {
|
||||
destination: new_key,
|
||||
result: xcm_version,
|
||||
cost,
|
||||
message_id,
|
||||
}
|
||||
},
|
||||
Err(e) => Event::NotifyTargetSendFail {
|
||||
location: new_key,
|
||||
query_id,
|
||||
error: e.into(),
|
||||
},
|
||||
Err(e) => Event::NotifyTargetSendFail(new_key, query_id, e.into()),
|
||||
};
|
||||
Self::deposit_event(event);
|
||||
weight_used.saturating_accrue(vnt_notify_migrate_weight);
|
||||
@@ -1415,8 +1421,8 @@ impl<T: Config> Pallet<T> {
|
||||
});
|
||||
// TODO #3735: Correct weight.
|
||||
let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
|
||||
let (_hash, cost) = send_xcm::<T::XcmRouter>(dest, Xcm(vec![instruction]))?;
|
||||
Self::deposit_event(Event::VersionNotifyRequested(dest, cost));
|
||||
let (message_id, cost) = send_xcm::<T::XcmRouter>(dest, Xcm(vec![instruction]))?;
|
||||
Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
|
||||
VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
|
||||
let query_status =
|
||||
QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
|
||||
@@ -1430,8 +1436,12 @@ impl<T: Config> Pallet<T> {
|
||||
let versioned_dest = LatestVersionedMultiLocation(&dest);
|
||||
let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
|
||||
.ok_or(XcmError::InvalidLocation)?;
|
||||
let (_hash, cost) = send_xcm::<T::XcmRouter>(dest, Xcm(vec![UnsubscribeVersion]))?;
|
||||
Self::deposit_event(Event::VersionNotifyUnrequested(dest, cost));
|
||||
let (message_id, cost) = send_xcm::<T::XcmRouter>(dest, Xcm(vec![UnsubscribeVersion]))?;
|
||||
Self::deposit_event(Event::VersionNotifyUnrequested {
|
||||
destination: dest,
|
||||
cost,
|
||||
message_id,
|
||||
});
|
||||
Queries::<T>::remove(query_id);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1589,7 +1599,7 @@ impl<T: Config> Pallet<T> {
|
||||
if let Some(QueryStatus::Ready { response, at }) = Queries::<T>::get(query_id) {
|
||||
let response = response.try_into().ok()?;
|
||||
Queries::<T>::remove(query_id);
|
||||
Self::deposit_event(Event::ResponseTaken(query_id));
|
||||
Self::deposit_event(Event::ResponseTaken { query_id });
|
||||
Some((response, at))
|
||||
} else {
|
||||
None
|
||||
@@ -1623,7 +1633,7 @@ impl<T: Config> Pallet<T> {
|
||||
fn charge_fees(location: MultiLocation, assets: MultiAssets) -> DispatchResult {
|
||||
T::XcmExecutor::charge_fees(location, assets.clone())
|
||||
.map_err(|_| Error::<T>::FeesNotMet)?;
|
||||
Self::deposit_event(Event::FeesPaid(location, assets));
|
||||
Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1861,8 +1871,12 @@ impl<T: Config> VersionChangeNotifier for Pallet<T> {
|
||||
let xcm_version = T::AdvertisedXcmVersion::get();
|
||||
let response = Response::Version(xcm_version);
|
||||
let instruction = QueryResponse { query_id, response, max_weight, querier: None };
|
||||
let (_hash, cost) = send_xcm::<T::XcmRouter>(*dest, Xcm(vec![instruction]))?;
|
||||
Self::deposit_event(Event::<T>::VersionNotifyStarted(*dest, cost));
|
||||
let (message_id, cost) = send_xcm::<T::XcmRouter>(*dest, Xcm(vec![instruction]))?;
|
||||
Self::deposit_event(Event::<T>::VersionNotifyStarted {
|
||||
destination: *dest,
|
||||
cost,
|
||||
message_id,
|
||||
});
|
||||
|
||||
let value = (query_id, max_weight, xcm_version);
|
||||
VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
|
||||
@@ -1891,7 +1905,7 @@ impl<T: Config> DropAssets for Pallet<T> {
|
||||
let versioned = VersionedMultiAssets::from(MultiAssets::from(assets));
|
||||
let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
|
||||
AssetTraps::<T>::mutate(hash, |n| *n += 1);
|
||||
Self::deposit_event(Event::AssetsTrapped(hash, *origin, versioned));
|
||||
Self::deposit_event(Event::AssetsTrapped { hash, origin: *origin, assets: versioned });
|
||||
// TODO #3735: Put the real weight in there.
|
||||
Weight::zero()
|
||||
}
|
||||
@@ -1920,7 +1934,7 @@ impl<T: Config> ClaimAssets for Pallet<T> {
|
||||
1 => AssetTraps::<T>::remove(hash),
|
||||
n => AssetTraps::<T>::insert(hash, n - 1),
|
||||
}
|
||||
Self::deposit_event(Event::AssetsClaimed(hash, *origin, versioned));
|
||||
Self::deposit_event(Event::AssetsClaimed { hash, origin: *origin, assets: versioned });
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1953,19 +1967,28 @@ impl<T: Config> OnResponse for Pallet<T> {
|
||||
max_weight: Weight,
|
||||
_context: &XcmContext,
|
||||
) -> Weight {
|
||||
let origin = *origin;
|
||||
match (response, Queries::<T>::get(query_id)) {
|
||||
(
|
||||
Response::Version(v),
|
||||
Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
|
||||
) => {
|
||||
let origin: MultiLocation = match expected_origin.try_into() {
|
||||
Ok(o) if &o == origin => o,
|
||||
Ok(o) if o == origin => o,
|
||||
Ok(o) => {
|
||||
Self::deposit_event(Event::InvalidResponder(*origin, query_id, Some(o)));
|
||||
Self::deposit_event(Event::InvalidResponder {
|
||||
origin,
|
||||
query_id,
|
||||
expected_location: Some(o),
|
||||
});
|
||||
return Weight::zero()
|
||||
},
|
||||
_ => {
|
||||
Self::deposit_event(Event::InvalidResponder(*origin, query_id, None));
|
||||
Self::deposit_event(Event::InvalidResponder {
|
||||
origin,
|
||||
query_id,
|
||||
expected_location: None,
|
||||
});
|
||||
// TODO #3735: Correct weight for this.
|
||||
return Weight::zero()
|
||||
},
|
||||
@@ -1983,7 +2006,10 @@ impl<T: Config> OnResponse for Pallet<T> {
|
||||
LatestVersionedMultiLocation(&origin),
|
||||
v,
|
||||
);
|
||||
Self::deposit_event(Event::SupportedVersionChanged(origin, v));
|
||||
Self::deposit_event(Event::SupportedVersionChanged {
|
||||
location: origin,
|
||||
version: v,
|
||||
});
|
||||
Weight::zero()
|
||||
},
|
||||
(
|
||||
@@ -1994,33 +2020,33 @@ impl<T: Config> OnResponse for Pallet<T> {
|
||||
let match_querier = match MultiLocation::try_from(match_querier) {
|
||||
Ok(mq) => mq,
|
||||
Err(_) => {
|
||||
Self::deposit_event(Event::InvalidQuerierVersion(*origin, query_id));
|
||||
Self::deposit_event(Event::InvalidQuerierVersion { origin, query_id });
|
||||
return Weight::zero()
|
||||
},
|
||||
};
|
||||
if querier.map_or(true, |q| q != &match_querier) {
|
||||
Self::deposit_event(Event::InvalidQuerier(
|
||||
*origin,
|
||||
Self::deposit_event(Event::InvalidQuerier {
|
||||
origin,
|
||||
query_id,
|
||||
match_querier,
|
||||
querier.cloned(),
|
||||
));
|
||||
expected_querier: match_querier,
|
||||
maybe_actual_querier: querier.cloned(),
|
||||
});
|
||||
return Weight::zero()
|
||||
}
|
||||
}
|
||||
let responder = match MultiLocation::try_from(responder) {
|
||||
Ok(r) => r,
|
||||
Err(_) => {
|
||||
Self::deposit_event(Event::InvalidResponderVersion(*origin, query_id));
|
||||
Self::deposit_event(Event::InvalidResponderVersion { origin, query_id });
|
||||
return Weight::zero()
|
||||
},
|
||||
};
|
||||
if origin != &responder {
|
||||
Self::deposit_event(Event::InvalidResponder(
|
||||
*origin,
|
||||
if origin != responder {
|
||||
Self::deposit_event(Event::InvalidResponder {
|
||||
origin,
|
||||
query_id,
|
||||
Some(responder),
|
||||
));
|
||||
expected_location: Some(responder),
|
||||
});
|
||||
return Weight::zero()
|
||||
}
|
||||
return match maybe_notify {
|
||||
@@ -2035,29 +2061,29 @@ impl<T: Config> OnResponse for Pallet<T> {
|
||||
Queries::<T>::remove(query_id);
|
||||
let weight = call.get_dispatch_info().weight;
|
||||
if weight.any_gt(max_weight) {
|
||||
let e = Event::NotifyOverweight(
|
||||
let e = Event::NotifyOverweight {
|
||||
query_id,
|
||||
pallet_index,
|
||||
call_index,
|
||||
weight,
|
||||
max_weight,
|
||||
);
|
||||
actual_weight: weight,
|
||||
max_budgeted_weight: max_weight,
|
||||
};
|
||||
Self::deposit_event(e);
|
||||
return Weight::zero()
|
||||
}
|
||||
let dispatch_origin = Origin::Response(*origin).into();
|
||||
let dispatch_origin = Origin::Response(origin).into();
|
||||
match call.dispatch(dispatch_origin) {
|
||||
Ok(post_info) => {
|
||||
let e = Event::Notified(query_id, pallet_index, call_index);
|
||||
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(
|
||||
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.
|
||||
@@ -2066,13 +2092,14 @@ impl<T: Config> OnResponse for Pallet<T> {
|
||||
}
|
||||
.unwrap_or(weight)
|
||||
} else {
|
||||
let e = Event::NotifyDecodeFailed(query_id, pallet_index, call_index);
|
||||
let e =
|
||||
Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
|
||||
Self::deposit_event(e);
|
||||
Weight::zero()
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let e = Event::ResponseReady(query_id, response.clone());
|
||||
let e = Event::ResponseReady { query_id, response: response.clone() };
|
||||
Self::deposit_event(e);
|
||||
let at = frame_system::Pallet::<T>::current_block_number();
|
||||
let response = response.into();
|
||||
@@ -2082,7 +2109,8 @@ impl<T: Config> OnResponse for Pallet<T> {
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
Self::deposit_event(Event::UnexpectedResponse(*origin, query_id));
|
||||
let e = Event::UnexpectedResponse { origin, query_id };
|
||||
Self::deposit_event(e);
|
||||
Weight::zero()
|
||||
},
|
||||
}
|
||||
@@ -2094,7 +2122,7 @@ impl<T: Config> CheckSuspension for Pallet<T> {
|
||||
_origin: &MultiLocation,
|
||||
_instructions: &mut [Instruction<Call>],
|
||||
_max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
_properties: &mut Properties,
|
||||
) -> bool {
|
||||
XcmExecutionSuspended::<T>::get()
|
||||
}
|
||||
|
||||
@@ -27,7 +27,10 @@ use polkadot_parachain::primitives::Id as ParaId;
|
||||
use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, Hash};
|
||||
use xcm::{latest::QueryResponseInfo, prelude::*};
|
||||
use xcm_builder::AllowKnownQueryResponses;
|
||||
use xcm_executor::{traits::ShouldExecute, XcmExecutor};
|
||||
use xcm_executor::{
|
||||
traits::{Properties, ShouldExecute},
|
||||
XcmExecutor,
|
||||
};
|
||||
|
||||
const ALICE: AccountId = AccountId::new([0u8; 32]);
|
||||
const BOB: AccountId = AccountId::new([1u8; 32]);
|
||||
@@ -101,7 +104,11 @@ fn report_outcome_notify_works() {
|
||||
0,
|
||||
Response::ExecutionResult(None),
|
||||
)),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Notified(0, 4, 2)),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Notified {
|
||||
query_id: 0,
|
||||
pallet_index: 4,
|
||||
call_index: 2
|
||||
}),
|
||||
]
|
||||
);
|
||||
assert_eq!(crate::Queries::<Test>::iter().collect::<Vec<_>>(), vec![]);
|
||||
@@ -157,10 +164,10 @@ fn report_outcome_works() {
|
||||
assert_eq!(r, Outcome::Complete(Weight::from_parts(1_000, 1_000)));
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::ResponseReady(
|
||||
0,
|
||||
Response::ExecutionResult(None),
|
||||
))
|
||||
RuntimeEvent::XcmPallet(crate::Event::ResponseReady {
|
||||
query_id: 0,
|
||||
response: Response::ExecutionResult(None),
|
||||
})
|
||||
);
|
||||
|
||||
let response = Some((Response::ExecutionResult(None), 1));
|
||||
@@ -206,12 +213,12 @@ fn custom_querier_works() {
|
||||
assert_eq!(r, Outcome::Complete(Weight::from_parts(1_000, 1_000)));
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::InvalidQuerier(
|
||||
AccountId32 { network: None, id: ALICE.into() }.into(),
|
||||
0,
|
||||
querier.clone(),
|
||||
None,
|
||||
)),
|
||||
RuntimeEvent::XcmPallet(crate::Event::InvalidQuerier {
|
||||
origin: AccountId32 { network: None, id: ALICE.into() }.into(),
|
||||
query_id: 0,
|
||||
expected_querier: querier.clone(),
|
||||
maybe_actual_querier: None,
|
||||
}),
|
||||
);
|
||||
|
||||
// Supplying the wrong querier will also fail
|
||||
@@ -232,12 +239,12 @@ fn custom_querier_works() {
|
||||
assert_eq!(r, Outcome::Complete(Weight::from_parts(1_000, 1_000)));
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::InvalidQuerier(
|
||||
AccountId32 { network: None, id: ALICE.into() }.into(),
|
||||
0,
|
||||
querier.clone(),
|
||||
Some(MultiLocation::here()),
|
||||
)),
|
||||
RuntimeEvent::XcmPallet(crate::Event::InvalidQuerier {
|
||||
origin: AccountId32 { network: None, id: ALICE.into() }.into(),
|
||||
query_id: 0,
|
||||
expected_querier: querier.clone(),
|
||||
maybe_actual_querier: Some(MultiLocation::here()),
|
||||
}),
|
||||
);
|
||||
|
||||
// Multiple failures should not have changed the query state
|
||||
@@ -257,10 +264,10 @@ fn custom_querier_works() {
|
||||
assert_eq!(r, Outcome::Complete(Weight::from_parts(1_000, 1_000)));
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::ResponseReady(
|
||||
0,
|
||||
Response::ExecutionResult(None),
|
||||
))
|
||||
RuntimeEvent::XcmPallet(crate::Event::ResponseReady {
|
||||
query_id: 0,
|
||||
response: Response::ExecutionResult(None),
|
||||
})
|
||||
);
|
||||
|
||||
let response = Some((Response::ExecutionResult(None), 1));
|
||||
@@ -285,6 +292,7 @@ fn send_works() {
|
||||
buy_execution((Parent, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
|
||||
]);
|
||||
|
||||
let versioned_dest = Box::new(RelayLocation::get().into());
|
||||
let versioned_message = Box::new(VersionedXcm::from(message.clone()));
|
||||
assert_ok!(XcmPallet::send(
|
||||
@@ -292,19 +300,20 @@ fn send_works() {
|
||||
versioned_dest,
|
||||
versioned_message
|
||||
));
|
||||
assert_eq!(
|
||||
sent_xcm(),
|
||||
vec![(
|
||||
Here.into(),
|
||||
Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap()))
|
||||
.into_iter()
|
||||
.chain(message.0.clone().into_iter())
|
||||
.collect())
|
||||
)],
|
||||
);
|
||||
let sent_message = Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap()))
|
||||
.into_iter()
|
||||
.chain(message.0.clone().into_iter())
|
||||
.collect());
|
||||
let id = fake_message_hash(&sent_message);
|
||||
assert_eq!(sent_xcm(), vec![(Here.into(), sent_message)]);
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Sent(sender, RelayLocation::get(), message))
|
||||
RuntimeEvent::XcmPallet(crate::Event::Sent {
|
||||
origin: sender,
|
||||
destination: RelayLocation::get(),
|
||||
message,
|
||||
message_id: id,
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -376,7 +385,7 @@ fn teleport_assets_works() {
|
||||
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight)))
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -420,7 +429,7 @@ fn limited_teleport_assets_works() {
|
||||
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight)))
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -462,7 +471,7 @@ fn unlimited_teleport_assets_works() {
|
||||
);
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight)))
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -509,7 +518,7 @@ fn reserve_transfer_assets_works() {
|
||||
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight)))
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -557,7 +566,7 @@ fn limited_reserve_transfer_assets_works() {
|
||||
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight)))
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -603,7 +612,7 @@ fn unlimited_reserve_transfer_assets_works() {
|
||||
);
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight)))
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -635,7 +644,7 @@ fn execute_withdraw_to_deposit_works() {
|
||||
assert_eq!(Balances::total_balance(&BOB), SEND_AMOUNT);
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Complete(weight)))
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -670,10 +679,14 @@ fn trapped_assets_can_be_claimed() {
|
||||
assert_eq!(
|
||||
last_events(2),
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(crate::Event::AssetsTrapped(hash.clone(), source, vma)),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Complete(
|
||||
BaseXcmWeight::get() * 5
|
||||
)))
|
||||
RuntimeEvent::XcmPallet(crate::Event::AssetsTrapped {
|
||||
hash: hash.clone(),
|
||||
origin: source,
|
||||
assets: vma
|
||||
}),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted {
|
||||
outcome: Outcome::Complete(BaseXcmWeight::get() * 5)
|
||||
}),
|
||||
]
|
||||
);
|
||||
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
|
||||
@@ -707,13 +720,8 @@ fn trapped_assets_can_be_claimed() {
|
||||
]))),
|
||||
weight
|
||||
));
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Incomplete(
|
||||
BaseXcmWeight::get(),
|
||||
XcmError::UnknownClaim
|
||||
)))
|
||||
);
|
||||
let outcome = Outcome::Incomplete(BaseXcmWeight::get(), XcmError::UnknownClaim);
|
||||
assert_eq!(last_event(), RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome }));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -768,7 +776,7 @@ fn basic_subscription_works() {
|
||||
&remote,
|
||||
message.inner_mut(),
|
||||
weight,
|
||||
&mut Weight::zero(),
|
||||
&mut Properties { weight_credit: Weight::zero(), message_id: None },
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ pub mod prelude {
|
||||
NetworkId::{self, *},
|
||||
OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId,
|
||||
QueryResponseInfo, Response, Result as XcmResult, SendError, SendResult, SendXcm,
|
||||
Unwrappable,
|
||||
Unwrappable, Weight,
|
||||
WeightLimit::{self, *},
|
||||
WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
|
||||
WildMultiAsset::{self, *},
|
||||
@@ -337,19 +337,27 @@ impl TryFrom<OldWeightLimit> for WeightLimit {
|
||||
/// Contextual data pertaining to a specific list of XCM instructions.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
|
||||
pub struct XcmContext {
|
||||
/// The `MultiLocation` origin of the corresponding XCM.
|
||||
/// The current value of the Origin register of the XCVM.
|
||||
pub origin: Option<MultiLocation>,
|
||||
/// The hash of the XCM.
|
||||
pub message_hash: XcmHash,
|
||||
/// The topic of the XCM.
|
||||
/// The identity of the XCM; this may be a hash of its versioned encoding but could also be
|
||||
/// a high-level identity set by an appropriate barrier.
|
||||
pub message_id: XcmHash,
|
||||
/// The current value of the Topic register of the XCVM.
|
||||
pub topic: Option<[u8; 32]>,
|
||||
}
|
||||
|
||||
impl XcmContext {
|
||||
/// Constructor which sets the message hash to the supplied parameter and leaves the origin and
|
||||
/// Constructor which sets the message ID to the supplied parameter and leaves the origin and
|
||||
/// topic unset.
|
||||
pub fn with_message_hash(message_hash: XcmHash) -> XcmContext {
|
||||
XcmContext { origin: None, message_hash, topic: None }
|
||||
#[deprecated = "Use `with_message_id` instead."]
|
||||
pub fn with_message_hash(message_id: XcmHash) -> XcmContext {
|
||||
XcmContext { origin: None, message_id, topic: None }
|
||||
}
|
||||
|
||||
/// Constructor which sets the message ID to the supplied parameter and leaves the origin and
|
||||
/// topic unset.
|
||||
pub fn with_message_id(message_id: XcmHash) -> XcmContext {
|
||||
XcmContext { origin: None, message_id, topic: None }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,7 +941,7 @@ pub enum Instruction<Call> {
|
||||
UnlockAsset { asset: MultiAsset, target: MultiLocation },
|
||||
|
||||
/// Asset (`asset`) has been locked on the `origin` system and may not be transferred. It may
|
||||
/// only be unlocked with the receipt of the `UnlockAsset` instruction from this chain.
|
||||
/// only be unlocked with the receipt of the `UnlockAsset` instruction from this chain.
|
||||
///
|
||||
/// - `asset`: The asset(s) which are now unlockable from this origin.
|
||||
/// - `owner`: The owner of the asset on the chain in which it was locked. This may be a
|
||||
|
||||
@@ -212,6 +212,52 @@ impl From<SendError> for Error {
|
||||
|
||||
pub type Result = result::Result<(), Error>;
|
||||
|
||||
/*
|
||||
TODO: XCMv4
|
||||
/// Outcome of an XCM execution.
|
||||
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
|
||||
pub enum Outcome {
|
||||
/// Execution completed successfully; given weight was used.
|
||||
Complete { used: Weight },
|
||||
/// Execution started, but did not complete successfully due to the given error; given weight
|
||||
/// was used.
|
||||
Incomplete { used: Weight, error: Error },
|
||||
/// Execution did not start due to the given error.
|
||||
Error { error: Error },
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
pub fn ensure_complete(self) -> Result {
|
||||
match self {
|
||||
Outcome::Complete { .. } => Ok(()),
|
||||
Outcome::Incomplete { error, .. } => Err(error),
|
||||
Outcome::Error { error, .. } => Err(error),
|
||||
}
|
||||
}
|
||||
pub fn ensure_execution(self) -> result::Result<Weight, Error> {
|
||||
match self {
|
||||
Outcome::Complete { used, .. } => Ok(used),
|
||||
Outcome::Incomplete { used, .. } => Ok(used),
|
||||
Outcome::Error { error, .. } => Err(error),
|
||||
}
|
||||
}
|
||||
/// How much weight was used by the XCM execution attempt.
|
||||
pub fn weight_used(&self) -> Weight {
|
||||
match self {
|
||||
Outcome::Complete { used, .. } => *used,
|
||||
Outcome::Incomplete { used, .. } => *used,
|
||||
Outcome::Error { .. } => Weight::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for Outcome {
|
||||
fn from(error: Error) -> Self {
|
||||
Self::Error { error, maybe_id: None }
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/// Outcome of an XCM execution.
|
||||
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
|
||||
pub enum Outcome {
|
||||
@@ -259,13 +305,34 @@ pub trait ExecuteXcm<Call> {
|
||||
fn execute(
|
||||
origin: impl Into<MultiLocation>,
|
||||
pre: Self::Prepared,
|
||||
hash: XcmHash,
|
||||
id: &mut XcmHash,
|
||||
weight_credit: Weight,
|
||||
) -> Outcome;
|
||||
fn prepare_and_execute(
|
||||
origin: impl Into<MultiLocation>,
|
||||
message: Xcm<Call>,
|
||||
id: &mut XcmHash,
|
||||
weight_limit: Weight,
|
||||
weight_credit: Weight,
|
||||
) -> Outcome {
|
||||
let pre = match Self::prepare(message) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Outcome::Error(Error::WeightNotComputable),
|
||||
};
|
||||
let xcm_weight = pre.weight_of();
|
||||
if xcm_weight.any_gt(weight_limit) {
|
||||
return Outcome::Error(Error::WeightLimitReached(xcm_weight))
|
||||
}
|
||||
Self::execute(origin, pre, id, weight_credit)
|
||||
}
|
||||
|
||||
/// Execute some XCM `message` with the message `hash` from `origin` using no more than `weight_limit` weight.
|
||||
/// The weight limit is a basic hard-limit and the implementation may place further restrictions or requirements
|
||||
/// on weight and other aspects.
|
||||
/// Execute some XCM `message` with the message `hash` from `origin` using no more than
|
||||
/// `weight_limit` weight.
|
||||
///
|
||||
/// The weight limit is a basic hard-limit and the implementation may place further
|
||||
/// restrictions or requirements on weight and other aspects.
|
||||
// TODO: XCMv4
|
||||
// #[deprecated = "Use `prepare_and_execute` instead"]
|
||||
fn execute_xcm(
|
||||
origin: impl Into<MultiLocation>,
|
||||
message: Xcm<Call>,
|
||||
@@ -283,14 +350,17 @@ pub trait ExecuteXcm<Call> {
|
||||
Self::execute_xcm_in_credit(origin, message, hash, weight_limit, Weight::zero())
|
||||
}
|
||||
|
||||
/// Execute some XCM `message` with the message `hash` from `origin` using no more than `weight_limit` weight.
|
||||
/// Execute some XCM `message` with the message `hash` from `origin` using no more than
|
||||
/// `weight_limit` weight.
|
||||
///
|
||||
/// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow
|
||||
/// execution without associated payment.
|
||||
/// Some amount of `weight_credit` may be provided which, depending on the implementation, may
|
||||
/// allow execution without associated payment.
|
||||
// TODO: XCMv4
|
||||
// #[deprecated = "Use `prepare_and_execute` instead"]
|
||||
fn execute_xcm_in_credit(
|
||||
origin: impl Into<MultiLocation>,
|
||||
message: Xcm<Call>,
|
||||
hash: XcmHash,
|
||||
mut hash: XcmHash,
|
||||
weight_limit: Weight,
|
||||
weight_credit: Weight,
|
||||
) -> Outcome {
|
||||
@@ -302,7 +372,7 @@ pub trait ExecuteXcm<Call> {
|
||||
if xcm_weight.any_gt(weight_limit) {
|
||||
return Outcome::Error(Error::WeightLimitReached(xcm_weight))
|
||||
}
|
||||
Self::execute(origin, pre, hash, weight_credit)
|
||||
Self::execute(origin, pre, &mut hash, weight_credit)
|
||||
}
|
||||
|
||||
/// Deduct some `fees` to the sovereign account of the given `location` and place them as per
|
||||
@@ -322,7 +392,12 @@ impl<C> ExecuteXcm<C> for () {
|
||||
fn prepare(message: Xcm<C>) -> result::Result<Self::Prepared, Xcm<C>> {
|
||||
Err(message)
|
||||
}
|
||||
fn execute(_: impl Into<MultiLocation>, _: Self::Prepared, _: XcmHash, _: Weight) -> Outcome {
|
||||
fn execute(
|
||||
_: impl Into<MultiLocation>,
|
||||
_: Self::Prepared,
|
||||
_: &mut XcmHash,
|
||||
_: Weight,
|
||||
) -> Outcome {
|
||||
unreachable!()
|
||||
}
|
||||
fn charge_fees(_location: impl Into<MultiLocation>, _fees: MultiAssets) -> Result {
|
||||
|
||||
@@ -23,16 +23,10 @@ use frame_support::{
|
||||
};
|
||||
use polkadot_parachain::primitives::IsSystem;
|
||||
use sp_std::{cell::Cell, marker::PhantomData, ops::ControlFlow, result::Result};
|
||||
use xcm::latest::{
|
||||
Instruction::{self, *},
|
||||
InteriorMultiLocation, Junction, Junctions,
|
||||
Junctions::X1,
|
||||
MultiLocation, Weight,
|
||||
WeightLimit::*,
|
||||
};
|
||||
use xcm_executor::traits::{CheckSuspension, OnResponse, ShouldExecute};
|
||||
use xcm::prelude::*;
|
||||
use xcm_executor::traits::{CheckSuspension, OnResponse, Properties, ShouldExecute};
|
||||
|
||||
/// Execution barrier that just takes `max_weight` from `weight_credit`.
|
||||
/// Execution barrier that just takes `max_weight` from `properties.weight_credit`.
|
||||
///
|
||||
/// Useful to allow XCM execution by local chain users via extrinsics.
|
||||
/// E.g. `pallet_xcm::reserve_asset_transfer` to transfer a reserve asset
|
||||
@@ -43,14 +37,15 @@ impl ShouldExecute for TakeWeightCredit {
|
||||
_origin: &MultiLocation,
|
||||
_instructions: &mut [Instruction<RuntimeCall>],
|
||||
max_weight: Weight,
|
||||
weight_credit: &mut Weight,
|
||||
properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
log::trace!(
|
||||
target: "xcm::barriers",
|
||||
"TakeWeightCredit origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
||||
_origin, _instructions, max_weight, weight_credit,
|
||||
"TakeWeightCredit origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
|
||||
_origin, _instructions, max_weight, properties,
|
||||
);
|
||||
*weight_credit = weight_credit
|
||||
properties.weight_credit = properties
|
||||
.weight_credit
|
||||
.checked_sub(&max_weight)
|
||||
.ok_or(ProcessMessageError::Overweight(max_weight))?;
|
||||
Ok(())
|
||||
@@ -68,12 +63,12 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFro
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<RuntimeCall>],
|
||||
max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
_properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
log::trace!(
|
||||
target: "xcm::barriers",
|
||||
"AllowTopLevelPaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
||||
origin, instructions, max_weight, _weight_credit,
|
||||
"AllowTopLevelPaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
|
||||
origin, instructions, max_weight, _properties,
|
||||
);
|
||||
|
||||
ensure!(T::contains(origin), ProcessMessageError::Unsupported);
|
||||
@@ -166,12 +161,12 @@ impl<
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<Call>],
|
||||
max_weight: Weight,
|
||||
weight_credit: &mut Weight,
|
||||
properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
log::trace!(
|
||||
target: "xcm::barriers",
|
||||
"WithComputedOrigin origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
||||
origin, instructions, max_weight, weight_credit,
|
||||
"WithComputedOrigin origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
|
||||
origin, instructions, max_weight, properties,
|
||||
);
|
||||
let mut actual_origin = *origin;
|
||||
let skipped = Cell::new(0usize);
|
||||
@@ -205,11 +200,37 @@ impl<
|
||||
&actual_origin,
|
||||
&mut instructions[skipped.get()..],
|
||||
max_weight,
|
||||
weight_credit,
|
||||
properties,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the message ID to `t` using a `SetTopic(t)` in the last position if present.
|
||||
///
|
||||
/// Requires some inner barrier to pass on the rest of the message.
|
||||
pub struct TrailingSetTopicAsId<InnerBarrier>(PhantomData<InnerBarrier>);
|
||||
impl<InnerBarrier: ShouldExecute> ShouldExecute for TrailingSetTopicAsId<InnerBarrier> {
|
||||
fn should_execute<Call>(
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<Call>],
|
||||
max_weight: Weight,
|
||||
properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
log::trace!(
|
||||
target: "xcm::barriers",
|
||||
"TrailingSetTopicAsId origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
|
||||
origin, instructions, max_weight, properties,
|
||||
);
|
||||
let until = if let Some(SetTopic(t)) = instructions.last() {
|
||||
properties.message_id = Some(*t);
|
||||
instructions.len() - 1
|
||||
} else {
|
||||
instructions.len()
|
||||
};
|
||||
InnerBarrier::should_execute(&origin, &mut instructions[..until], max_weight, properties)
|
||||
}
|
||||
}
|
||||
|
||||
/// Barrier condition that allows for a `SuspensionChecker` that controls whether or not the XCM
|
||||
/// executor will be suspended from executing the given XCM.
|
||||
pub struct RespectSuspension<Inner, SuspensionChecker>(PhantomData<(Inner, SuspensionChecker)>);
|
||||
@@ -222,12 +243,12 @@ where
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<Call>],
|
||||
max_weight: Weight,
|
||||
weight_credit: &mut Weight,
|
||||
properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
if SuspensionChecker::is_suspended(origin, instructions, max_weight, weight_credit) {
|
||||
if SuspensionChecker::is_suspended(origin, instructions, max_weight, properties) {
|
||||
Err(ProcessMessageError::Yield)
|
||||
} else {
|
||||
Inner::should_execute(origin, instructions, max_weight, weight_credit)
|
||||
Inner::should_execute(origin, instructions, max_weight, properties)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,12 +263,12 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<RuntimeCall>],
|
||||
_max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
_properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
log::trace!(
|
||||
target: "xcm::barriers",
|
||||
"AllowUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
||||
origin, instructions, _max_weight, _weight_credit,
|
||||
"AllowUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
|
||||
origin, instructions, _max_weight, _properties,
|
||||
);
|
||||
ensure!(T::contains(origin), ProcessMessageError::Unsupported);
|
||||
Ok(())
|
||||
@@ -264,12 +285,12 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowExplicitUnpaidExecutionF
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<Call>],
|
||||
max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
_properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
log::trace!(
|
||||
target: "xcm::barriers",
|
||||
"AllowExplicitUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
||||
origin, instructions, max_weight, _weight_credit,
|
||||
"AllowExplicitUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
|
||||
origin, instructions, max_weight, _properties,
|
||||
);
|
||||
ensure!(T::contains(origin), ProcessMessageError::Unsupported);
|
||||
instructions.matcher().match_next_inst(|inst| match inst {
|
||||
@@ -300,12 +321,12 @@ impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<Res
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<RuntimeCall>],
|
||||
_max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
_properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
log::trace!(
|
||||
target: "xcm::barriers",
|
||||
"AllowKnownQueryResponses origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
||||
origin, instructions, _max_weight, _weight_credit,
|
||||
"AllowKnownQueryResponses origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
|
||||
origin, instructions, _max_weight, _properties,
|
||||
);
|
||||
instructions
|
||||
.matcher()
|
||||
@@ -328,12 +349,12 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowSubscriptionsFrom<T> {
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<RuntimeCall>],
|
||||
_max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
_properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
log::trace!(
|
||||
target: "xcm::barriers",
|
||||
"AllowSubscriptionsFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
||||
origin, instructions, _max_weight, _weight_credit,
|
||||
"AllowSubscriptionsFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
|
||||
origin, instructions, _max_weight, _properties,
|
||||
);
|
||||
ensure!(T::contains(origin), ProcessMessageError::Unsupported);
|
||||
instructions
|
||||
@@ -346,3 +367,72 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowSubscriptionsFrom<T> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Deny executing the XCM if it matches any of the Deny filter regardless of anything else.
|
||||
/// If it passes the Deny, and matches one of the Allow cases then it is let through.
|
||||
pub struct DenyThenTry<Deny, Allow>(PhantomData<Deny>, PhantomData<Allow>)
|
||||
where
|
||||
Deny: ShouldExecute,
|
||||
Allow: ShouldExecute;
|
||||
|
||||
impl<Deny, Allow> ShouldExecute for DenyThenTry<Deny, Allow>
|
||||
where
|
||||
Deny: ShouldExecute,
|
||||
Allow: ShouldExecute,
|
||||
{
|
||||
fn should_execute<RuntimeCall>(
|
||||
origin: &MultiLocation,
|
||||
message: &mut [Instruction<RuntimeCall>],
|
||||
max_weight: Weight,
|
||||
properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
Deny::should_execute(origin, message, max_weight, properties)?;
|
||||
Allow::should_execute(origin, message, max_weight, properties)
|
||||
}
|
||||
}
|
||||
|
||||
// See issue <https://github.com/paritytech/polkadot/issues/5233>
|
||||
pub struct DenyReserveTransferToRelayChain;
|
||||
impl ShouldExecute for DenyReserveTransferToRelayChain {
|
||||
fn should_execute<RuntimeCall>(
|
||||
origin: &MultiLocation,
|
||||
message: &mut [Instruction<RuntimeCall>],
|
||||
_max_weight: Weight,
|
||||
_properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
message.matcher().match_next_inst_while(
|
||||
|_| true,
|
||||
|inst| match inst {
|
||||
InitiateReserveWithdraw {
|
||||
reserve: MultiLocation { parents: 1, interior: Here },
|
||||
..
|
||||
} |
|
||||
DepositReserveAsset {
|
||||
dest: MultiLocation { parents: 1, interior: Here }, ..
|
||||
} |
|
||||
TransferReserveAsset {
|
||||
dest: MultiLocation { parents: 1, interior: Here }, ..
|
||||
} => {
|
||||
Err(ProcessMessageError::Unsupported) // Deny
|
||||
},
|
||||
|
||||
// An unexpected reserve transfer has arrived from the Relay Chain. Generally,
|
||||
// `IsReserve` should not allow this, but we just log it here.
|
||||
ReserveAssetDeposited { .. }
|
||||
if matches!(origin, MultiLocation { parents: 1, interior: Here }) =>
|
||||
{
|
||||
log::warn!(
|
||||
target: "xcm::barrier",
|
||||
"Unexpected ReserveAssetDeposited from the Relay Chain",
|
||||
);
|
||||
Ok(ControlFlow::Continue(()))
|
||||
},
|
||||
|
||||
_ => Ok(ControlFlow::Continue(())),
|
||||
},
|
||||
)?;
|
||||
|
||||
// Permit everything else
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,9 @@ pub use asset_conversion::{ConvertedAbstractAssetId, ConvertedConcreteAssetId};
|
||||
mod barriers;
|
||||
pub use barriers::{
|
||||
AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom,
|
||||
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, IsChildSystemParachain,
|
||||
RespectSuspension, TakeWeightCredit, WithComputedOrigin,
|
||||
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, DenyReserveTransferToRelayChain,
|
||||
DenyThenTry, IsChildSystemParachain, RespectSuspension, TakeWeightCredit, TrailingSetTopicAsId,
|
||||
WithComputedOrigin,
|
||||
};
|
||||
|
||||
mod process_xcm_message;
|
||||
@@ -85,6 +86,9 @@ pub use matcher::{CreateMatcher, MatchXcm, Matcher};
|
||||
mod filter_asset_location;
|
||||
pub use filter_asset_location::{Case, NativeAsset};
|
||||
|
||||
mod routing;
|
||||
pub use routing::{WithTopicSource, WithUniqueTopic};
|
||||
|
||||
mod universal_exports;
|
||||
pub use universal_exports::{
|
||||
ensure_is_remote, BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError,
|
||||
|
||||
@@ -53,7 +53,7 @@ impl<
|
||||
let required = pre.weight_of();
|
||||
ensure!(meter.can_accrue(required), ProcessMessageError::Overweight(required));
|
||||
|
||||
let (consumed, result) = match XcmExecutor::execute(origin.into(), pre, *id, Weight::zero())
|
||||
let (consumed, result) = match XcmExecutor::execute(origin.into(), pre, id, Weight::zero())
|
||||
{
|
||||
Outcome::Complete(w) => (w, Ok(true)),
|
||||
Outcome::Incomplete(w, _) => (w, Ok(false)),
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Various implementations for `SendXcm`.
|
||||
|
||||
use frame_system::unique;
|
||||
use parity_scale_codec::Encode;
|
||||
use sp_std::{marker::PhantomData, result::Result};
|
||||
use xcm::prelude::*;
|
||||
|
||||
/// Wrapper router which, if the message does not already end with a `SetTopic` instruction,
|
||||
/// appends one to the message filled with a universally unique ID. This ID is returned from a
|
||||
/// successful `deliver`.
|
||||
///
|
||||
/// This is designed to be at the top-level of any routers, since it will always mutate the
|
||||
/// passed `message` reference into a `None`. Don't try to combine it within a tuple except as the
|
||||
/// last element.
|
||||
pub struct WithUniqueTopic<Inner>(PhantomData<Inner>);
|
||||
impl<Inner: SendXcm> SendXcm for WithUniqueTopic<Inner> {
|
||||
type Ticket = (Inner::Ticket, [u8; 32]);
|
||||
|
||||
fn validate(
|
||||
destination: &mut Option<MultiLocation>,
|
||||
message: &mut Option<Xcm<()>>,
|
||||
) -> SendResult<Self::Ticket> {
|
||||
let mut message = message.take().ok_or(SendError::MissingArgument)?;
|
||||
let unique_id = if let Some(SetTopic(id)) = message.last() {
|
||||
*id
|
||||
} else {
|
||||
let unique_id = unique(&message);
|
||||
message.0.push(SetTopic(unique_id.clone()));
|
||||
unique_id
|
||||
};
|
||||
let (ticket, assets) = Inner::validate(destination, &mut Some(message))
|
||||
.map_err(|_| SendError::NotApplicable)?;
|
||||
Ok(((ticket, unique_id), assets))
|
||||
}
|
||||
|
||||
fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
|
||||
let (ticket, unique_id) = ticket;
|
||||
Inner::deliver(ticket)?;
|
||||
Ok(unique_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SourceTopic {
|
||||
fn source_topic(entropy: impl Encode) -> XcmHash;
|
||||
}
|
||||
|
||||
impl SourceTopic for () {
|
||||
fn source_topic(_: impl Encode) -> XcmHash {
|
||||
[0u8; 32]
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper router which, if the message does not already end with a `SetTopic` instruction,
|
||||
/// prepends one to the message filled with an ID from `TopicSource`. This ID is returned from a
|
||||
/// successful `deliver`.
|
||||
///
|
||||
/// This is designed to be at the top-level of any routers, since it will always mutate the
|
||||
/// passed `message` reference into a `None`. Don't try to combine it within a tuple except as the
|
||||
/// last element.
|
||||
pub struct WithTopicSource<Inner, TopicSource>(PhantomData<(Inner, TopicSource)>);
|
||||
impl<Inner: SendXcm, TopicSource: SourceTopic> SendXcm for WithTopicSource<Inner, TopicSource> {
|
||||
type Ticket = (Inner::Ticket, [u8; 32]);
|
||||
|
||||
fn validate(
|
||||
destination: &mut Option<MultiLocation>,
|
||||
message: &mut Option<Xcm<()>>,
|
||||
) -> SendResult<Self::Ticket> {
|
||||
let mut message = message.take().ok_or(SendError::MissingArgument)?;
|
||||
let unique_id = if let Some(SetTopic(id)) = message.last() {
|
||||
*id
|
||||
} else {
|
||||
let unique_id = TopicSource::source_topic(&message);
|
||||
message.0.push(SetTopic(unique_id.clone()));
|
||||
unique_id
|
||||
};
|
||||
let (ticket, assets) = Inner::validate(destination, &mut Some(message))
|
||||
.map_err(|_| SendError::NotApplicable)?;
|
||||
Ok(((ticket, unique_id), assets))
|
||||
}
|
||||
|
||||
fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
|
||||
let (ticket, unique_id) = ticket;
|
||||
Inner::deliver(ticket)?;
|
||||
Ok(unique_id)
|
||||
}
|
||||
}
|
||||
@@ -14,30 +14,36 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use xcm_executor::traits::Properties;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn props(weight_credit: Weight) -> Properties {
|
||||
Properties { weight_credit, message_id: None }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_weight_credit_barrier_should_work() {
|
||||
let mut message =
|
||||
Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]);
|
||||
let mut weight_credit = Weight::from_parts(10, 10);
|
||||
let mut properties = props(Weight::from_parts(10, 10));
|
||||
let r = TakeWeightCredit::should_execute(
|
||||
&Parent.into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(10, 10),
|
||||
&mut weight_credit,
|
||||
&mut properties,
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
assert_eq!(weight_credit, Weight::zero());
|
||||
assert_eq!(properties.weight_credit, Weight::zero());
|
||||
|
||||
let r = TakeWeightCredit::should_execute(
|
||||
&Parent.into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(10, 10),
|
||||
&mut weight_credit,
|
||||
&mut properties,
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(10, 10))));
|
||||
assert_eq!(weight_credit, Weight::zero());
|
||||
assert_eq!(properties.weight_credit, Weight::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -67,7 +73,7 @@ fn computed_origin_should_work() {
|
||||
&Parent.into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(100, 100),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Unsupported));
|
||||
|
||||
@@ -79,7 +85,7 @@ fn computed_origin_should_work() {
|
||||
&Parent.into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(100, 100),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Unsupported));
|
||||
|
||||
@@ -91,7 +97,7 @@ fn computed_origin_should_work() {
|
||||
&Parent.into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(100, 100),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
}
|
||||
@@ -107,7 +113,7 @@ fn allow_unpaid_should_work() {
|
||||
&Parachain(1).into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(10, 10),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Unsupported));
|
||||
|
||||
@@ -115,7 +121,7 @@ fn allow_unpaid_should_work() {
|
||||
&Parent.into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(10, 10),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
}
|
||||
@@ -147,7 +153,7 @@ fn allow_explicit_unpaid_should_work() {
|
||||
&Parachain(1).into(),
|
||||
good_message.inner_mut(),
|
||||
Weight::from_parts(20, 20),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Unsupported));
|
||||
|
||||
@@ -155,7 +161,7 @@ fn allow_explicit_unpaid_should_work() {
|
||||
&Parent.into(),
|
||||
bad_message1.inner_mut(),
|
||||
Weight::from_parts(20, 20),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20))));
|
||||
|
||||
@@ -163,7 +169,7 @@ fn allow_explicit_unpaid_should_work() {
|
||||
&Parent.into(),
|
||||
bad_message2.inner_mut(),
|
||||
Weight::from_parts(20, 20),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20))));
|
||||
|
||||
@@ -171,7 +177,7 @@ fn allow_explicit_unpaid_should_work() {
|
||||
&Parent.into(),
|
||||
good_message.inner_mut(),
|
||||
Weight::from_parts(20, 20),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
|
||||
@@ -187,7 +193,7 @@ fn allow_explicit_unpaid_should_work() {
|
||||
&Parent.into(),
|
||||
message_with_different_weight_parts.inner_mut(),
|
||||
Weight::from_parts(20, 20),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20))));
|
||||
|
||||
@@ -195,7 +201,7 @@ fn allow_explicit_unpaid_should_work() {
|
||||
&Parent.into(),
|
||||
message_with_different_weight_parts.inner_mut(),
|
||||
Weight::from_parts(10, 10),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
}
|
||||
@@ -211,7 +217,7 @@ fn allow_paid_should_work() {
|
||||
&Parachain(1).into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(10, 10),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Unsupported));
|
||||
|
||||
@@ -226,7 +232,7 @@ fn allow_paid_should_work() {
|
||||
&Parent.into(),
|
||||
underpaying_message.inner_mut(),
|
||||
Weight::from_parts(30, 30),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(30, 30))));
|
||||
|
||||
@@ -241,7 +247,7 @@ fn allow_paid_should_work() {
|
||||
&Parachain(1).into(),
|
||||
paying_message.inner_mut(),
|
||||
Weight::from_parts(30, 30),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Unsupported));
|
||||
|
||||
@@ -249,7 +255,7 @@ fn allow_paid_should_work() {
|
||||
&Parent.into(),
|
||||
paying_message.inner_mut(),
|
||||
Weight::from_parts(30, 30),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
|
||||
@@ -264,7 +270,7 @@ fn allow_paid_should_work() {
|
||||
&Parent.into(),
|
||||
paying_message_with_different_weight_parts.inner_mut(),
|
||||
Weight::from_parts(20, 20),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20))));
|
||||
|
||||
@@ -272,7 +278,7 @@ fn allow_paid_should_work() {
|
||||
&Parent.into(),
|
||||
paying_message_with_different_weight_parts.inner_mut(),
|
||||
Weight::from_parts(10, 10),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Ok(()))
|
||||
}
|
||||
@@ -288,7 +294,7 @@ fn suspension_should_work() {
|
||||
&Parent.into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(10, 10),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Err(ProcessMessageError::Yield));
|
||||
|
||||
@@ -299,7 +305,7 @@ fn suspension_should_work() {
|
||||
&Parent.into(),
|
||||
message.inner_mut(),
|
||||
Weight::from_parts(10, 10),
|
||||
&mut Weight::zero(),
|
||||
&mut props(Weight::zero()),
|
||||
);
|
||||
assert_eq!(r, Ok(()));
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ parameter_types! {
|
||||
}
|
||||
type TheBridge =
|
||||
TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation>>;
|
||||
type Router = UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, UniversalLocation>;
|
||||
type Router =
|
||||
TestTopic<UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, UniversalLocation>>;
|
||||
|
||||
/// ```nocompile
|
||||
/// local | remote
|
||||
@@ -40,21 +41,26 @@ type Router = UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, Un
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get(), Parachain(1)).into();
|
||||
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
assert_eq!(
|
||||
take_received_remote_messages(),
|
||||
vec![(
|
||||
Here.into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1).into()),
|
||||
Trap(1),
|
||||
])
|
||||
)]
|
||||
);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get(), Parachain(1)).into();
|
||||
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
assert_eq!(
|
||||
take_received_remote_messages(),
|
||||
vec![(
|
||||
Here.into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1).into()),
|
||||
Trap(1),
|
||||
]
|
||||
)
|
||||
)]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// ```nocompile
|
||||
@@ -69,19 +75,24 @@ fn sending_to_bridged_chain_works() {
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_parachain_of_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
(Parent, Parachain(1000)).into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1).into()),
|
||||
Trap(1),
|
||||
]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
(Parent, Parachain(1000)).into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1).into()),
|
||||
Trap(1),
|
||||
],
|
||||
),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
});
|
||||
}
|
||||
|
||||
/// ```nocompile
|
||||
@@ -96,17 +107,22 @@ fn sending_to_parachain_of_bridged_chain_works() {
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_relay_chain_of_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get()).into();
|
||||
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parent.into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1).into()),
|
||||
Trap(1),
|
||||
]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get()).into();
|
||||
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parent.into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1).into()),
|
||||
Trap(1),
|
||||
],
|
||||
),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ parameter_types! {
|
||||
}
|
||||
type TheBridge =
|
||||
TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation>>;
|
||||
type Router = UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, UniversalLocation>;
|
||||
type Router =
|
||||
TestTopic<UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, UniversalLocation>>;
|
||||
|
||||
/// ```nocompile
|
||||
/// local | remote
|
||||
@@ -36,16 +37,20 @@ type Router = UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, Un
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(
|
||||
send_xcm::<Router>((Parent, Remote::get()).into(), msg).unwrap().1,
|
||||
(Here, 100).into()
|
||||
);
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
assert_eq!(
|
||||
take_received_remote_messages(),
|
||||
vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]
|
||||
);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(
|
||||
send_xcm::<Router>((Parent, Remote::get()).into(), msg).unwrap().1,
|
||||
(Here, 100).into()
|
||||
);
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Here.into(),
|
||||
xcm_with_topic([0; 32], vec![UniversalOrigin(Local::get().into()), Trap(1)]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
assert_eq!(RoutingLog::take(), vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
/// ```nocompile
|
||||
@@ -60,11 +65,16 @@ fn sending_to_bridged_chain_works() {
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_parachain_of_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected =
|
||||
vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parachain(1000).into(),
|
||||
xcm_with_topic([0; 32], vec![UniversalOrigin(Local::get().into()), Trap(1)]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
assert_eq!(RoutingLog::take(), vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
//! Tests specific to the bridging primitives
|
||||
|
||||
use super::mock::*;
|
||||
use crate::universal_exports::*;
|
||||
use crate::{universal_exports::*, WithTopicSource};
|
||||
use frame_support::{parameter_types, traits::Get};
|
||||
use std::{cell::RefCell, marker::PhantomData};
|
||||
use xcm_executor::{
|
||||
@@ -37,12 +37,73 @@ parameter_types! {
|
||||
pub Local: NetworkId = ByGenesis([0; 32]);
|
||||
pub Remote: NetworkId = ByGenesis([1; 32]);
|
||||
pub Price: MultiAssets = MultiAssets::from((Here, 100u128));
|
||||
pub static UsingTopic: bool = false;
|
||||
}
|
||||
|
||||
std::thread_local! {
|
||||
static BRIDGE_TRAFFIC: RefCell<Vec<Vec<u8>>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
fn maybe_with_topic(f: impl Fn()) {
|
||||
UsingTopic::set(false);
|
||||
f();
|
||||
UsingTopic::set(true);
|
||||
f();
|
||||
}
|
||||
|
||||
fn xcm_with_topic<T>(topic: XcmHash, mut xcm: Vec<Instruction<T>>) -> Xcm<T> {
|
||||
if UsingTopic::get() {
|
||||
xcm.push(SetTopic(topic));
|
||||
}
|
||||
Xcm(xcm)
|
||||
}
|
||||
|
||||
fn fake_id() -> XcmHash {
|
||||
[255; 32]
|
||||
}
|
||||
|
||||
fn test_weight(mut count: u64) -> Weight {
|
||||
if UsingTopic::get() {
|
||||
count += 1;
|
||||
}
|
||||
Weight::from_parts(count * 10, count * 10)
|
||||
}
|
||||
|
||||
fn maybe_forward_id_for(topic: &XcmHash) -> XcmHash {
|
||||
match UsingTopic::get() {
|
||||
true => forward_id_for(topic),
|
||||
false => fake_id(),
|
||||
}
|
||||
}
|
||||
|
||||
enum TestTicket<T: SendXcm> {
|
||||
Basic(T::Ticket),
|
||||
Topic(<WithTopicSource<T, ()> as SendXcm>::Ticket),
|
||||
}
|
||||
|
||||
struct TestTopic<R>(PhantomData<R>);
|
||||
impl<R: SendXcm> SendXcm for TestTopic<R> {
|
||||
type Ticket = TestTicket<R>;
|
||||
fn deliver(ticket: Self::Ticket) -> core::result::Result<XcmHash, SendError> {
|
||||
match ticket {
|
||||
TestTicket::Basic(t) => R::deliver(t),
|
||||
TestTicket::Topic(t) => WithTopicSource::<R, ()>::deliver(t),
|
||||
}
|
||||
}
|
||||
fn validate(
|
||||
destination: &mut Option<MultiLocation>,
|
||||
message: &mut Option<Xcm<()>>,
|
||||
) -> SendResult<Self::Ticket> {
|
||||
Ok(if UsingTopic::get() {
|
||||
let (t, a) = WithTopicSource::<R, ()>::validate(destination, message)?;
|
||||
(TestTicket::Topic(t), a)
|
||||
} else {
|
||||
let (t, a) = R::validate(destination, message)?;
|
||||
(TestTicket::Basic(t), a)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct TestBridge<D>(PhantomData<D>);
|
||||
impl<D: DispatchBlob> TestBridge<D> {
|
||||
fn service() -> u64 {
|
||||
@@ -71,7 +132,7 @@ impl SendXcm for TestRemoteIncomingRouter {
|
||||
Ok((pair, MultiAssets::new()))
|
||||
}
|
||||
fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<XcmHash, SendError> {
|
||||
let hash = fake_message_hash(&pair.1);
|
||||
let hash = fake_id();
|
||||
REMOTE_INCOMING_XCM.with(|q| q.borrow_mut().push(pair));
|
||||
Ok(hash)
|
||||
}
|
||||
@@ -107,6 +168,20 @@ fn deliver<RemoteExporter: ExportXcm>(
|
||||
export_xcm::<RemoteExporter>(n, c, s, d, m).map(|(hash, _)| hash)
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct LogEntry {
|
||||
local: Junctions,
|
||||
remote: Junctions,
|
||||
id: XcmHash,
|
||||
message: Xcm<()>,
|
||||
outcome: Outcome,
|
||||
paid: bool,
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static RoutingLog: Vec<LogEntry> = vec![];
|
||||
}
|
||||
|
||||
impl<Local: Get<Junctions>, Remote: Get<Junctions>, RemoteExporter: ExportXcm> SendXcm
|
||||
for UnpaidExecutingRouter<Local, Remote, RemoteExporter>
|
||||
{
|
||||
@@ -133,15 +208,20 @@ impl<Local: Get<Junctions>, Remote: Get<Junctions>, RemoteExporter: ExportXcm> S
|
||||
AllowUnpaidFrom::set(vec![origin.clone()]);
|
||||
set_exporter_override(price::<RemoteExporter>, deliver::<RemoteExporter>);
|
||||
// The we execute it:
|
||||
let hash = fake_message_hash(&message);
|
||||
let outcome = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
let mut id = fake_id();
|
||||
let outcome = XcmExecutor::<TestConfig>::prepare_and_execute(
|
||||
origin,
|
||||
message.into(),
|
||||
hash,
|
||||
message.clone().into(),
|
||||
&mut id,
|
||||
Weight::from_parts(2_000_000_000_000, 2_000_000_000_000),
|
||||
Weight::zero(),
|
||||
);
|
||||
let local = Local::get();
|
||||
let remote = Remote::get();
|
||||
let entry = LogEntry { local, remote, id, message, outcome: outcome.clone(), paid: false };
|
||||
RoutingLog::mutate(|l| l.push(entry));
|
||||
match outcome {
|
||||
Outcome::Complete(..) => Ok(hash),
|
||||
Outcome::Complete(..) => Ok(id),
|
||||
Outcome::Incomplete(..) => Err(Transport("Error executing")),
|
||||
Outcome::Error(..) => Err(Transport("Unable to execute")),
|
||||
}
|
||||
@@ -178,15 +258,20 @@ impl<Local: Get<Junctions>, Remote: Get<Junctions>, RemoteExporter: ExportXcm> S
|
||||
AllowPaidFrom::set(vec![origin.clone()]);
|
||||
set_exporter_override(price::<RemoteExporter>, deliver::<RemoteExporter>);
|
||||
// Then we execute it:
|
||||
let hash = fake_message_hash(&message);
|
||||
let outcome = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
let mut id = fake_id();
|
||||
let outcome = XcmExecutor::<TestConfig>::prepare_and_execute(
|
||||
origin,
|
||||
message.into(),
|
||||
hash,
|
||||
message.clone().into(),
|
||||
&mut id,
|
||||
Weight::from_parts(2_000_000_000_000, 2_000_000_000_000),
|
||||
Weight::zero(),
|
||||
);
|
||||
let local = Local::get();
|
||||
let remote = Remote::get();
|
||||
let entry = LogEntry { local, remote, id, message, outcome: outcome.clone(), paid: true };
|
||||
RoutingLog::mutate(|l| l.push(entry));
|
||||
match outcome {
|
||||
Outcome::Complete(..) => Ok(hash),
|
||||
Outcome::Complete(..) => Ok(id),
|
||||
Outcome::Incomplete(..) => Err(Transport("Error executing")),
|
||||
Outcome::Error(..) => Err(Transport("Unable to execute")),
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ parameter_types! {
|
||||
pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(100));
|
||||
pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get()));
|
||||
pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get()));
|
||||
pub static BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>
|
||||
= vec![(Remote::get(), MultiLocation::parent(), Some((Parent, 200u128).into()))];
|
||||
pub BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>
|
||||
= vec![(Remote::get(), MultiLocation::parent(), Some((Parent, 200u128 + if UsingTopic::get() { 20 } else { 0 }).into()))];
|
||||
// ^^^ 100 to use the bridge (export) and 100 for the remote execution weight (5 instructions
|
||||
// x (10 + 10) weight each).
|
||||
}
|
||||
@@ -41,7 +41,7 @@ type LocalBridgeRouter = SovereignPaidRemoteExporter<
|
||||
LocalInnerRouter,
|
||||
UniversalLocation,
|
||||
>;
|
||||
type LocalRouter = (LocalInnerRouter, LocalBridgeRouter);
|
||||
type LocalRouter = TestTopic<(LocalInnerRouter, LocalBridgeRouter)>;
|
||||
|
||||
/// ```nocompile
|
||||
/// local | remote
|
||||
@@ -55,33 +55,66 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter);
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_bridged_chain_works() {
|
||||
maybe_with_topic(|| {
|
||||
let dest: MultiLocation = (Parent, Parent, Remote::get()).into();
|
||||
|
||||
// Initialize the local relay so that our parachain has funds to pay for export.
|
||||
clear_assets(Parachain(100));
|
||||
add_asset(Parachain(100), (Here, 1000u128));
|
||||
|
||||
let price = 200u128 + if UsingTopic::get() { 20 } else { 0 };
|
||||
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, (Parent, price).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Here.into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(100).into()),
|
||||
Trap(1),
|
||||
],
|
||||
),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
|
||||
// The export cost 50 ref time and 50 proof size weight units (and thus 100 units of balance).
|
||||
assert_eq!(asset_list(Parachain(100)), vec![(Here, 1000u128 - price).into()]);
|
||||
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: RelayUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
WithdrawAsset(MultiAsset::from((Here, price)).into()),
|
||||
BuyExecution { fees: (Here, price).into(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Here,
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: Parachain(100).into() },
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(5)),
|
||||
paid: true,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn sending_to_bridged_chain_without_funds_fails() {
|
||||
let dest: MultiLocation = (Parent, Parent, Remote::get()).into();
|
||||
// Routing won't work if we don't have enough funds.
|
||||
assert_eq!(
|
||||
send_xcm::<LocalRouter>(dest.clone(), Xcm(vec![Trap(1)])),
|
||||
Err(SendError::Transport("Error executing")),
|
||||
);
|
||||
|
||||
// Initialize the local relay so that our parachain has funds to pay for export.
|
||||
add_asset(Parachain(100), (Here, 1000u128));
|
||||
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, (Parent, 200u128).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
assert_eq!(
|
||||
take_received_remote_messages(),
|
||||
vec![(
|
||||
Here.into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(100).into()),
|
||||
Trap(1),
|
||||
])
|
||||
)]
|
||||
);
|
||||
|
||||
// The export cost 50 ref time and 50 proof size weight units (and thus 100 units of balance).
|
||||
assert_eq!(asset_list(Parachain(100)), vec![(Here, 800u128).into()]);
|
||||
}
|
||||
|
||||
/// ```nocompile
|
||||
@@ -96,29 +129,64 @@ fn sending_to_bridged_chain_works() {
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_parachain_of_bridged_chain_works() {
|
||||
maybe_with_topic(|| {
|
||||
let dest: MultiLocation = (Parent, Parent, Remote::get(), Parachain(100)).into();
|
||||
|
||||
// Initialize the local relay so that our parachain has funds to pay for export.
|
||||
clear_assets(Parachain(100));
|
||||
add_asset(Parachain(100), (Here, 1000u128));
|
||||
|
||||
let price = 200u128 + if UsingTopic::get() { 20 } else { 0 };
|
||||
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, (Parent, price).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parachain(100).into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(100).into()),
|
||||
Trap(1),
|
||||
],
|
||||
),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
|
||||
// The export cost 50 ref time and 50 proof size weight units (and thus 100 units of balance).
|
||||
assert_eq!(asset_list(Parachain(100)), vec![(Here, 1000u128 - price).into()]);
|
||||
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: RelayUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
WithdrawAsset(MultiAsset::from((Here, price)).into()),
|
||||
BuyExecution { fees: (Here, price).into(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Parachain(100).into(),
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: Parachain(100).into() },
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(5)),
|
||||
paid: true,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn sending_to_parachain_of_bridged_chain_without_funds_fails() {
|
||||
let dest: MultiLocation = (Parent, Parent, Remote::get(), Parachain(100)).into();
|
||||
// Routing won't work if we don't have enough funds.
|
||||
assert_eq!(
|
||||
send_xcm::<LocalRouter>(dest.clone(), Xcm(vec![Trap(1)])),
|
||||
Err(SendError::Transport("Error executing")),
|
||||
);
|
||||
|
||||
// Initialize the local relay so that our parachain has funds to pay for export.
|
||||
add_asset(Parachain(100), (Here, 1000u128));
|
||||
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, (Parent, 200u128).into());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parachain(100).into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(100).into()),
|
||||
Trap(1),
|
||||
]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
|
||||
// The export cost 50 ref time and 50 proof size weight units (and thus 100 units of balance).
|
||||
assert_eq!(asset_list(Parachain(100)), vec![(Here, 800u128).into()]);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ type LocalInnerRouter =
|
||||
UnpaidExecutingRouter<UniversalLocation, ParaBridgeUniversalLocation, RelayExporter>;
|
||||
type LocalBridgingRouter =
|
||||
UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, LocalInnerRouter, UniversalLocation>;
|
||||
type LocalRouter = (LocalInnerRouter, LocalBridgingRouter);
|
||||
type LocalRouter = TestTopic<(LocalInnerRouter, LocalBridgingRouter)>;
|
||||
|
||||
/// ```nocompile
|
||||
/// local | remote
|
||||
@@ -48,25 +48,49 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter);
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(
|
||||
send_xcm::<LocalRouter>((Parent, Parent, Remote::get(), Parachain(1)).into(), msg)
|
||||
.unwrap()
|
||||
.1,
|
||||
MultiAssets::new()
|
||||
);
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
assert_eq!(
|
||||
take_received_remote_messages(),
|
||||
vec![(
|
||||
Here.into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1)
|
||||
])
|
||||
)]
|
||||
);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(
|
||||
send_xcm::<LocalRouter>((Parent, Parent, Remote::get(), Parachain(1)).into(), msg)
|
||||
.unwrap()
|
||||
.1,
|
||||
MultiAssets::new()
|
||||
);
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
assert_eq!(
|
||||
take_received_remote_messages(),
|
||||
vec![(
|
||||
Here.into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1)
|
||||
]
|
||||
)
|
||||
)]
|
||||
);
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: ParaBridgeUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Parachain(1).into(),
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(2)),
|
||||
paid: false,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
});
|
||||
}
|
||||
|
||||
/// ```nocompile
|
||||
@@ -81,19 +105,43 @@ fn sending_to_bridged_chain_works() {
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_sibling_of_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
(Parent, Parachain(1000)).into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1),
|
||||
]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
(Parent, Parachain(1000)).into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1),
|
||||
],
|
||||
),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: ParaBridgeUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Parachain(1000).into(),
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(2)),
|
||||
paid: false,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
});
|
||||
}
|
||||
|
||||
/// ```nocompile
|
||||
@@ -108,17 +156,41 @@ fn sending_to_sibling_of_bridged_chain_works() {
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_relay_of_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get()).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parent.into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1),
|
||||
]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get()).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parent.into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1),
|
||||
],
|
||||
),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: ParaBridgeUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Here,
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(2)),
|
||||
paid: false,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ type LocalInnerRouter =
|
||||
UnpaidExecutingRouter<UniversalLocation, ParaBridgeUniversalLocation, RelayExporter>;
|
||||
type LocalBridgingRouter =
|
||||
UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, LocalInnerRouter, UniversalLocation>;
|
||||
type LocalRouter = (LocalInnerRouter, LocalBridgingRouter);
|
||||
type LocalRouter = TestTopic<(LocalInnerRouter, LocalBridgingRouter)>;
|
||||
|
||||
/// ```nocompile
|
||||
/// local | remote
|
||||
@@ -48,18 +48,40 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter);
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(
|
||||
send_xcm::<LocalRouter>((Parent, Remote::get(), Parachain(1)).into(), msg)
|
||||
.unwrap()
|
||||
.1,
|
||||
MultiAssets::new()
|
||||
);
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
assert_eq!(
|
||||
take_received_remote_messages(),
|
||||
vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]
|
||||
);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(
|
||||
send_xcm::<LocalRouter>((Parent, Remote::get(), Parachain(1)).into(), msg)
|
||||
.unwrap()
|
||||
.1,
|
||||
MultiAssets::new()
|
||||
);
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Here.into(),
|
||||
xcm_with_topic([0; 32], vec![UniversalOrigin(Local::get().into()), Trap(1)]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: ParaBridgeUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Parachain(1).into(),
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(2)),
|
||||
paid: false,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
});
|
||||
}
|
||||
|
||||
/// ```nocompile
|
||||
@@ -74,15 +96,36 @@ fn sending_to_bridged_chain_works() {
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_sibling_of_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
(Parent, Parachain(1000)).into(),
|
||||
Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
(Parent, Parachain(1000)).into(),
|
||||
xcm_with_topic([0; 32], vec![UniversalOrigin(Local::get().into()), Trap(1)]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: ParaBridgeUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Parachain(1000).into(),
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(2)),
|
||||
paid: false,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
});
|
||||
}
|
||||
|
||||
/// ```nocompile
|
||||
@@ -97,10 +140,34 @@ fn sending_to_sibling_of_bridged_chain_works() {
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_relay_of_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Remote::get()).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(Parent.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Remote::get()).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parent.into(),
|
||||
xcm_with_topic([0; 32], vec![UniversalOrigin(Local::get().into()), Trap(1)]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: ParaBridgeUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Here,
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(2)),
|
||||
paid: false,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ type LocalInnerRouter =
|
||||
UnpaidExecutingRouter<UniversalLocation, RelayUniversalLocation, RelayExporter>;
|
||||
type LocalBridgeRouter =
|
||||
UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, LocalInnerRouter, UniversalLocation>;
|
||||
type LocalRouter = (LocalInnerRouter, LocalBridgeRouter);
|
||||
type LocalRouter = TestTopic<(LocalInnerRouter, LocalBridgeRouter)>;
|
||||
|
||||
/// ```nocompile
|
||||
/// local | remote
|
||||
@@ -48,23 +48,47 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter);
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(
|
||||
send_xcm::<LocalRouter>((Parent, Parent, Remote::get()).into(), msg).unwrap().1,
|
||||
MultiAssets::new()
|
||||
);
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
assert_eq!(
|
||||
take_received_remote_messages(),
|
||||
vec![(
|
||||
Here.into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1)
|
||||
])
|
||||
)]
|
||||
);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
assert_eq!(
|
||||
send_xcm::<LocalRouter>((Parent, Parent, Remote::get()).into(), msg).unwrap().1,
|
||||
MultiAssets::new()
|
||||
);
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
assert_eq!(
|
||||
take_received_remote_messages(),
|
||||
vec![(
|
||||
Here.into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1)
|
||||
]
|
||||
)
|
||||
)]
|
||||
);
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: RelayUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Here,
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(2)),
|
||||
paid: false,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
});
|
||||
}
|
||||
|
||||
/// ```nocompile
|
||||
@@ -79,17 +103,41 @@ fn sending_to_bridged_chain_works() {
|
||||
/// ```
|
||||
#[test]
|
||||
fn sending_to_parachain_of_bridged_chain_works() {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parachain(1000).into(),
|
||||
Xcm(vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1),
|
||||
]),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
maybe_with_topic(|| {
|
||||
let msg = Xcm(vec![Trap(1)]);
|
||||
let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into();
|
||||
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
|
||||
assert_eq!(TheBridge::service(), 1);
|
||||
let expected = vec![(
|
||||
Parachain(1000).into(),
|
||||
xcm_with_topic(
|
||||
[0; 32],
|
||||
vec![
|
||||
UniversalOrigin(Local::get().into()),
|
||||
DescendOrigin(Parachain(1000).into()),
|
||||
Trap(1),
|
||||
],
|
||||
),
|
||||
)];
|
||||
assert_eq!(take_received_remote_messages(), expected);
|
||||
let entry = LogEntry {
|
||||
local: UniversalLocation::get(),
|
||||
remote: RelayUniversalLocation::get(),
|
||||
id: maybe_forward_id_for(&[0; 32]),
|
||||
message: xcm_with_topic(
|
||||
maybe_forward_id_for(&[0; 32]),
|
||||
vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
ExportMessage {
|
||||
network: ByGenesis([1; 32]),
|
||||
destination: Parachain(1000).into(),
|
||||
xcm: xcm_with_topic([0; 32], vec![Trap(1)]),
|
||||
},
|
||||
],
|
||||
),
|
||||
outcome: Outcome::Complete(test_weight(2)),
|
||||
paid: false,
|
||||
};
|
||||
assert_eq!(RoutingLog::take(), vec![entry]);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
barriers::{AllowSubscriptionsFrom, RespectSuspension},
|
||||
barriers::{AllowSubscriptionsFrom, RespectSuspension, TrailingSetTopicAsId},
|
||||
test_utils::*,
|
||||
};
|
||||
pub use crate::{
|
||||
@@ -41,6 +41,7 @@ pub use sp_std::{
|
||||
marker::PhantomData,
|
||||
};
|
||||
pub use xcm::latest::{prelude::*, Weight};
|
||||
use xcm_executor::traits::Properties;
|
||||
pub use xcm_executor::{
|
||||
traits::{
|
||||
AssetExchange, AssetLock, CheckSuspension, ConvertOrigin, Enact, ExportXcm, FeeManager,
|
||||
@@ -241,6 +242,9 @@ pub fn asset_list(who: impl Into<MultiLocation>) -> Vec<MultiAsset> {
|
||||
pub fn add_asset(who: impl Into<MultiLocation>, what: impl Into<MultiAsset>) {
|
||||
ASSETS.with(|a| a.borrow_mut().entry(who.into()).or_insert(Assets::new()).subsume(what.into()));
|
||||
}
|
||||
pub fn clear_assets(who: impl Into<MultiLocation>) {
|
||||
ASSETS.with(|a| a.borrow_mut().remove(&who.into()));
|
||||
}
|
||||
|
||||
pub struct TestAssetTransactor;
|
||||
impl TransactAsset for TestAssetTransactor {
|
||||
@@ -429,7 +433,7 @@ impl CheckSuspension for TestSuspender {
|
||||
_origin: &MultiLocation,
|
||||
_instructions: &mut [Instruction<Call>],
|
||||
_max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
_properties: &mut Properties,
|
||||
) -> bool {
|
||||
SUSPENDED.with(|s| s.get())
|
||||
}
|
||||
@@ -651,7 +655,7 @@ impl Config for TestConfig {
|
||||
type IsReserve = TestIsReserve;
|
||||
type IsTeleporter = TestIsTeleporter;
|
||||
type UniversalLocation = ExecutorUniversalLocation;
|
||||
type Barrier = RespectSuspension<TestBarrier, TestSuspender>;
|
||||
type Barrier = TrailingSetTopicAsId<RespectSuspension<TestBarrier, TestSuspender>>;
|
||||
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall, MaxInstructions>;
|
||||
type Trader = FixedRateOfFungible<WeightPrice, ()>;
|
||||
type ResponseHandler = TestResponseHandler;
|
||||
|
||||
@@ -130,6 +130,10 @@ impl<T: Get<Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>>> ExporterFor
|
||||
}
|
||||
}
|
||||
|
||||
pub fn forward_id_for(original_id: &XcmHash) -> XcmHash {
|
||||
(b"forward_id_for", original_id).using_encoded(sp_io::hashing::blake2_256)
|
||||
}
|
||||
|
||||
/// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction
|
||||
/// and sends it to a destination known to be able to handle it.
|
||||
///
|
||||
@@ -139,6 +143,16 @@ impl<T: Get<Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>>> ExporterFor
|
||||
///
|
||||
/// This is only useful if we have special dispensation by the remote bridges to have the
|
||||
/// `ExportMessage` instruction executed without payment.
|
||||
///
|
||||
/// The `XcmHash` value returned by `deliver` will always be the same as that returned by the
|
||||
/// message exporter (`Bridges`). Generally this should take notice of the message should it
|
||||
/// end with the `SetTopic` instruction.
|
||||
///
|
||||
/// In the case that the message ends with a `SetTopic(T)` (as should be the case if the top-level
|
||||
/// router is `EnsureUniqueTopic`), then the forwarding message (i.e. the one carrying the
|
||||
/// export instruction *to* the bridge in local consensus) will also end with a `SetTopic` whose
|
||||
/// inner is `forward_id_for(T)`. If this is not the case then the onward message will not be given
|
||||
/// the `SetTopic` afterword.
|
||||
pub struct UnpaidRemoteExporter<Bridges, Router, UniversalLocation>(
|
||||
PhantomData<(Bridges, Router, UniversalLocation)>,
|
||||
);
|
||||
@@ -155,21 +169,32 @@ impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMulti
|
||||
let devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|_| NotApplicable)?;
|
||||
let (remote_network, remote_location) = devolved;
|
||||
let xcm = xcm.take().ok_or(MissingArgument)?;
|
||||
|
||||
let (bridge, maybe_payment) =
|
||||
Bridges::exporter_for(&remote_network, &remote_location, &xcm).ok_or(NotApplicable)?;
|
||||
ensure!(maybe_payment.is_none(), Unroutable);
|
||||
|
||||
// `xcm` should already end with `SetTopic` - if it does, then extract and derive into
|
||||
// an onward topic ID.
|
||||
let maybe_forward_id = match xcm.last() {
|
||||
Some(SetTopic(t)) => Some(forward_id_for(t)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// We then send a normal message to the bridge asking it to export the prepended
|
||||
// message to the remote chain. This will only work if the bridge will do the message
|
||||
// export for free. Common-good chains will typically be afforded this.
|
||||
let message = Xcm(vec![
|
||||
let mut message = Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
ExportMessage { network: remote_network, destination: remote_location, xcm },
|
||||
]);
|
||||
if let Some(forward_id) = maybe_forward_id {
|
||||
message.0.push(SetTopic(forward_id));
|
||||
}
|
||||
validate_send::<Router>(bridge, message)
|
||||
}
|
||||
|
||||
fn deliver(validation: Router::Ticket) -> Result<XcmHash, SendError> {
|
||||
fn deliver(validation: Self::Ticket) -> Result<XcmHash, SendError> {
|
||||
Router::deliver(validation)
|
||||
}
|
||||
}
|
||||
@@ -179,6 +204,16 @@ impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMulti
|
||||
///
|
||||
/// The `ExportMessage` instruction on the bridge is paid for from the local chain's sovereign
|
||||
/// account on the bridge. The amount paid is determined through the `ExporterFor` trait.
|
||||
///
|
||||
/// The `XcmHash` value returned by `deliver` will always be the same as that returned by the
|
||||
/// message exporter (`Bridges`). Generally this should take notice of the message should it
|
||||
/// end with the `SetTopic` instruction.
|
||||
///
|
||||
/// In the case that the message ends with a `SetTopic(T)` (as should be the case if the top-level
|
||||
/// router is `EnsureUniqueTopic`), then the forwarding message (i.e. the one carrying the
|
||||
/// export instruction *to* the bridge in local consensus) will also end with a `SetTopic` whose
|
||||
/// inner is `forward_id_for(T)`. If this is not the case then the onward message will not be given
|
||||
/// the `SetTopic` afterword.
|
||||
pub struct SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>(
|
||||
PhantomData<(Bridges, Router, UniversalLocation)>,
|
||||
);
|
||||
@@ -196,6 +231,14 @@ impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMulti
|
||||
let (remote_network, remote_location) = devolved;
|
||||
|
||||
let xcm = xcm.take().ok_or(MissingArgument)?;
|
||||
|
||||
// `xcm` should already end with `SetTopic` - if it does, then extract and derive into
|
||||
// an onward topic ID.
|
||||
let maybe_forward_id = match xcm.last() {
|
||||
Some(SetTopic(t)) => Some(forward_id_for(t)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let (bridge, maybe_payment) =
|
||||
Bridges::exporter_for(&remote_network, &remote_location, &xcm).ok_or(NotApplicable)?;
|
||||
|
||||
@@ -204,7 +247,7 @@ impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMulti
|
||||
let export_instruction =
|
||||
ExportMessage { network: remote_network, destination: remote_location, xcm };
|
||||
|
||||
let message = Xcm(if let Some(ref payment) = maybe_payment {
|
||||
let mut message = Xcm(if let Some(ref payment) = maybe_payment {
|
||||
let fees = payment
|
||||
.clone()
|
||||
.reanchored(&bridge, UniversalLocation::get())
|
||||
@@ -219,6 +262,9 @@ impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMulti
|
||||
} else {
|
||||
vec![export_instruction]
|
||||
});
|
||||
if let Some(forward_id) = maybe_forward_id {
|
||||
message.0.push(SetTopic(forward_id));
|
||||
}
|
||||
|
||||
// We then send a normal message to the bridge asking it to export the prepended
|
||||
// message to the remote chain.
|
||||
@@ -344,22 +390,24 @@ impl<Bridge: HaulBlob, BridgedNetwork: Get<NetworkId>, Price: Get<MultiAssets>>
|
||||
.ok_or(SendError::MissingArgument)?
|
||||
.split_global()
|
||||
.map_err(|()| SendError::Unroutable)?;
|
||||
let mut inner: Xcm<()> = vec![UniversalOrigin(GlobalConsensus(local_net))].into();
|
||||
let mut message = message.take().ok_or(SendError::MissingArgument)?;
|
||||
let maybe_id = match message.last() {
|
||||
Some(SetTopic(t)) => Some(*t),
|
||||
_ => None,
|
||||
};
|
||||
message.0.insert(0, UniversalOrigin(GlobalConsensus(local_net)));
|
||||
if local_sub != Here {
|
||||
inner.inner_mut().push(DescendOrigin(local_sub));
|
||||
message.0.insert(1, DescendOrigin(local_sub));
|
||||
}
|
||||
inner
|
||||
.inner_mut()
|
||||
.extend(message.take().ok_or(SendError::MissingArgument)?.into_iter());
|
||||
let message = VersionedXcm::from(inner);
|
||||
let hash = message.using_encoded(sp_io::hashing::blake2_256);
|
||||
let message = VersionedXcm::from(message);
|
||||
let id = maybe_id.unwrap_or_else(|| message.using_encoded(sp_io::hashing::blake2_256));
|
||||
let blob = BridgeMessage { universal_dest, message }.encode();
|
||||
Ok(((blob, hash), Price::get()))
|
||||
Ok(((blob, id), Price::get()))
|
||||
}
|
||||
|
||||
fn deliver((blob, hash): (Vec<u8>, XcmHash)) -> Result<XcmHash, SendError> {
|
||||
fn deliver((blob, id): (Vec<u8>, XcmHash)) -> Result<XcmHash, SendError> {
|
||||
Bridge::haul_blob(blob)?;
|
||||
Ok(hash)
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
mod mock;
|
||||
|
||||
use frame_support::weights::Weight;
|
||||
use mock::{
|
||||
fake_message_hash, kusama_like_with_balances, AccountId, Balance, Balances, BaseXcmWeight,
|
||||
System, XcmConfig, CENTS,
|
||||
|
||||
@@ -65,9 +65,9 @@ fn basic_buy_fees_message_executes() {
|
||||
client.state_at(block_hash).expect("state should exist").inspect_state(|| {
|
||||
assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
|
||||
r.event,
|
||||
polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted(
|
||||
Outcome::Complete(_)
|
||||
)),
|
||||
polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted {
|
||||
outcome: Outcome::Complete(_)
|
||||
}),
|
||||
)));
|
||||
});
|
||||
}
|
||||
@@ -118,9 +118,9 @@ fn transact_recursion_limit_works() {
|
||||
client.state_at(block_hash).expect("state should exist").inspect_state(|| {
|
||||
assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
|
||||
r.event,
|
||||
polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted(
|
||||
Outcome::Incomplete(_, XcmError::ExceedsStackLimit)
|
||||
)),
|
||||
polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted {
|
||||
outcome: Outcome::Incomplete(_, XcmError::ExceedsStackLimit)
|
||||
}),
|
||||
)));
|
||||
});
|
||||
}
|
||||
@@ -195,10 +195,10 @@ fn query_response_fires() {
|
||||
client.state_at(block_hash).expect("state should exist").inspect_state(|| {
|
||||
assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
|
||||
r.event,
|
||||
polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::ResponseReady(
|
||||
q,
|
||||
Response::ExecutionResult(None),
|
||||
)) if q == query_id,
|
||||
polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::ResponseReady {
|
||||
query_id: q,
|
||||
response: Response::ExecutionResult(None),
|
||||
}) if q == query_id,
|
||||
)));
|
||||
assert_eq!(
|
||||
polkadot_test_runtime::Xcm::query(query_id),
|
||||
|
||||
@@ -31,8 +31,8 @@ use xcm::latest::prelude::*;
|
||||
pub mod traits;
|
||||
use traits::{
|
||||
validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin,
|
||||
DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, ShouldExecute, TransactAsset,
|
||||
VersionChangeNotifier, WeightBounds, WeightTrader,
|
||||
DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, Properties, ShouldExecute,
|
||||
TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader,
|
||||
};
|
||||
|
||||
mod assets;
|
||||
@@ -193,8 +193,8 @@ impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Con
|
||||
fn execute(
|
||||
origin: impl Into<MultiLocation>,
|
||||
WeighedMessage(xcm_weight, mut message): WeighedMessage<Config::RuntimeCall>,
|
||||
message_hash: XcmHash,
|
||||
mut weight_credit: Weight,
|
||||
id: &mut XcmHash,
|
||||
weight_credit: Weight,
|
||||
) -> Outcome {
|
||||
let origin = origin.into();
|
||||
log::trace!(
|
||||
@@ -204,24 +204,27 @@ impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Con
|
||||
message,
|
||||
weight_credit,
|
||||
);
|
||||
let mut properties = Properties { weight_credit, message_id: None };
|
||||
if let Err(e) = Config::Barrier::should_execute(
|
||||
&origin,
|
||||
message.inner_mut(),
|
||||
xcm_weight,
|
||||
&mut weight_credit,
|
||||
&mut properties,
|
||||
) {
|
||||
log::trace!(
|
||||
target: "xcm::execute_xcm_in_credit",
|
||||
"Barrier blocked execution! Error: {:?}. (origin: {:?}, message: {:?}, weight_credit: {:?})",
|
||||
"Barrier blocked execution! Error: {:?}. (origin: {:?}, message: {:?}, properties: {:?})",
|
||||
e,
|
||||
origin,
|
||||
message,
|
||||
weight_credit,
|
||||
properties,
|
||||
);
|
||||
return Outcome::Error(XcmError::Barrier)
|
||||
}
|
||||
|
||||
let mut vm = Self::new(origin, message_hash);
|
||||
*id = properties.message_id.unwrap_or(*id);
|
||||
|
||||
let mut vm = Self::new(origin, *id);
|
||||
|
||||
while !message.0.is_empty() {
|
||||
let result = vm.process(message);
|
||||
@@ -272,12 +275,12 @@ impl From<ExecutorError> for frame_benchmarking::BenchmarkError {
|
||||
}
|
||||
|
||||
impl<Config: config::Config> XcmExecutor<Config> {
|
||||
pub fn new(origin: impl Into<MultiLocation>, message_hash: XcmHash) -> Self {
|
||||
pub fn new(origin: impl Into<MultiLocation>, message_id: XcmHash) -> Self {
|
||||
let origin = origin.into();
|
||||
Self {
|
||||
holding: Assets::new(),
|
||||
holding_limit: Config::MaxAssetsIntoHolding::get() as usize,
|
||||
context: XcmContext { origin: Some(origin), message_hash, topic: None },
|
||||
context: XcmContext { origin: Some(origin), message_id, topic: None },
|
||||
original_origin: origin,
|
||||
trader: Config::Trader::new(),
|
||||
error: None,
|
||||
|
||||
@@ -40,7 +40,7 @@ pub use token_matching::{
|
||||
mod on_response;
|
||||
pub use on_response::{OnResponse, VersionChangeNotifier};
|
||||
mod should_execute;
|
||||
pub use should_execute::{CheckSuspension, ShouldExecute};
|
||||
pub use should_execute::{CheckSuspension, Properties, ShouldExecute};
|
||||
mod transact_asset;
|
||||
pub use transact_asset::TransactAsset;
|
||||
mod weight;
|
||||
|
||||
@@ -16,7 +16,19 @@
|
||||
|
||||
use frame_support::traits::ProcessMessageError;
|
||||
use sp_std::result::Result;
|
||||
use xcm::latest::{Instruction, MultiLocation, Weight};
|
||||
use xcm::latest::{Instruction, MultiLocation, Weight, XcmHash};
|
||||
|
||||
/// Properyies of an XCM message and its imminent execution.
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct Properties {
|
||||
/// The amount of weight that the system has determined this
|
||||
/// message may utilize in its execution. Typically non-zero only because of prior fee
|
||||
/// payment, but could in principle be due to other factors.
|
||||
pub weight_credit: Weight,
|
||||
/// The identity of the message, if one is known. If left as `None`, then it will generally
|
||||
/// default to the hash of the message which may be non-unique.
|
||||
pub message_id: Option<XcmHash>,
|
||||
}
|
||||
|
||||
/// Trait to determine whether the execution engine should actually execute a given XCM.
|
||||
///
|
||||
@@ -28,14 +40,13 @@ pub trait ShouldExecute {
|
||||
/// - `origin`: The origin (sender) of the message.
|
||||
/// - `instructions`: The message itself.
|
||||
/// - `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
|
||||
/// message may utilize in its execution. Typically non-zero only because of prior fee
|
||||
/// payment, but could in principle be due to other factors.
|
||||
/// - `properties`: Various pre-established properties of the message which may be mutated by
|
||||
/// this API.
|
||||
fn should_execute<RuntimeCall>(
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<RuntimeCall>],
|
||||
max_weight: Weight,
|
||||
weight_credit: &mut Weight,
|
||||
properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError>;
|
||||
}
|
||||
|
||||
@@ -45,21 +56,21 @@ impl ShouldExecute for Tuple {
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<RuntimeCall>],
|
||||
max_weight: Weight,
|
||||
weight_credit: &mut Weight,
|
||||
properties: &mut Properties,
|
||||
) -> Result<(), ProcessMessageError> {
|
||||
for_tuples!( #(
|
||||
match Tuple::should_execute(origin, instructions, max_weight, weight_credit) {
|
||||
match Tuple::should_execute(origin, instructions, max_weight, properties) {
|
||||
Ok(()) => return Ok(()),
|
||||
_ => (),
|
||||
}
|
||||
)* );
|
||||
log::trace!(
|
||||
target: "xcm::should_execute",
|
||||
"did not pass barrier: origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
|
||||
"did not pass barrier: origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
|
||||
origin,
|
||||
instructions,
|
||||
max_weight,
|
||||
weight_credit,
|
||||
properties,
|
||||
);
|
||||
Err(ProcessMessageError::Unsupported)
|
||||
}
|
||||
@@ -79,7 +90,7 @@ pub trait CheckSuspension {
|
||||
origin: &MultiLocation,
|
||||
instructions: &mut [Instruction<Call>],
|
||||
max_weight: Weight,
|
||||
weight_credit: &mut Weight,
|
||||
properties: &mut Properties,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
@@ -89,10 +100,10 @@ impl CheckSuspension for Tuple {
|
||||
origin: &MultiLocation,
|
||||
instruction: &mut [Instruction<Call>],
|
||||
max_weight: Weight,
|
||||
weight_credit: &mut Weight,
|
||||
properties: &mut Properties,
|
||||
) -> bool {
|
||||
for_tuples!( #(
|
||||
if Tuple::is_suspended(origin, instruction, max_weight, weight_credit) {
|
||||
if Tuple::is_suspended(origin, instruction, max_weight, properties) {
|
||||
return true
|
||||
}
|
||||
)* );
|
||||
|
||||
@@ -397,7 +397,7 @@ mod tests {
|
||||
MultiTransactor::deposit_asset(
|
||||
&(Here, 1u128).into(),
|
||||
&Here.into(),
|
||||
&XcmContext::with_message_hash([0; 32]),
|
||||
&XcmContext::with_message_id([0; 32]),
|
||||
),
|
||||
Err(XcmError::AssetNotFound)
|
||||
);
|
||||
@@ -411,7 +411,7 @@ mod tests {
|
||||
MultiTransactor::deposit_asset(
|
||||
&(Here, 1u128).into(),
|
||||
&Here.into(),
|
||||
&XcmContext::with_message_hash([0; 32]),
|
||||
&XcmContext::with_message_id([0; 32]),
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
@@ -425,7 +425,7 @@ mod tests {
|
||||
MultiTransactor::deposit_asset(
|
||||
&(Here, 1u128).into(),
|
||||
&Here.into(),
|
||||
&XcmContext::with_message_hash([0; 32]),
|
||||
&XcmContext::with_message_id([0; 32]),
|
||||
),
|
||||
Err(XcmError::Overflow)
|
||||
);
|
||||
@@ -439,7 +439,7 @@ mod tests {
|
||||
MultiTransactor::deposit_asset(
|
||||
&(Here, 1u128).into(),
|
||||
&Here.into(),
|
||||
&XcmContext::with_message_hash([0; 32]),
|
||||
&XcmContext::with_message_id([0; 32]),
|
||||
),
|
||||
Ok(()),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user