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:
Gavin Wood
2023-05-25 07:57:32 +01:00
committed by GitHub
parent 28de4f1337
commit 85dfadff2c
29 changed files with 1305 additions and 552 deletions
+6 -6
View File
@@ -41,8 +41,8 @@ use xcm_builder::{
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, IsChildSystemParachain, IsConcrete, CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, IsChildSystemParachain, IsConcrete,
MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents,
WithComputedOrigin, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
}; };
use xcm_executor::traits::WithOriginFilter; 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 /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
/// individual routers. /// individual routers.
pub type XcmRouter = ( pub type XcmRouter = WithUniqueTopic<(
// Only one router so far - use DMP to communicate with child parachains. // Only one router so far - use DMP to communicate with child parachains.
ChildParachainRouter< ChildParachainRouter<
Runtime, Runtime,
XcmPallet, XcmPallet,
ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>, ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
>, >,
); )>;
parameter_types! { parameter_types! {
pub const Ksm: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); 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. /// 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. // Weight that is paid for may be consumed.
TakeWeightCredit, TakeWeightCredit,
// Expected responses are OK. // Expected responses are OK.
@@ -159,7 +159,7 @@ pub type Barrier = (
UniversalLocation, UniversalLocation,
ConstU32<8>, ConstU32<8>,
>, >,
); )>;
/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly /// A call filter for the XCM Transact instruction. This is a temporary measure until we properly
/// account for proof size weights. /// account for proof size weights.
+6 -5
View File
@@ -40,7 +40,8 @@ use xcm_builder::{
ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, IsChildSystemParachain, IsConcrete, CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, IsChildSystemParachain, IsConcrete,
MintLocation, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, MintLocation, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin,
WithUniqueTopic,
}; };
use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; 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 /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
/// individual routers. /// individual routers.
pub type XcmRouter = ( pub type XcmRouter = WithUniqueTopic<(
// Only one router so far - use DMP to communicate with child parachains. // Only one router so far - use DMP to communicate with child parachains.
ChildParachainRouter< ChildParachainRouter<
Runtime, Runtime,
XcmPallet, XcmPallet,
ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>, ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
>, >,
); )>;
parameter_types! { parameter_types! {
pub const Roc: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); 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. /// 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. // Weight that is paid for may be consumed.
TakeWeightCredit, TakeWeightCredit,
// Expected responses are OK. // Expected responses are OK.
@@ -154,7 +155,7 @@ pub type Barrier = (
UniversalLocation, UniversalLocation,
ConstU32<8>, ConstU32<8>,
>, >,
); )>;
/// A call filter for the XCM Transact instruction. This is a temporary measure until we /// A call filter for the XCM Transact instruction. This is a temporary measure until we
/// properly account for proof size weights. /// properly account for proof size weights.
+5 -5
View File
@@ -39,7 +39,7 @@ use xcm_builder::{
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
CurrencyAdapter as XcmCurrencyAdapter, IsChildSystemParachain, IsConcrete, MintLocation, CurrencyAdapter as XcmCurrencyAdapter, IsChildSystemParachain, IsConcrete, MintLocation,
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
UsingComponents, WeightInfoBounds, WithComputedOrigin, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
}; };
use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; 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 /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
/// individual routers. /// individual routers.
pub type XcmRouter = ( pub type XcmRouter = WithUniqueTopic<(
// Only one router so far - use DMP to communicate with child parachains. // Only one router so far - use DMP to communicate with child parachains.
ChildParachainRouter< ChildParachainRouter<
Runtime, Runtime,
XcmPallet, XcmPallet,
ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>, ExponentialPrice<FeeAssetId, BaseDeliveryFee, TransactionByteFee, Dmp>,
>, >,
); )>;
parameter_types! { parameter_types! {
pub const Westmint: MultiLocation = Parachain(1000).into_location(); pub const Westmint: MultiLocation = Parachain(1000).into_location();
@@ -108,7 +108,7 @@ pub type TrustedTeleporters =
(xcm_builder::Case<WndForWestmint>, xcm_builder::Case<WndForCollectives>); (xcm_builder::Case<WndForWestmint>, xcm_builder::Case<WndForCollectives>);
/// The barriers one of which must be passed for an XCM message to be executed. /// 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. // Weight that is paid for may be consumed.
TakeWeightCredit, TakeWeightCredit,
// Expected responses are OK. // Expected responses are OK.
@@ -125,7 +125,7 @@ pub type Barrier = (
UniversalLocation, UniversalLocation,
ConstU32<8>, ConstU32<8>,
>, >,
); )>;
/// A call filter for the XCM Transact instruction. This is a temporary measure until we /// A call filter for the XCM Transact instruction. This is a temporary measure until we
/// properly account for proof size weights. /// properly account for proof size weights.
@@ -49,7 +49,7 @@ benchmarks_instance_pallet! {
&sender_location, &sender_location,
&XcmContext { &XcmContext {
origin: Some(sender_location.clone()), origin: Some(sender_location.clone()),
message_hash: [0; 32], message_id: [0; 32],
topic: None, topic: None,
}, },
).unwrap(); ).unwrap();
@@ -82,7 +82,7 @@ benchmarks_instance_pallet! {
&sender_location, &sender_location,
&XcmContext { &XcmContext {
origin: Some(sender_location.clone()), origin: Some(sender_location.clone()),
message_hash: [0; 32], message_id: [0; 32],
topic: None, topic: None,
}, },
).unwrap(); ).unwrap();
@@ -109,7 +109,7 @@ benchmarks_instance_pallet! {
&sender_location, &sender_location,
&XcmContext { &XcmContext {
origin: Some(sender_location.clone()), origin: Some(sender_location.clone()),
message_hash: [0; 32], message_id: [0; 32],
topic: None, topic: None,
}, },
).unwrap(); ).unwrap();
@@ -209,7 +209,7 @@ benchmarks! {
assets.clone().into(), assets.clone().into(),
&XcmContext { &XcmContext {
origin: Some(origin.clone()), origin: Some(origin.clone()),
message_hash: [0; 32], message_id: [0; 32],
topic: None, topic: None,
}, },
); );
@@ -266,7 +266,7 @@ benchmarks! {
max_response_weight, max_response_weight,
&XcmContext { &XcmContext {
origin: Some(origin.clone()), origin: Some(origin.clone()),
message_hash: [0; 32], message_id: [0; 32],
topic: None, topic: None,
}, },
).map_err(|_| "Could not start subscription")?; ).map_err(|_| "Could not start subscription")?;
+152 -124
View File
@@ -40,7 +40,7 @@ use sp_runtime::{
}; };
use sp_std::{boxed::Box, marker::PhantomData, prelude::*, result::Result, vec}; use sp_std::{boxed::Box, marker::PhantomData, prelude::*, result::Result, vec};
use xcm::{latest::QueryResponseInfo, prelude::*}; use xcm::{latest::QueryResponseInfo, prelude::*};
use xcm_executor::traits::{Convert, ConvertOrigin}; use xcm_executor::traits::{Convert, ConvertOrigin, Properties};
use frame_support::{ use frame_support::{
dispatch::{Dispatchable, GetDispatchInfo}, dispatch::{Dispatchable, GetDispatchInfo},
@@ -272,52 +272,49 @@ pub mod pallet {
#[pallet::generate_deposit(pub(super) fn deposit_event)] #[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> { pub enum Event<T: Config> {
/// Execution of an XCM message was attempted. /// Execution of an XCM message was attempted.
/// Attempted { outcome: xcm::latest::Outcome },
/// \[ outcome \]
Attempted(xcm::latest::Outcome),
/// A XCM message was sent. /// A XCM message was sent.
/// Sent {
/// \[ origin, destination, message \] origin: MultiLocation,
Sent(MultiLocation, MultiLocation, Xcm<()>), destination: MultiLocation,
message: Xcm<()>,
message_id: XcmHash,
},
/// Query response received which does not match a registered query. This may be because a /// 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 /// matching query was never registered, it may be because it is a duplicate response, or
/// because the query timed out. /// because the query timed out.
/// UnexpectedResponse { origin: MultiLocation, query_id: QueryId },
/// \[ origin location, id \]
UnexpectedResponse(MultiLocation, QueryId),
/// Query response has been received and is ready for taking with `take_response`. There is /// Query response has been received and is ready for taking with `take_response`. There is
/// no registered notification call. /// no registered notification call.
/// ResponseReady { query_id: QueryId, response: Response },
/// \[ id, response \]
ResponseReady(QueryId, Response),
/// Query response has been received and query is removed. The registered notification has /// Query response has been received and query is removed. The registered notification has
/// been dispatched and executed successfully. /// been dispatched and executed successfully.
/// Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
/// \[ id, pallet index, call index \]
Notified(QueryId, u8, u8),
/// Query response has been received and query is removed. The registered notification could /// 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 /// not be dispatched because the dispatch weight is greater than the maximum weight
/// originally budgeted by this runtime for the query result. /// originally budgeted by this runtime for the query result.
/// NotifyOverweight {
/// \[ id, pallet index, call index, actual weight, max budgeted weight \] query_id: QueryId,
NotifyOverweight(QueryId, u8, u8, Weight, Weight), 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 /// Query response has been received and query is removed. There was a general error with
/// dispatching the notification call. /// dispatching the notification call.
/// NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
/// \[ id, pallet index, call index \]
NotifyDispatchError(QueryId, u8, u8),
/// Query response has been received and query is removed. The dispatch was unable to be /// 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 /// decoded into a `Call`; this might be due to dispatch function having a signature which
/// is not `(origin, QueryId, Response)`. /// is not `(origin, QueryId, Response)`.
/// NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
/// \[ id, pallet index, call index \]
NotifyDecodeFailed(QueryId, u8, u8),
/// Expected query response has been received but the origin location of the response does /// 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 /// not match that expected. The query remains registered for a later, valid, response to
/// be received and acted upon. /// be received and acted upon.
/// InvalidResponder {
/// \[ origin location, id, expected location \] origin: MultiLocation,
InvalidResponder(MultiLocation, QueryId, Option<MultiLocation>), query_id: QueryId,
expected_location: Option<MultiLocation>,
},
/// Expected query response has been received but the expected origin location placed in /// Expected query response has been received but the expected origin location placed in
/// storage by this runtime previously cannot be decoded. The query remains registered. /// 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 /// 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 /// valid response will be dropped. Manual governance intervention is probably going to be
/// needed. /// needed.
/// InvalidResponderVersion { origin: MultiLocation, query_id: QueryId },
/// \[ origin location, id \]
InvalidResponderVersion(MultiLocation, QueryId),
/// Received query response has been read and removed. /// Received query response has been read and removed.
/// ResponseTaken { query_id: QueryId },
/// \[ id \]
ResponseTaken(QueryId),
/// Some assets have been placed in an asset trap. /// Some assets have been placed in an asset trap.
/// AssetsTrapped { hash: H256, origin: MultiLocation, assets: VersionedMultiAssets },
/// \[ hash, origin, assets \]
AssetsTrapped(H256, MultiLocation, VersionedMultiAssets),
/// An XCM version change notification message has been attempted to be sent. /// An XCM version change notification message has been attempted to be sent.
/// ///
/// The cost of sending it (borne by the chain) is included. /// The cost of sending it (borne by the chain) is included.
/// VersionChangeNotified {
/// \[ destination, result, cost \] destination: MultiLocation,
VersionChangeNotified(MultiLocation, XcmVersion, MultiAssets), result: XcmVersion,
cost: MultiAssets,
message_id: XcmHash,
},
/// The supported version of a location has been changed. This might be through an /// The supported version of a location has been changed. This might be through an
/// automatic notification or a manual intervention. /// automatic notification or a manual intervention.
/// SupportedVersionChanged { location: MultiLocation, version: XcmVersion },
/// \[ location, XCM version \]
SupportedVersionChanged(MultiLocation, XcmVersion),
/// A given location which had a version change subscription was dropped owing to an error /// A given location which had a version change subscription was dropped owing to an error
/// sending the notification to it. /// sending the notification to it.
/// NotifyTargetSendFail { location: MultiLocation, query_id: QueryId, error: XcmError },
/// \[ location, query ID, error \]
NotifyTargetSendFail(MultiLocation, QueryId, XcmError),
/// A given location which had a version change subscription was dropped owing to an error /// A given location which had a version change subscription was dropped owing to an error
/// migrating the location to our new XCM format. /// migrating the location to our new XCM format.
/// NotifyTargetMigrationFail { location: VersionedMultiLocation, query_id: QueryId },
/// \[ location, query ID \]
NotifyTargetMigrationFail(VersionedMultiLocation, QueryId),
/// Expected query response has been received but the expected querier location placed in /// 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. /// 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 /// 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 /// valid response will be dropped. Manual governance intervention is probably going to be
/// needed. /// needed.
/// InvalidQuerierVersion { origin: MultiLocation, query_id: QueryId },
/// \[ origin location, id \]
InvalidQuerierVersion(MultiLocation, QueryId),
/// Expected query response has been received but the querier location of the response does /// 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 /// not match the expected. The query remains registered for a later, valid, response to
/// be received and acted upon. /// be received and acted upon.
/// InvalidQuerier {
/// \[ origin location, id, expected querier, maybe actual querier \] origin: MultiLocation,
InvalidQuerier(MultiLocation, QueryId, MultiLocation, Option<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 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. /// A version information message is sent to them and its cost is included.
/// VersionNotifyStarted { destination: MultiLocation, cost: MultiAssets, message_id: XcmHash },
/// \[ destination location, cost \] /// We have requested that a remote chain send us XCM version change notifications.
VersionNotifyStarted(MultiLocation, MultiAssets), VersionNotifyRequested {
/// We have requested that a remote chain sends us XCM version change notifications. destination: MultiLocation,
/// cost: MultiAssets,
/// \[ destination location, cost \] message_id: XcmHash,
VersionNotifyRequested(MultiLocation, MultiAssets), },
/// We have requested that a remote chain stops sending us XCM version change notifications. /// We have requested that a remote chain stops sending us XCM version change notifications.
/// VersionNotifyUnrequested {
/// \[ destination location, cost \] destination: MultiLocation,
VersionNotifyUnrequested(MultiLocation, MultiAssets), cost: MultiAssets,
message_id: XcmHash,
},
/// Fees were paid from a location for an operation (often for using `SendXcm`). /// Fees were paid from a location for an operation (often for using `SendXcm`).
/// FeesPaid { paying: MultiLocation, fees: MultiAssets },
/// \[ paying location, fees \]
FeesPaid(MultiLocation, MultiAssets),
/// Some assets have been claimed from an asset trap /// Some assets have been claimed from an asset trap
/// AssetsClaimed { hash: H256, origin: MultiLocation, assets: VersionedMultiAssets },
/// \[ hash, origin, assets \]
AssetsClaimed(H256, MultiLocation, VersionedMultiAssets),
} }
#[pallet::origin] #[pallet::origin]
@@ -793,8 +780,10 @@ pub mod pallet {
let dest = MultiLocation::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?; let dest = MultiLocation::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
let message: Xcm<()> = (*message).try_into().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)?; let message_id =
Self::deposit_event(Event::Sent(origin_location, dest, message)); 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(()) Ok(())
} }
@@ -927,7 +916,7 @@ pub mod pallet {
); );
let result = let result =
Ok(Some(outcome.weight_used().saturating_add(T::WeightInfo::execute())).into()); Ok(Some(outcome.weight_used().saturating_add(T::WeightInfo::execute())).into());
Self::deposit_event(Event::Attempted(outcome)); Self::deposit_event(Event::Attempted { outcome });
result result
} }
@@ -942,16 +931,16 @@ pub mod pallet {
pub fn force_xcm_version( pub fn force_xcm_version(
origin: OriginFor<T>, origin: OriginFor<T>,
location: Box<MultiLocation>, location: Box<MultiLocation>,
xcm_version: XcmVersion, version: XcmVersion,
) -> DispatchResult { ) -> DispatchResult {
T::AdminOrigin::ensure_origin(origin)?; T::AdminOrigin::ensure_origin(origin)?;
let location = *location; let location = *location;
SupportedVersion::<T>::insert( SupportedVersion::<T>::insert(
XCM_VERSION, XCM_VERSION,
LatestVersionedMultiLocation(&location), LatestVersionedMultiLocation(&location),
xcm_version, version,
); );
Self::deposit_event(Event::SupportedVersionChanged(location, xcm_version)); Self::deposit_event(Event::SupportedVersionChanged { location, version });
Ok(()) Ok(())
} }
@@ -1195,7 +1184,7 @@ impl<T: Config> Pallet<T> {
let hash = message.using_encoded(sp_io::hashing::blake2_256); let hash = message.using_encoded(sp_io::hashing::blake2_256);
let outcome = let outcome =
T::XcmExecutor::execute_xcm_in_credit(origin_location, message, hash, weight, weight); 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(()) Ok(())
} }
@@ -1256,7 +1245,7 @@ impl<T: Config> Pallet<T> {
let hash = message.using_encoded(sp_io::hashing::blake2_256); let hash = message.using_encoded(sp_io::hashing::blake2_256);
let outcome = let outcome =
T::XcmExecutor::execute_xcm_in_credit(origin_location, message, hash, weight, weight); 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(()) Ok(())
} }
@@ -1331,14 +1320,19 @@ impl<T: Config> Pallet<T> {
let message = let message =
Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]); Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
let event = match send_xcm::<T::XcmRouter>(new_key, message) { 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); let value = (query_id, max_weight, xcm_version);
VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value); 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) => { Err(e) => {
VersionNotifyTargets::<T>::remove(XCM_VERSION, key); 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); Self::deposit_event(event);
@@ -1357,7 +1351,10 @@ impl<T: Config> Pallet<T> {
let new_key = match MultiLocation::try_from(old_key.clone()) { let new_key = match MultiLocation::try_from(old_key.clone()) {
Ok(k) => k, Ok(k) => k,
Err(()) => { 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); weight_used.saturating_accrue(vnt_migrate_fail_weight);
if weight_used.any_gte(weight_cutoff) { if weight_used.any_gte(weight_cutoff) {
return (weight_used, Some(stage)) return (weight_used, Some(stage))
@@ -1380,15 +1377,24 @@ impl<T: Config> Pallet<T> {
querier: None, querier: None,
}]); }]);
let event = match send_xcm::<T::XcmRouter>(new_key, message) { let event = match send_xcm::<T::XcmRouter>(new_key, message) {
Ok((_hash, cost)) => { Ok((message_id, cost)) => {
VersionNotifyTargets::<T>::insert( VersionNotifyTargets::<T>::insert(
XCM_VERSION, XCM_VERSION,
versioned_key, versioned_key,
(query_id, max_weight, xcm_version), (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); Self::deposit_event(event);
weight_used.saturating_accrue(vnt_notify_migrate_weight); weight_used.saturating_accrue(vnt_notify_migrate_weight);
@@ -1415,8 +1421,8 @@ impl<T: Config> Pallet<T> {
}); });
// TODO #3735: Correct weight. // TODO #3735: Correct weight.
let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() }; let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
let (_hash, cost) = send_xcm::<T::XcmRouter>(dest, Xcm(vec![instruction]))?; let (message_id, cost) = send_xcm::<T::XcmRouter>(dest, Xcm(vec![instruction]))?;
Self::deposit_event(Event::VersionNotifyRequested(dest, cost)); Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id); VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
let query_status = let query_status =
QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false }; QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
@@ -1430,8 +1436,12 @@ impl<T: Config> Pallet<T> {
let versioned_dest = LatestVersionedMultiLocation(&dest); let versioned_dest = LatestVersionedMultiLocation(&dest);
let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest) let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
.ok_or(XcmError::InvalidLocation)?; .ok_or(XcmError::InvalidLocation)?;
let (_hash, cost) = send_xcm::<T::XcmRouter>(dest, Xcm(vec![UnsubscribeVersion]))?; let (message_id, cost) = send_xcm::<T::XcmRouter>(dest, Xcm(vec![UnsubscribeVersion]))?;
Self::deposit_event(Event::VersionNotifyUnrequested(dest, cost)); Self::deposit_event(Event::VersionNotifyUnrequested {
destination: dest,
cost,
message_id,
});
Queries::<T>::remove(query_id); Queries::<T>::remove(query_id);
Ok(()) Ok(())
} }
@@ -1589,7 +1599,7 @@ impl<T: Config> Pallet<T> {
if let Some(QueryStatus::Ready { response, at }) = Queries::<T>::get(query_id) { if let Some(QueryStatus::Ready { response, at }) = Queries::<T>::get(query_id) {
let response = response.try_into().ok()?; let response = response.try_into().ok()?;
Queries::<T>::remove(query_id); Queries::<T>::remove(query_id);
Self::deposit_event(Event::ResponseTaken(query_id)); Self::deposit_event(Event::ResponseTaken { query_id });
Some((response, at)) Some((response, at))
} else { } else {
None None
@@ -1623,7 +1633,7 @@ impl<T: Config> Pallet<T> {
fn charge_fees(location: MultiLocation, assets: MultiAssets) -> DispatchResult { fn charge_fees(location: MultiLocation, assets: MultiAssets) -> DispatchResult {
T::XcmExecutor::charge_fees(location, assets.clone()) T::XcmExecutor::charge_fees(location, assets.clone())
.map_err(|_| Error::<T>::FeesNotMet)?; .map_err(|_| Error::<T>::FeesNotMet)?;
Self::deposit_event(Event::FeesPaid(location, assets)); Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
Ok(()) Ok(())
} }
} }
@@ -1861,8 +1871,12 @@ impl<T: Config> VersionChangeNotifier for Pallet<T> {
let xcm_version = T::AdvertisedXcmVersion::get(); let xcm_version = T::AdvertisedXcmVersion::get();
let response = Response::Version(xcm_version); let response = Response::Version(xcm_version);
let instruction = QueryResponse { query_id, response, max_weight, querier: None }; let instruction = QueryResponse { query_id, response, max_weight, querier: None };
let (_hash, cost) = send_xcm::<T::XcmRouter>(*dest, Xcm(vec![instruction]))?; let (message_id, cost) = send_xcm::<T::XcmRouter>(*dest, Xcm(vec![instruction]))?;
Self::deposit_event(Event::<T>::VersionNotifyStarted(*dest, cost)); Self::deposit_event(Event::<T>::VersionNotifyStarted {
destination: *dest,
cost,
message_id,
});
let value = (query_id, max_weight, xcm_version); let value = (query_id, max_weight, xcm_version);
VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value); 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 versioned = VersionedMultiAssets::from(MultiAssets::from(assets));
let hash = BlakeTwo256::hash_of(&(&origin, &versioned)); let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
AssetTraps::<T>::mutate(hash, |n| *n += 1); AssetTraps::<T>::mutate(hash, |n| *n += 1);
Self::deposit_event(Event::AssetsTrapped(hash, *origin, versioned)); Self::deposit_event(Event::AssetsTrapped { hash, origin: *origin, assets: versioned });
// TODO #3735: Put the real weight in there. // TODO #3735: Put the real weight in there.
Weight::zero() Weight::zero()
} }
@@ -1920,7 +1934,7 @@ impl<T: Config> ClaimAssets for Pallet<T> {
1 => AssetTraps::<T>::remove(hash), 1 => AssetTraps::<T>::remove(hash),
n => AssetTraps::<T>::insert(hash, n - 1), 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 return true
} }
} }
@@ -1953,19 +1967,28 @@ impl<T: Config> OnResponse for Pallet<T> {
max_weight: Weight, max_weight: Weight,
_context: &XcmContext, _context: &XcmContext,
) -> Weight { ) -> Weight {
let origin = *origin;
match (response, Queries::<T>::get(query_id)) { match (response, Queries::<T>::get(query_id)) {
( (
Response::Version(v), Response::Version(v),
Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }), Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
) => { ) => {
let origin: MultiLocation = match expected_origin.try_into() { let origin: MultiLocation = match expected_origin.try_into() {
Ok(o) if &o == origin => o, Ok(o) if o == origin => o,
Ok(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() 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. // TODO #3735: Correct weight for this.
return Weight::zero() return Weight::zero()
}, },
@@ -1983,7 +2006,10 @@ impl<T: Config> OnResponse for Pallet<T> {
LatestVersionedMultiLocation(&origin), LatestVersionedMultiLocation(&origin),
v, v,
); );
Self::deposit_event(Event::SupportedVersionChanged(origin, v)); Self::deposit_event(Event::SupportedVersionChanged {
location: origin,
version: v,
});
Weight::zero() Weight::zero()
}, },
( (
@@ -1994,33 +2020,33 @@ impl<T: Config> OnResponse for Pallet<T> {
let match_querier = match MultiLocation::try_from(match_querier) { let match_querier = match MultiLocation::try_from(match_querier) {
Ok(mq) => mq, Ok(mq) => mq,
Err(_) => { Err(_) => {
Self::deposit_event(Event::InvalidQuerierVersion(*origin, query_id)); Self::deposit_event(Event::InvalidQuerierVersion { origin, query_id });
return Weight::zero() return Weight::zero()
}, },
}; };
if querier.map_or(true, |q| q != &match_querier) { if querier.map_or(true, |q| q != &match_querier) {
Self::deposit_event(Event::InvalidQuerier( Self::deposit_event(Event::InvalidQuerier {
*origin, origin,
query_id, query_id,
match_querier, expected_querier: match_querier,
querier.cloned(), maybe_actual_querier: querier.cloned(),
)); });
return Weight::zero() return Weight::zero()
} }
} }
let responder = match MultiLocation::try_from(responder) { let responder = match MultiLocation::try_from(responder) {
Ok(r) => r, Ok(r) => r,
Err(_) => { Err(_) => {
Self::deposit_event(Event::InvalidResponderVersion(*origin, query_id)); Self::deposit_event(Event::InvalidResponderVersion { origin, query_id });
return Weight::zero() return Weight::zero()
}, },
}; };
if origin != &responder { if origin != responder {
Self::deposit_event(Event::InvalidResponder( Self::deposit_event(Event::InvalidResponder {
*origin, origin,
query_id, query_id,
Some(responder), expected_location: Some(responder),
)); });
return Weight::zero() return Weight::zero()
} }
return match maybe_notify { return match maybe_notify {
@@ -2035,29 +2061,29 @@ impl<T: Config> OnResponse for Pallet<T> {
Queries::<T>::remove(query_id); Queries::<T>::remove(query_id);
let weight = call.get_dispatch_info().weight; let weight = call.get_dispatch_info().weight;
if weight.any_gt(max_weight) { if weight.any_gt(max_weight) {
let e = Event::NotifyOverweight( let e = Event::NotifyOverweight {
query_id, query_id,
pallet_index, pallet_index,
call_index, call_index,
weight, actual_weight: weight,
max_weight, max_budgeted_weight: max_weight,
); };
Self::deposit_event(e); Self::deposit_event(e);
return Weight::zero() return Weight::zero()
} }
let dispatch_origin = Origin::Response(*origin).into(); let dispatch_origin = Origin::Response(origin).into();
match call.dispatch(dispatch_origin) { match call.dispatch(dispatch_origin) {
Ok(post_info) => { 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); Self::deposit_event(e);
post_info.actual_weight post_info.actual_weight
}, },
Err(error_and_info) => { Err(error_and_info) => {
let e = Event::NotifyDispatchError( let e = Event::NotifyDispatchError {
query_id, query_id,
pallet_index, pallet_index,
call_index, call_index,
); };
Self::deposit_event(e); Self::deposit_event(e);
// Not much to do with the result as it is. It's up to the parachain to ensure that the // Not much to do with the result as it is. It's up to the parachain to ensure that the
// message makes sense. // message makes sense.
@@ -2066,13 +2092,14 @@ impl<T: Config> OnResponse for Pallet<T> {
} }
.unwrap_or(weight) .unwrap_or(weight)
} else { } 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); Self::deposit_event(e);
Weight::zero() Weight::zero()
} }
}, },
None => { None => {
let e = Event::ResponseReady(query_id, response.clone()); let e = Event::ResponseReady { query_id, response: response.clone() };
Self::deposit_event(e); Self::deposit_event(e);
let at = frame_system::Pallet::<T>::current_block_number(); let at = frame_system::Pallet::<T>::current_block_number();
let response = response.into(); 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() Weight::zero()
}, },
} }
@@ -2094,7 +2122,7 @@ impl<T: Config> CheckSuspension for Pallet<T> {
_origin: &MultiLocation, _origin: &MultiLocation,
_instructions: &mut [Instruction<Call>], _instructions: &mut [Instruction<Call>],
_max_weight: Weight, _max_weight: Weight,
_weight_credit: &mut Weight, _properties: &mut Properties,
) -> bool { ) -> bool {
XcmExecutionSuspended::<T>::get() XcmExecutionSuspended::<T>::get()
} }
+60 -52
View File
@@ -27,7 +27,10 @@ use polkadot_parachain::primitives::Id as ParaId;
use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, Hash}; use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, Hash};
use xcm::{latest::QueryResponseInfo, prelude::*}; use xcm::{latest::QueryResponseInfo, prelude::*};
use xcm_builder::AllowKnownQueryResponses; 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 ALICE: AccountId = AccountId::new([0u8; 32]);
const BOB: AccountId = AccountId::new([1u8; 32]); const BOB: AccountId = AccountId::new([1u8; 32]);
@@ -101,7 +104,11 @@ fn report_outcome_notify_works() {
0, 0,
Response::ExecutionResult(None), 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![]); 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!(r, Outcome::Complete(Weight::from_parts(1_000, 1_000)));
assert_eq!( assert_eq!(
last_event(), last_event(),
RuntimeEvent::XcmPallet(crate::Event::ResponseReady( RuntimeEvent::XcmPallet(crate::Event::ResponseReady {
0, query_id: 0,
Response::ExecutionResult(None), response: Response::ExecutionResult(None),
)) })
); );
let response = Some((Response::ExecutionResult(None), 1)); 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!(r, Outcome::Complete(Weight::from_parts(1_000, 1_000)));
assert_eq!( assert_eq!(
last_event(), last_event(),
RuntimeEvent::XcmPallet(crate::Event::InvalidQuerier( RuntimeEvent::XcmPallet(crate::Event::InvalidQuerier {
AccountId32 { network: None, id: ALICE.into() }.into(), origin: AccountId32 { network: None, id: ALICE.into() }.into(),
0, query_id: 0,
querier.clone(), expected_querier: querier.clone(),
None, maybe_actual_querier: None,
)), }),
); );
// Supplying the wrong querier will also fail // 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!(r, Outcome::Complete(Weight::from_parts(1_000, 1_000)));
assert_eq!( assert_eq!(
last_event(), last_event(),
RuntimeEvent::XcmPallet(crate::Event::InvalidQuerier( RuntimeEvent::XcmPallet(crate::Event::InvalidQuerier {
AccountId32 { network: None, id: ALICE.into() }.into(), origin: AccountId32 { network: None, id: ALICE.into() }.into(),
0, query_id: 0,
querier.clone(), expected_querier: querier.clone(),
Some(MultiLocation::here()), maybe_actual_querier: Some(MultiLocation::here()),
)), }),
); );
// Multiple failures should not have changed the query state // 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!(r, Outcome::Complete(Weight::from_parts(1_000, 1_000)));
assert_eq!( assert_eq!(
last_event(), last_event(),
RuntimeEvent::XcmPallet(crate::Event::ResponseReady( RuntimeEvent::XcmPallet(crate::Event::ResponseReady {
0, query_id: 0,
Response::ExecutionResult(None), response: Response::ExecutionResult(None),
)) })
); );
let response = Some((Response::ExecutionResult(None), 1)); let response = Some((Response::ExecutionResult(None), 1));
@@ -285,6 +292,7 @@ fn send_works() {
buy_execution((Parent, SEND_AMOUNT)), buy_execution((Parent, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() }, DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
]); ]);
let versioned_dest = Box::new(RelayLocation::get().into()); let versioned_dest = Box::new(RelayLocation::get().into());
let versioned_message = Box::new(VersionedXcm::from(message.clone())); let versioned_message = Box::new(VersionedXcm::from(message.clone()));
assert_ok!(XcmPallet::send( assert_ok!(XcmPallet::send(
@@ -292,19 +300,20 @@ fn send_works() {
versioned_dest, versioned_dest,
versioned_message versioned_message
)); ));
assert_eq!( let sent_message = Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap()))
sent_xcm(), .into_iter()
vec![( .chain(message.0.clone().into_iter())
Here.into(), .collect());
Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap())) let id = fake_message_hash(&sent_message);
.into_iter() assert_eq!(sent_xcm(), vec![(Here.into(), sent_message)]);
.chain(message.0.clone().into_iter())
.collect())
)],
);
assert_eq!( assert_eq!(
last_event(), 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(); let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!( assert_eq!(
last_event(), 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(); let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!( assert_eq!(
last_event(), 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!( assert_eq!(
last_event(), 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(); let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!( assert_eq!(
last_event(), 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(); let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!( assert_eq!(
last_event(), 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!( assert_eq!(
last_event(), 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!(Balances::total_balance(&BOB), SEND_AMOUNT);
assert_eq!( assert_eq!(
last_event(), 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!( assert_eq!(
last_events(2), last_events(2),
vec![ vec![
RuntimeEvent::XcmPallet(crate::Event::AssetsTrapped(hash.clone(), source, vma)), RuntimeEvent::XcmPallet(crate::Event::AssetsTrapped {
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Complete( hash: hash.clone(),
BaseXcmWeight::get() * 5 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); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
@@ -707,13 +720,8 @@ fn trapped_assets_can_be_claimed() {
]))), ]))),
weight weight
)); ));
assert_eq!( let outcome = Outcome::Incomplete(BaseXcmWeight::get(), XcmError::UnknownClaim);
last_event(), assert_eq!(last_event(), RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome }));
RuntimeEvent::XcmPallet(crate::Event::Attempted(Outcome::Incomplete(
BaseXcmWeight::get(),
XcmError::UnknownClaim
)))
);
}); });
} }
@@ -768,7 +776,7 @@ fn basic_subscription_works() {
&remote, &remote,
message.inner_mut(), message.inner_mut(),
weight, weight,
&mut Weight::zero(), &mut Properties { weight_credit: Weight::zero(), message_id: None },
)); ));
}); });
} }
+17 -9
View File
@@ -184,7 +184,7 @@ pub mod prelude {
NetworkId::{self, *}, NetworkId::{self, *},
OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId, OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId,
QueryResponseInfo, Response, Result as XcmResult, SendError, SendResult, SendXcm, QueryResponseInfo, Response, Result as XcmResult, SendError, SendResult, SendXcm,
Unwrappable, Unwrappable, Weight,
WeightLimit::{self, *}, WeightLimit::{self, *},
WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
WildMultiAsset::{self, *}, WildMultiAsset::{self, *},
@@ -337,19 +337,27 @@ impl TryFrom<OldWeightLimit> for WeightLimit {
/// Contextual data pertaining to a specific list of XCM instructions. /// Contextual data pertaining to a specific list of XCM instructions.
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)] #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
pub struct XcmContext { pub struct XcmContext {
/// The `MultiLocation` origin of the corresponding XCM. /// The current value of the Origin register of the XCVM.
pub origin: Option<MultiLocation>, pub origin: Option<MultiLocation>,
/// The hash of the XCM. /// The identity of the XCM; this may be a hash of its versioned encoding but could also be
pub message_hash: XcmHash, /// a high-level identity set by an appropriate barrier.
/// The topic of the XCM. pub message_id: XcmHash,
/// The current value of the Topic register of the XCVM.
pub topic: Option<[u8; 32]>, pub topic: Option<[u8; 32]>,
} }
impl XcmContext { 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. /// topic unset.
pub fn with_message_hash(message_hash: XcmHash) -> XcmContext { #[deprecated = "Use `with_message_id` instead."]
XcmContext { origin: None, message_hash, topic: None } 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 }, UnlockAsset { asset: MultiAsset, target: MultiLocation },
/// Asset (`asset`) has been locked on the `origin` system and may not be transferred. It may /// 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. /// - `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 /// - `owner`: The owner of the asset on the chain in which it was locked. This may be a
+85 -10
View File
@@ -212,6 +212,52 @@ impl From<SendError> for Error {
pub type Result = result::Result<(), 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. /// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
pub enum Outcome { pub enum Outcome {
@@ -259,13 +305,34 @@ pub trait ExecuteXcm<Call> {
fn execute( fn execute(
origin: impl Into<MultiLocation>, origin: impl Into<MultiLocation>,
pre: Self::Prepared, pre: Self::Prepared,
hash: XcmHash, id: &mut XcmHash,
weight_credit: Weight, weight_credit: Weight,
) -> Outcome; ) -> 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. /// Execute some XCM `message` with the message `hash` from `origin` using no more than
/// The weight limit is a basic hard-limit and the implementation may place further restrictions or requirements /// `weight_limit` weight.
/// on weight and other aspects. ///
/// 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( fn execute_xcm(
origin: impl Into<MultiLocation>, origin: impl Into<MultiLocation>,
message: Xcm<Call>, message: Xcm<Call>,
@@ -283,14 +350,17 @@ pub trait ExecuteXcm<Call> {
Self::execute_xcm_in_credit(origin, message, hash, weight_limit, Weight::zero()) 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 /// Some amount of `weight_credit` may be provided which, depending on the implementation, may
/// execution without associated payment. /// allow execution without associated payment.
// TODO: XCMv4
// #[deprecated = "Use `prepare_and_execute` instead"]
fn execute_xcm_in_credit( fn execute_xcm_in_credit(
origin: impl Into<MultiLocation>, origin: impl Into<MultiLocation>,
message: Xcm<Call>, message: Xcm<Call>,
hash: XcmHash, mut hash: XcmHash,
weight_limit: Weight, weight_limit: Weight,
weight_credit: Weight, weight_credit: Weight,
) -> Outcome { ) -> Outcome {
@@ -302,7 +372,7 @@ pub trait ExecuteXcm<Call> {
if xcm_weight.any_gt(weight_limit) { if xcm_weight.any_gt(weight_limit) {
return Outcome::Error(Error::WeightLimitReached(xcm_weight)) 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 /// 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>> { fn prepare(message: Xcm<C>) -> result::Result<Self::Prepared, Xcm<C>> {
Err(message) Err(message)
} }
fn execute(_: impl Into<MultiLocation>, _: Self::Prepared, _: XcmHash, _: Weight) -> Outcome { fn execute(
_: impl Into<MultiLocation>,
_: Self::Prepared,
_: &mut XcmHash,
_: Weight,
) -> Outcome {
unreachable!() unreachable!()
} }
fn charge_fees(_location: impl Into<MultiLocation>, _fees: MultiAssets) -> Result { fn charge_fees(_location: impl Into<MultiLocation>, _fees: MultiAssets) -> Result {
+125 -35
View File
@@ -23,16 +23,10 @@ use frame_support::{
}; };
use polkadot_parachain::primitives::IsSystem; use polkadot_parachain::primitives::IsSystem;
use sp_std::{cell::Cell, marker::PhantomData, ops::ControlFlow, result::Result}; use sp_std::{cell::Cell, marker::PhantomData, ops::ControlFlow, result::Result};
use xcm::latest::{ use xcm::prelude::*;
Instruction::{self, *}, use xcm_executor::traits::{CheckSuspension, OnResponse, Properties, ShouldExecute};
InteriorMultiLocation, Junction, Junctions,
Junctions::X1,
MultiLocation, Weight,
WeightLimit::*,
};
use xcm_executor::traits::{CheckSuspension, OnResponse, 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. /// Useful to allow XCM execution by local chain users via extrinsics.
/// E.g. `pallet_xcm::reserve_asset_transfer` to transfer a reserve asset /// E.g. `pallet_xcm::reserve_asset_transfer` to transfer a reserve asset
@@ -43,14 +37,15 @@ impl ShouldExecute for TakeWeightCredit {
_origin: &MultiLocation, _origin: &MultiLocation,
_instructions: &mut [Instruction<RuntimeCall>], _instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight, max_weight: Weight,
weight_credit: &mut Weight, properties: &mut Properties,
) -> Result<(), ProcessMessageError> { ) -> Result<(), ProcessMessageError> {
log::trace!( log::trace!(
target: "xcm::barriers", target: "xcm::barriers",
"TakeWeightCredit origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", "TakeWeightCredit origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
_origin, _instructions, max_weight, weight_credit, _origin, _instructions, max_weight, properties,
); );
*weight_credit = weight_credit properties.weight_credit = properties
.weight_credit
.checked_sub(&max_weight) .checked_sub(&max_weight)
.ok_or(ProcessMessageError::Overweight(max_weight))?; .ok_or(ProcessMessageError::Overweight(max_weight))?;
Ok(()) Ok(())
@@ -68,12 +63,12 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFro
origin: &MultiLocation, origin: &MultiLocation,
instructions: &mut [Instruction<RuntimeCall>], instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight, max_weight: Weight,
_weight_credit: &mut Weight, _properties: &mut Properties,
) -> Result<(), ProcessMessageError> { ) -> Result<(), ProcessMessageError> {
log::trace!( log::trace!(
target: "xcm::barriers", target: "xcm::barriers",
"AllowTopLevelPaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", "AllowTopLevelPaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, max_weight, _weight_credit, origin, instructions, max_weight, _properties,
); );
ensure!(T::contains(origin), ProcessMessageError::Unsupported); ensure!(T::contains(origin), ProcessMessageError::Unsupported);
@@ -166,12 +161,12 @@ impl<
origin: &MultiLocation, origin: &MultiLocation,
instructions: &mut [Instruction<Call>], instructions: &mut [Instruction<Call>],
max_weight: Weight, max_weight: Weight,
weight_credit: &mut Weight, properties: &mut Properties,
) -> Result<(), ProcessMessageError> { ) -> Result<(), ProcessMessageError> {
log::trace!( log::trace!(
target: "xcm::barriers", target: "xcm::barriers",
"WithComputedOrigin origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", "WithComputedOrigin origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, max_weight, weight_credit, origin, instructions, max_weight, properties,
); );
let mut actual_origin = *origin; let mut actual_origin = *origin;
let skipped = Cell::new(0usize); let skipped = Cell::new(0usize);
@@ -205,11 +200,37 @@ impl<
&actual_origin, &actual_origin,
&mut instructions[skipped.get()..], &mut instructions[skipped.get()..],
max_weight, 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 /// Barrier condition that allows for a `SuspensionChecker` that controls whether or not the XCM
/// executor will be suspended from executing the given XCM. /// executor will be suspended from executing the given XCM.
pub struct RespectSuspension<Inner, SuspensionChecker>(PhantomData<(Inner, SuspensionChecker)>); pub struct RespectSuspension<Inner, SuspensionChecker>(PhantomData<(Inner, SuspensionChecker)>);
@@ -222,12 +243,12 @@ where
origin: &MultiLocation, origin: &MultiLocation,
instructions: &mut [Instruction<Call>], instructions: &mut [Instruction<Call>],
max_weight: Weight, max_weight: Weight,
weight_credit: &mut Weight, properties: &mut Properties,
) -> Result<(), ProcessMessageError> { ) -> Result<(), ProcessMessageError> {
if SuspensionChecker::is_suspended(origin, instructions, max_weight, weight_credit) { if SuspensionChecker::is_suspended(origin, instructions, max_weight, properties) {
Err(ProcessMessageError::Yield) Err(ProcessMessageError::Yield)
} else { } 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, origin: &MultiLocation,
instructions: &mut [Instruction<RuntimeCall>], instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight, _max_weight: Weight,
_weight_credit: &mut Weight, _properties: &mut Properties,
) -> Result<(), ProcessMessageError> { ) -> Result<(), ProcessMessageError> {
log::trace!( log::trace!(
target: "xcm::barriers", target: "xcm::barriers",
"AllowUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", "AllowUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, _max_weight, _weight_credit, origin, instructions, _max_weight, _properties,
); );
ensure!(T::contains(origin), ProcessMessageError::Unsupported); ensure!(T::contains(origin), ProcessMessageError::Unsupported);
Ok(()) Ok(())
@@ -264,12 +285,12 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowExplicitUnpaidExecutionF
origin: &MultiLocation, origin: &MultiLocation,
instructions: &mut [Instruction<Call>], instructions: &mut [Instruction<Call>],
max_weight: Weight, max_weight: Weight,
_weight_credit: &mut Weight, _properties: &mut Properties,
) -> Result<(), ProcessMessageError> { ) -> Result<(), ProcessMessageError> {
log::trace!( log::trace!(
target: "xcm::barriers", target: "xcm::barriers",
"AllowExplicitUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", "AllowExplicitUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, max_weight, _weight_credit, origin, instructions, max_weight, _properties,
); );
ensure!(T::contains(origin), ProcessMessageError::Unsupported); ensure!(T::contains(origin), ProcessMessageError::Unsupported);
instructions.matcher().match_next_inst(|inst| match inst { instructions.matcher().match_next_inst(|inst| match inst {
@@ -300,12 +321,12 @@ impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<Res
origin: &MultiLocation, origin: &MultiLocation,
instructions: &mut [Instruction<RuntimeCall>], instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight, _max_weight: Weight,
_weight_credit: &mut Weight, _properties: &mut Properties,
) -> Result<(), ProcessMessageError> { ) -> Result<(), ProcessMessageError> {
log::trace!( log::trace!(
target: "xcm::barriers", target: "xcm::barriers",
"AllowKnownQueryResponses origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", "AllowKnownQueryResponses origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, _max_weight, _weight_credit, origin, instructions, _max_weight, _properties,
); );
instructions instructions
.matcher() .matcher()
@@ -328,12 +349,12 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowSubscriptionsFrom<T> {
origin: &MultiLocation, origin: &MultiLocation,
instructions: &mut [Instruction<RuntimeCall>], instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight, _max_weight: Weight,
_weight_credit: &mut Weight, _properties: &mut Properties,
) -> Result<(), ProcessMessageError> { ) -> Result<(), ProcessMessageError> {
log::trace!( log::trace!(
target: "xcm::barriers", target: "xcm::barriers",
"AllowSubscriptionsFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", "AllowSubscriptionsFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, _max_weight, _weight_credit, origin, instructions, _max_weight, _properties,
); );
ensure!(T::contains(origin), ProcessMessageError::Unsupported); ensure!(T::contains(origin), ProcessMessageError::Unsupported);
instructions instructions
@@ -346,3 +367,72 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowSubscriptionsFrom<T> {
Ok(()) 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(())
}
}
+6 -2
View File
@@ -50,8 +50,9 @@ pub use asset_conversion::{ConvertedAbstractAssetId, ConvertedConcreteAssetId};
mod barriers; mod barriers;
pub use barriers::{ pub use barriers::{
AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, IsChildSystemParachain, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, DenyReserveTransferToRelayChain,
RespectSuspension, TakeWeightCredit, WithComputedOrigin, DenyThenTry, IsChildSystemParachain, RespectSuspension, TakeWeightCredit, TrailingSetTopicAsId,
WithComputedOrigin,
}; };
mod process_xcm_message; mod process_xcm_message;
@@ -85,6 +86,9 @@ pub use matcher::{CreateMatcher, MatchXcm, Matcher};
mod filter_asset_location; mod filter_asset_location;
pub use filter_asset_location::{Case, NativeAsset}; pub use filter_asset_location::{Case, NativeAsset};
mod routing;
pub use routing::{WithTopicSource, WithUniqueTopic};
mod universal_exports; mod universal_exports;
pub use universal_exports::{ pub use universal_exports::{
ensure_is_remote, BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError, ensure_is_remote, BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError,
@@ -53,7 +53,7 @@ impl<
let required = pre.weight_of(); let required = pre.weight_of();
ensure!(meter.can_accrue(required), ProcessMessageError::Overweight(required)); 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::Complete(w) => (w, Ok(true)),
Outcome::Incomplete(w, _) => (w, Ok(false)), Outcome::Incomplete(w, _) => (w, Ok(false)),
+102
View File
@@ -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)
}
}
+30 -24
View File
@@ -14,30 +14,36 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use xcm_executor::traits::Properties;
use super::*; use super::*;
fn props(weight_credit: Weight) -> Properties {
Properties { weight_credit, message_id: None }
}
#[test] #[test]
fn take_weight_credit_barrier_should_work() { fn take_weight_credit_barrier_should_work() {
let mut message = let mut message =
Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]); Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]);
let mut weight_credit = Weight::from_parts(10, 10); let mut properties = props(Weight::from_parts(10, 10));
let r = TakeWeightCredit::should_execute( let r = TakeWeightCredit::should_execute(
&Parent.into(), &Parent.into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(10, 10), Weight::from_parts(10, 10),
&mut weight_credit, &mut properties,
); );
assert_eq!(r, Ok(())); assert_eq!(r, Ok(()));
assert_eq!(weight_credit, Weight::zero()); assert_eq!(properties.weight_credit, Weight::zero());
let r = TakeWeightCredit::should_execute( let r = TakeWeightCredit::should_execute(
&Parent.into(), &Parent.into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(10, 10), Weight::from_parts(10, 10),
&mut weight_credit, &mut properties,
); );
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(10, 10)))); 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] #[test]
@@ -67,7 +73,7 @@ fn computed_origin_should_work() {
&Parent.into(), &Parent.into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(100, 100), Weight::from_parts(100, 100),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Unsupported)); assert_eq!(r, Err(ProcessMessageError::Unsupported));
@@ -79,7 +85,7 @@ fn computed_origin_should_work() {
&Parent.into(), &Parent.into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(100, 100), Weight::from_parts(100, 100),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Unsupported)); assert_eq!(r, Err(ProcessMessageError::Unsupported));
@@ -91,7 +97,7 @@ fn computed_origin_should_work() {
&Parent.into(), &Parent.into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(100, 100), Weight::from_parts(100, 100),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Ok(())); assert_eq!(r, Ok(()));
} }
@@ -107,7 +113,7 @@ fn allow_unpaid_should_work() {
&Parachain(1).into(), &Parachain(1).into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(10, 10), Weight::from_parts(10, 10),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Unsupported)); assert_eq!(r, Err(ProcessMessageError::Unsupported));
@@ -115,7 +121,7 @@ fn allow_unpaid_should_work() {
&Parent.into(), &Parent.into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(10, 10), Weight::from_parts(10, 10),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Ok(())); assert_eq!(r, Ok(()));
} }
@@ -147,7 +153,7 @@ fn allow_explicit_unpaid_should_work() {
&Parachain(1).into(), &Parachain(1).into(),
good_message.inner_mut(), good_message.inner_mut(),
Weight::from_parts(20, 20), Weight::from_parts(20, 20),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Unsupported)); assert_eq!(r, Err(ProcessMessageError::Unsupported));
@@ -155,7 +161,7 @@ fn allow_explicit_unpaid_should_work() {
&Parent.into(), &Parent.into(),
bad_message1.inner_mut(), bad_message1.inner_mut(),
Weight::from_parts(20, 20), Weight::from_parts(20, 20),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20)))); assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20))));
@@ -163,7 +169,7 @@ fn allow_explicit_unpaid_should_work() {
&Parent.into(), &Parent.into(),
bad_message2.inner_mut(), bad_message2.inner_mut(),
Weight::from_parts(20, 20), Weight::from_parts(20, 20),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20)))); assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20))));
@@ -171,7 +177,7 @@ fn allow_explicit_unpaid_should_work() {
&Parent.into(), &Parent.into(),
good_message.inner_mut(), good_message.inner_mut(),
Weight::from_parts(20, 20), Weight::from_parts(20, 20),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Ok(())); assert_eq!(r, Ok(()));
@@ -187,7 +193,7 @@ fn allow_explicit_unpaid_should_work() {
&Parent.into(), &Parent.into(),
message_with_different_weight_parts.inner_mut(), message_with_different_weight_parts.inner_mut(),
Weight::from_parts(20, 20), Weight::from_parts(20, 20),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20)))); assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20))));
@@ -195,7 +201,7 @@ fn allow_explicit_unpaid_should_work() {
&Parent.into(), &Parent.into(),
message_with_different_weight_parts.inner_mut(), message_with_different_weight_parts.inner_mut(),
Weight::from_parts(10, 10), Weight::from_parts(10, 10),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Ok(())); assert_eq!(r, Ok(()));
} }
@@ -211,7 +217,7 @@ fn allow_paid_should_work() {
&Parachain(1).into(), &Parachain(1).into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(10, 10), Weight::from_parts(10, 10),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Unsupported)); assert_eq!(r, Err(ProcessMessageError::Unsupported));
@@ -226,7 +232,7 @@ fn allow_paid_should_work() {
&Parent.into(), &Parent.into(),
underpaying_message.inner_mut(), underpaying_message.inner_mut(),
Weight::from_parts(30, 30), Weight::from_parts(30, 30),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(30, 30)))); assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(30, 30))));
@@ -241,7 +247,7 @@ fn allow_paid_should_work() {
&Parachain(1).into(), &Parachain(1).into(),
paying_message.inner_mut(), paying_message.inner_mut(),
Weight::from_parts(30, 30), Weight::from_parts(30, 30),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Unsupported)); assert_eq!(r, Err(ProcessMessageError::Unsupported));
@@ -249,7 +255,7 @@ fn allow_paid_should_work() {
&Parent.into(), &Parent.into(),
paying_message.inner_mut(), paying_message.inner_mut(),
Weight::from_parts(30, 30), Weight::from_parts(30, 30),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Ok(())); assert_eq!(r, Ok(()));
@@ -264,7 +270,7 @@ fn allow_paid_should_work() {
&Parent.into(), &Parent.into(),
paying_message_with_different_weight_parts.inner_mut(), paying_message_with_different_weight_parts.inner_mut(),
Weight::from_parts(20, 20), Weight::from_parts(20, 20),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20)))); assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(20, 20))));
@@ -272,7 +278,7 @@ fn allow_paid_should_work() {
&Parent.into(), &Parent.into(),
paying_message_with_different_weight_parts.inner_mut(), paying_message_with_different_weight_parts.inner_mut(),
Weight::from_parts(10, 10), Weight::from_parts(10, 10),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Ok(())) assert_eq!(r, Ok(()))
} }
@@ -288,7 +294,7 @@ fn suspension_should_work() {
&Parent.into(), &Parent.into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(10, 10), Weight::from_parts(10, 10),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Err(ProcessMessageError::Yield)); assert_eq!(r, Err(ProcessMessageError::Yield));
@@ -299,7 +305,7 @@ fn suspension_should_work() {
&Parent.into(), &Parent.into(),
message.inner_mut(), message.inner_mut(),
Weight::from_parts(10, 10), Weight::from_parts(10, 10),
&mut Weight::zero(), &mut props(Weight::zero()),
); );
assert_eq!(r, Ok(())); assert_eq!(r, Ok(()));
} }
@@ -26,7 +26,8 @@ parameter_types! {
} }
type TheBridge = type TheBridge =
TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation>>; TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation>>;
type Router = UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, UniversalLocation>; type Router =
TestTopic<UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, UniversalLocation>>;
/// ```nocompile /// ```nocompile
/// local | remote /// local | remote
@@ -40,21 +41,26 @@ type Router = UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, Un
/// ``` /// ```
#[test] #[test]
fn sending_to_bridged_chain_works() { fn sending_to_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
let dest = (Parent, Parent, Remote::get(), Parachain(1)).into(); let msg = Xcm(vec![Trap(1)]);
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into()); let dest = (Parent, Parent, Remote::get(), Parachain(1)).into();
assert_eq!(TheBridge::service(), 1); assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
assert_eq!( assert_eq!(TheBridge::service(), 1);
take_received_remote_messages(), assert_eq!(
vec![( take_received_remote_messages(),
Here.into(), vec![(
Xcm(vec![ Here.into(),
UniversalOrigin(Local::get().into()), xcm_with_topic(
DescendOrigin(Parachain(1).into()), [0; 32],
Trap(1), vec![
]) UniversalOrigin(Local::get().into()),
)] DescendOrigin(Parachain(1).into()),
); Trap(1),
]
)
)]
);
});
} }
/// ```nocompile /// ```nocompile
@@ -69,19 +75,24 @@ fn sending_to_bridged_chain_works() {
/// ``` /// ```
#[test] #[test]
fn sending_to_parachain_of_bridged_chain_works() { fn sending_to_parachain_of_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); let msg = Xcm(vec![Trap(1)]);
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into()); let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into();
assert_eq!(TheBridge::service(), 1); assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
let expected = vec![( assert_eq!(TheBridge::service(), 1);
(Parent, Parachain(1000)).into(), let expected = vec![(
Xcm(vec![ (Parent, Parachain(1000)).into(),
UniversalOrigin(Local::get().into()), xcm_with_topic(
DescendOrigin(Parachain(1).into()), [0; 32],
Trap(1), vec![
]), UniversalOrigin(Local::get().into()),
)]; DescendOrigin(Parachain(1).into()),
assert_eq!(take_received_remote_messages(), expected); Trap(1),
],
),
)];
assert_eq!(take_received_remote_messages(), expected);
});
} }
/// ```nocompile /// ```nocompile
@@ -96,17 +107,22 @@ fn sending_to_parachain_of_bridged_chain_works() {
/// ``` /// ```
#[test] #[test]
fn sending_to_relay_chain_of_bridged_chain_works() { fn sending_to_relay_chain_of_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
let dest = (Parent, Parent, Remote::get()).into(); let msg = Xcm(vec![Trap(1)]);
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into()); let dest = (Parent, Parent, Remote::get()).into();
assert_eq!(TheBridge::service(), 1); assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
let expected = vec![( assert_eq!(TheBridge::service(), 1);
Parent.into(), let expected = vec![(
Xcm(vec![ Parent.into(),
UniversalOrigin(Local::get().into()), xcm_with_topic(
DescendOrigin(Parachain(1).into()), [0; 32],
Trap(1), vec![
]), UniversalOrigin(Local::get().into()),
)]; DescendOrigin(Parachain(1).into()),
assert_eq!(take_received_remote_messages(), expected); Trap(1),
],
),
)];
assert_eq!(take_received_remote_messages(), expected);
});
} }
@@ -26,7 +26,8 @@ parameter_types! {
} }
type TheBridge = type TheBridge =
TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation>>; TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation>>;
type Router = UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, UniversalLocation>; type Router =
TestTopic<UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, UniversalLocation>>;
/// ```nocompile /// ```nocompile
/// local | remote /// local | remote
@@ -36,16 +37,20 @@ type Router = UnpaidLocalExporter<HaulBlobExporter<TheBridge, Remote, Price>, Un
/// ``` /// ```
#[test] #[test]
fn sending_to_bridged_chain_works() { fn sending_to_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
assert_eq!( let msg = Xcm(vec![Trap(1)]);
send_xcm::<Router>((Parent, Remote::get()).into(), msg).unwrap().1, assert_eq!(
(Here, 100).into() send_xcm::<Router>((Parent, Remote::get()).into(), msg).unwrap().1,
); (Here, 100).into()
assert_eq!(TheBridge::service(), 1); );
assert_eq!( assert_eq!(TheBridge::service(), 1);
take_received_remote_messages(), let expected = vec![(
vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))] 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 /// ```nocompile
@@ -60,11 +65,16 @@ fn sending_to_bridged_chain_works() {
/// ``` /// ```
#[test] #[test]
fn sending_to_parachain_of_bridged_chain_works() { fn sending_to_parachain_of_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
let dest = (Parent, Remote::get(), Parachain(1000)).into(); let msg = Xcm(vec![Trap(1)]);
assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into()); let dest = (Parent, Remote::get(), Parachain(1000)).into();
assert_eq!(TheBridge::service(), 1); assert_eq!(send_xcm::<Router>(dest, msg).unwrap().1, (Here, 100).into());
let expected = assert_eq!(TheBridge::service(), 1);
vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; let expected = vec![(
assert_eq!(take_received_remote_messages(), expected); 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 //! Tests specific to the bridging primitives
use super::mock::*; use super::mock::*;
use crate::universal_exports::*; use crate::{universal_exports::*, WithTopicSource};
use frame_support::{parameter_types, traits::Get}; use frame_support::{parameter_types, traits::Get};
use std::{cell::RefCell, marker::PhantomData}; use std::{cell::RefCell, marker::PhantomData};
use xcm_executor::{ use xcm_executor::{
@@ -37,12 +37,73 @@ parameter_types! {
pub Local: NetworkId = ByGenesis([0; 32]); pub Local: NetworkId = ByGenesis([0; 32]);
pub Remote: NetworkId = ByGenesis([1; 32]); pub Remote: NetworkId = ByGenesis([1; 32]);
pub Price: MultiAssets = MultiAssets::from((Here, 100u128)); pub Price: MultiAssets = MultiAssets::from((Here, 100u128));
pub static UsingTopic: bool = false;
} }
std::thread_local! { std::thread_local! {
static BRIDGE_TRAFFIC: RefCell<Vec<Vec<u8>>> = RefCell::new(Vec::new()); 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>); struct TestBridge<D>(PhantomData<D>);
impl<D: DispatchBlob> TestBridge<D> { impl<D: DispatchBlob> TestBridge<D> {
fn service() -> u64 { fn service() -> u64 {
@@ -71,7 +132,7 @@ impl SendXcm for TestRemoteIncomingRouter {
Ok((pair, MultiAssets::new())) Ok((pair, MultiAssets::new()))
} }
fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<XcmHash, SendError> { 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)); REMOTE_INCOMING_XCM.with(|q| q.borrow_mut().push(pair));
Ok(hash) Ok(hash)
} }
@@ -107,6 +168,20 @@ fn deliver<RemoteExporter: ExportXcm>(
export_xcm::<RemoteExporter>(n, c, s, d, m).map(|(hash, _)| hash) 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 impl<Local: Get<Junctions>, Remote: Get<Junctions>, RemoteExporter: ExportXcm> SendXcm
for UnpaidExecutingRouter<Local, Remote, RemoteExporter> for UnpaidExecutingRouter<Local, Remote, RemoteExporter>
{ {
@@ -133,15 +208,20 @@ impl<Local: Get<Junctions>, Remote: Get<Junctions>, RemoteExporter: ExportXcm> S
AllowUnpaidFrom::set(vec![origin.clone()]); AllowUnpaidFrom::set(vec![origin.clone()]);
set_exporter_override(price::<RemoteExporter>, deliver::<RemoteExporter>); set_exporter_override(price::<RemoteExporter>, deliver::<RemoteExporter>);
// The we execute it: // The we execute it:
let hash = fake_message_hash(&message); let mut id = fake_id();
let outcome = XcmExecutor::<TestConfig>::execute_xcm( let outcome = XcmExecutor::<TestConfig>::prepare_and_execute(
origin, origin,
message.into(), message.clone().into(),
hash, &mut id,
Weight::from_parts(2_000_000_000_000, 2_000_000_000_000), 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 { match outcome {
Outcome::Complete(..) => Ok(hash), Outcome::Complete(..) => Ok(id),
Outcome::Incomplete(..) => Err(Transport("Error executing")), Outcome::Incomplete(..) => Err(Transport("Error executing")),
Outcome::Error(..) => Err(Transport("Unable to execute")), 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()]); AllowPaidFrom::set(vec![origin.clone()]);
set_exporter_override(price::<RemoteExporter>, deliver::<RemoteExporter>); set_exporter_override(price::<RemoteExporter>, deliver::<RemoteExporter>);
// Then we execute it: // Then we execute it:
let hash = fake_message_hash(&message); let mut id = fake_id();
let outcome = XcmExecutor::<TestConfig>::execute_xcm( let outcome = XcmExecutor::<TestConfig>::prepare_and_execute(
origin, origin,
message.into(), message.clone().into(),
hash, &mut id,
Weight::from_parts(2_000_000_000_000, 2_000_000_000_000), 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 { match outcome {
Outcome::Complete(..) => Ok(hash), Outcome::Complete(..) => Ok(id),
Outcome::Incomplete(..) => Err(Transport("Error executing")), Outcome::Incomplete(..) => Err(Transport("Error executing")),
Outcome::Error(..) => Err(Transport("Unable to execute")), Outcome::Error(..) => Err(Transport("Unable to execute")),
} }
@@ -27,8 +27,8 @@ parameter_types! {
pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(100)); pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(100));
pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get())); pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get()));
pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get())); pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get()));
pub static BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)> pub BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>
= vec![(Remote::get(), MultiLocation::parent(), Some((Parent, 200u128).into()))]; = 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 // ^^^ 100 to use the bridge (export) and 100 for the remote execution weight (5 instructions
// x (10 + 10) weight each). // x (10 + 10) weight each).
} }
@@ -41,7 +41,7 @@ type LocalBridgeRouter = SovereignPaidRemoteExporter<
LocalInnerRouter, LocalInnerRouter,
UniversalLocation, UniversalLocation,
>; >;
type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); type LocalRouter = TestTopic<(LocalInnerRouter, LocalBridgeRouter)>;
/// ```nocompile /// ```nocompile
/// local | remote /// local | remote
@@ -55,33 +55,66 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter);
/// ``` /// ```
#[test] #[test]
fn sending_to_bridged_chain_works() { 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(); let dest: MultiLocation = (Parent, Parent, Remote::get()).into();
// Routing won't work if we don't have enough funds. // Routing won't work if we don't have enough funds.
assert_eq!( assert_eq!(
send_xcm::<LocalRouter>(dest.clone(), Xcm(vec![Trap(1)])), send_xcm::<LocalRouter>(dest.clone(), Xcm(vec![Trap(1)])),
Err(SendError::Transport("Error executing")), 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 /// ```nocompile
@@ -96,29 +129,64 @@ fn sending_to_bridged_chain_works() {
/// ``` /// ```
#[test] #[test]
fn sending_to_parachain_of_bridged_chain_works() { 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(); let dest: MultiLocation = (Parent, Parent, Remote::get(), Parachain(100)).into();
// Routing won't work if we don't have enough funds. // Routing won't work if we don't have enough funds.
assert_eq!( assert_eq!(
send_xcm::<LocalRouter>(dest.clone(), Xcm(vec![Trap(1)])), send_xcm::<LocalRouter>(dest.clone(), Xcm(vec![Trap(1)])),
Err(SendError::Transport("Error executing")), 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>; UnpaidExecutingRouter<UniversalLocation, ParaBridgeUniversalLocation, RelayExporter>;
type LocalBridgingRouter = type LocalBridgingRouter =
UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, LocalInnerRouter, UniversalLocation>; UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, LocalInnerRouter, UniversalLocation>;
type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); type LocalRouter = TestTopic<(LocalInnerRouter, LocalBridgingRouter)>;
/// ```nocompile /// ```nocompile
/// local | remote /// local | remote
@@ -48,25 +48,49 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter);
/// ``` /// ```
#[test] #[test]
fn sending_to_bridged_chain_works() { fn sending_to_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
assert_eq!( let msg = Xcm(vec![Trap(1)]);
send_xcm::<LocalRouter>((Parent, Parent, Remote::get(), Parachain(1)).into(), msg) assert_eq!(
.unwrap() send_xcm::<LocalRouter>((Parent, Parent, Remote::get(), Parachain(1)).into(), msg)
.1, .unwrap()
MultiAssets::new() .1,
); MultiAssets::new()
assert_eq!(TheBridge::service(), 1); );
assert_eq!( assert_eq!(TheBridge::service(), 1);
take_received_remote_messages(), assert_eq!(
vec![( take_received_remote_messages(),
Here.into(), vec![(
Xcm(vec![ Here.into(),
UniversalOrigin(Local::get().into()), xcm_with_topic(
DescendOrigin(Parachain(1000).into()), [0; 32],
Trap(1) 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 /// ```nocompile
@@ -81,19 +105,43 @@ fn sending_to_bridged_chain_works() {
/// ``` /// ```
#[test] #[test]
fn sending_to_sibling_of_bridged_chain_works() { fn sending_to_sibling_of_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); let msg = Xcm(vec![Trap(1)]);
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new()); let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into();
assert_eq!(TheBridge::service(), 1); assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
let expected = vec![( assert_eq!(TheBridge::service(), 1);
(Parent, Parachain(1000)).into(), let expected = vec![(
Xcm(vec![ (Parent, Parachain(1000)).into(),
UniversalOrigin(Local::get().into()), xcm_with_topic(
DescendOrigin(Parachain(1000).into()), [0; 32],
Trap(1), vec![
]), UniversalOrigin(Local::get().into()),
)]; DescendOrigin(Parachain(1000).into()),
assert_eq!(take_received_remote_messages(), expected); 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 /// ```nocompile
@@ -108,17 +156,41 @@ fn sending_to_sibling_of_bridged_chain_works() {
/// ``` /// ```
#[test] #[test]
fn sending_to_relay_of_bridged_chain_works() { fn sending_to_relay_of_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
let dest = (Parent, Parent, Remote::get()).into(); let msg = Xcm(vec![Trap(1)]);
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new()); let dest = (Parent, Parent, Remote::get()).into();
assert_eq!(TheBridge::service(), 1); assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
let expected = vec![( assert_eq!(TheBridge::service(), 1);
Parent.into(), let expected = vec![(
Xcm(vec![ Parent.into(),
UniversalOrigin(Local::get().into()), xcm_with_topic(
DescendOrigin(Parachain(1000).into()), [0; 32],
Trap(1), vec![
]), UniversalOrigin(Local::get().into()),
)]; DescendOrigin(Parachain(1000).into()),
assert_eq!(take_received_remote_messages(), expected); 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>; UnpaidExecutingRouter<UniversalLocation, ParaBridgeUniversalLocation, RelayExporter>;
type LocalBridgingRouter = type LocalBridgingRouter =
UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, LocalInnerRouter, UniversalLocation>; UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, LocalInnerRouter, UniversalLocation>;
type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); type LocalRouter = TestTopic<(LocalInnerRouter, LocalBridgingRouter)>;
/// ```nocompile /// ```nocompile
/// local | remote /// local | remote
@@ -48,18 +48,40 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter);
/// ``` /// ```
#[test] #[test]
fn sending_to_bridged_chain_works() { fn sending_to_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
assert_eq!( let msg = Xcm(vec![Trap(1)]);
send_xcm::<LocalRouter>((Parent, Remote::get(), Parachain(1)).into(), msg) assert_eq!(
.unwrap() send_xcm::<LocalRouter>((Parent, Remote::get(), Parachain(1)).into(), msg)
.1, .unwrap()
MultiAssets::new() .1,
); MultiAssets::new()
assert_eq!(TheBridge::service(), 1); );
assert_eq!( assert_eq!(TheBridge::service(), 1);
take_received_remote_messages(), let expected = vec![(
vec![(Here.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))] 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 /// ```nocompile
@@ -74,15 +96,36 @@ fn sending_to_bridged_chain_works() {
/// ``` /// ```
#[test] #[test]
fn sending_to_sibling_of_bridged_chain_works() { fn sending_to_sibling_of_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
let dest = (Parent, Remote::get(), Parachain(1000)).into(); let msg = Xcm(vec![Trap(1)]);
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new()); let dest = (Parent, Remote::get(), Parachain(1000)).into();
assert_eq!(TheBridge::service(), 1); assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
let expected = vec![( assert_eq!(TheBridge::service(), 1);
(Parent, Parachain(1000)).into(), let expected = vec![(
Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]), (Parent, Parachain(1000)).into(),
)]; xcm_with_topic([0; 32], vec![UniversalOrigin(Local::get().into()), Trap(1)]),
assert_eq!(take_received_remote_messages(), expected); )];
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 /// ```nocompile
@@ -97,10 +140,34 @@ fn sending_to_sibling_of_bridged_chain_works() {
/// ``` /// ```
#[test] #[test]
fn sending_to_relay_of_bridged_chain_works() { fn sending_to_relay_of_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
let dest = (Parent, Remote::get()).into(); let msg = Xcm(vec![Trap(1)]);
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new()); let dest = (Parent, Remote::get()).into();
assert_eq!(TheBridge::service(), 1); assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
let expected = vec![(Parent.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; assert_eq!(TheBridge::service(), 1);
assert_eq!(take_received_remote_messages(), expected); 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>; UnpaidExecutingRouter<UniversalLocation, RelayUniversalLocation, RelayExporter>;
type LocalBridgeRouter = type LocalBridgeRouter =
UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, LocalInnerRouter, UniversalLocation>; UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, LocalInnerRouter, UniversalLocation>;
type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); type LocalRouter = TestTopic<(LocalInnerRouter, LocalBridgeRouter)>;
/// ```nocompile /// ```nocompile
/// local | remote /// local | remote
@@ -48,23 +48,47 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter);
/// ``` /// ```
#[test] #[test]
fn sending_to_bridged_chain_works() { fn sending_to_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
assert_eq!( let msg = Xcm(vec![Trap(1)]);
send_xcm::<LocalRouter>((Parent, Parent, Remote::get()).into(), msg).unwrap().1, assert_eq!(
MultiAssets::new() send_xcm::<LocalRouter>((Parent, Parent, Remote::get()).into(), msg).unwrap().1,
); MultiAssets::new()
assert_eq!(TheBridge::service(), 1); );
assert_eq!( assert_eq!(TheBridge::service(), 1);
take_received_remote_messages(), assert_eq!(
vec![( take_received_remote_messages(),
Here.into(), vec![(
Xcm(vec![ Here.into(),
UniversalOrigin(Local::get().into()), xcm_with_topic(
DescendOrigin(Parachain(1000).into()), [0; 32],
Trap(1) 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 /// ```nocompile
@@ -79,17 +103,41 @@ fn sending_to_bridged_chain_works() {
/// ``` /// ```
#[test] #[test]
fn sending_to_parachain_of_bridged_chain_works() { fn sending_to_parachain_of_bridged_chain_works() {
let msg = Xcm(vec![Trap(1)]); maybe_with_topic(|| {
let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); let msg = Xcm(vec![Trap(1)]);
assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new()); let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into();
assert_eq!(TheBridge::service(), 1); assert_eq!(send_xcm::<LocalRouter>(dest, msg).unwrap().1, MultiAssets::new());
let expected = vec![( assert_eq!(TheBridge::service(), 1);
Parachain(1000).into(), let expected = vec![(
Xcm(vec![ Parachain(1000).into(),
UniversalOrigin(Local::get().into()), xcm_with_topic(
DescendOrigin(Parachain(1000).into()), [0; 32],
Trap(1), vec![
]), UniversalOrigin(Local::get().into()),
)]; DescendOrigin(Parachain(1000).into()),
assert_eq!(take_received_remote_messages(), expected); 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]);
})
} }
+7 -3
View File
@@ -15,7 +15,7 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use crate::{ use crate::{
barriers::{AllowSubscriptionsFrom, RespectSuspension}, barriers::{AllowSubscriptionsFrom, RespectSuspension, TrailingSetTopicAsId},
test_utils::*, test_utils::*,
}; };
pub use crate::{ pub use crate::{
@@ -41,6 +41,7 @@ pub use sp_std::{
marker::PhantomData, marker::PhantomData,
}; };
pub use xcm::latest::{prelude::*, Weight}; pub use xcm::latest::{prelude::*, Weight};
use xcm_executor::traits::Properties;
pub use xcm_executor::{ pub use xcm_executor::{
traits::{ traits::{
AssetExchange, AssetLock, CheckSuspension, ConvertOrigin, Enact, ExportXcm, FeeManager, 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>) { 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())); 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; pub struct TestAssetTransactor;
impl TransactAsset for TestAssetTransactor { impl TransactAsset for TestAssetTransactor {
@@ -429,7 +433,7 @@ impl CheckSuspension for TestSuspender {
_origin: &MultiLocation, _origin: &MultiLocation,
_instructions: &mut [Instruction<Call>], _instructions: &mut [Instruction<Call>],
_max_weight: Weight, _max_weight: Weight,
_weight_credit: &mut Weight, _properties: &mut Properties,
) -> bool { ) -> bool {
SUSPENDED.with(|s| s.get()) SUSPENDED.with(|s| s.get())
} }
@@ -651,7 +655,7 @@ impl Config for TestConfig {
type IsReserve = TestIsReserve; type IsReserve = TestIsReserve;
type IsTeleporter = TestIsTeleporter; type IsTeleporter = TestIsTeleporter;
type UniversalLocation = ExecutorUniversalLocation; type UniversalLocation = ExecutorUniversalLocation;
type Barrier = RespectSuspension<TestBarrier, TestSuspender>; type Barrier = TrailingSetTopicAsId<RespectSuspension<TestBarrier, TestSuspender>>;
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall, MaxInstructions>; type Weigher = FixedWeightBounds<UnitWeightCost, TestCall, MaxInstructions>;
type Trader = FixedRateOfFungible<WeightPrice, ()>; type Trader = FixedRateOfFungible<WeightPrice, ()>;
type ResponseHandler = TestResponseHandler; 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 /// Implementation of `SendXcm` which wraps the message inside an `ExportMessage` instruction
/// and sends it to a destination known to be able to handle it. /// 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 /// This is only useful if we have special dispensation by the remote bridges to have the
/// `ExportMessage` instruction executed without payment. /// `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>( pub struct UnpaidRemoteExporter<Bridges, Router, UniversalLocation>(
PhantomData<(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 devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|_| NotApplicable)?;
let (remote_network, remote_location) = devolved; let (remote_network, remote_location) = devolved;
let xcm = xcm.take().ok_or(MissingArgument)?; let xcm = xcm.take().ok_or(MissingArgument)?;
let (bridge, maybe_payment) = let (bridge, maybe_payment) =
Bridges::exporter_for(&remote_network, &remote_location, &xcm).ok_or(NotApplicable)?; Bridges::exporter_for(&remote_network, &remote_location, &xcm).ok_or(NotApplicable)?;
ensure!(maybe_payment.is_none(), Unroutable); 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 // 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 // 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. // 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 }, UnpaidExecution { weight_limit: Unlimited, check_origin: None },
ExportMessage { network: remote_network, destination: remote_location, xcm }, 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) validate_send::<Router>(bridge, message)
} }
fn deliver(validation: Router::Ticket) -> Result<XcmHash, SendError> { fn deliver(validation: Self::Ticket) -> Result<XcmHash, SendError> {
Router::deliver(validation) 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 /// 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. /// 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>( pub struct SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>(
PhantomData<(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 (remote_network, remote_location) = devolved;
let xcm = xcm.take().ok_or(MissingArgument)?; 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) = let (bridge, maybe_payment) =
Bridges::exporter_for(&remote_network, &remote_location, &xcm).ok_or(NotApplicable)?; 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 = let export_instruction =
ExportMessage { network: remote_network, destination: remote_location, xcm }; 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 let fees = payment
.clone() .clone()
.reanchored(&bridge, UniversalLocation::get()) .reanchored(&bridge, UniversalLocation::get())
@@ -219,6 +262,9 @@ impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMulti
} else { } else {
vec![export_instruction] 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 // We then send a normal message to the bridge asking it to export the prepended
// message to the remote chain. // message to the remote chain.
@@ -344,22 +390,24 @@ impl<Bridge: HaulBlob, BridgedNetwork: Get<NetworkId>, Price: Get<MultiAssets>>
.ok_or(SendError::MissingArgument)? .ok_or(SendError::MissingArgument)?
.split_global() .split_global()
.map_err(|()| SendError::Unroutable)?; .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 { if local_sub != Here {
inner.inner_mut().push(DescendOrigin(local_sub)); message.0.insert(1, DescendOrigin(local_sub));
} }
inner let message = VersionedXcm::from(message);
.inner_mut() let id = maybe_id.unwrap_or_else(|| message.using_encoded(sp_io::hashing::blake2_256));
.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 blob = BridgeMessage { universal_dest, message }.encode(); 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)?; Bridge::haul_blob(blob)?;
Ok(hash) Ok(id)
} }
} }
@@ -16,7 +16,6 @@
mod mock; mod mock;
use frame_support::weights::Weight;
use mock::{ use mock::{
fake_message_hash, kusama_like_with_balances, AccountId, Balance, Balances, BaseXcmWeight, fake_message_hash, kusama_like_with_balances, AccountId, Balance, Balances, BaseXcmWeight,
System, XcmConfig, CENTS, System, XcmConfig, CENTS,
@@ -65,9 +65,9 @@ fn basic_buy_fees_message_executes() {
client.state_at(block_hash).expect("state should exist").inspect_state(|| { client.state_at(block_hash).expect("state should exist").inspect_state(|| {
assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
r.event, r.event,
polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted( polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted {
Outcome::Complete(_) outcome: Outcome::Complete(_)
)), }),
))); )));
}); });
} }
@@ -118,9 +118,9 @@ fn transact_recursion_limit_works() {
client.state_at(block_hash).expect("state should exist").inspect_state(|| { client.state_at(block_hash).expect("state should exist").inspect_state(|| {
assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
r.event, r.event,
polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted( polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::Attempted {
Outcome::Incomplete(_, XcmError::ExceedsStackLimit) outcome: Outcome::Incomplete(_, XcmError::ExceedsStackLimit)
)), }),
))); )));
}); });
} }
@@ -195,10 +195,10 @@ fn query_response_fires() {
client.state_at(block_hash).expect("state should exist").inspect_state(|| { client.state_at(block_hash).expect("state should exist").inspect_state(|| {
assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!( assert!(polkadot_test_runtime::System::events().iter().any(|r| matches!(
r.event, r.event,
polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::ResponseReady( polkadot_test_runtime::RuntimeEvent::Xcm(pallet_xcm::Event::ResponseReady {
q, query_id: q,
Response::ExecutionResult(None), response: Response::ExecutionResult(None),
)) if q == query_id, }) if q == query_id,
))); )));
assert_eq!( assert_eq!(
polkadot_test_runtime::Xcm::query(query_id), polkadot_test_runtime::Xcm::query(query_id),
+13 -10
View File
@@ -31,8 +31,8 @@ use xcm::latest::prelude::*;
pub mod traits; pub mod traits;
use traits::{ use traits::{
validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin,
DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, ShouldExecute, TransactAsset, DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, Properties, ShouldExecute,
VersionChangeNotifier, WeightBounds, WeightTrader, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader,
}; };
mod assets; mod assets;
@@ -193,8 +193,8 @@ impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Con
fn execute( fn execute(
origin: impl Into<MultiLocation>, origin: impl Into<MultiLocation>,
WeighedMessage(xcm_weight, mut message): WeighedMessage<Config::RuntimeCall>, WeighedMessage(xcm_weight, mut message): WeighedMessage<Config::RuntimeCall>,
message_hash: XcmHash, id: &mut XcmHash,
mut weight_credit: Weight, weight_credit: Weight,
) -> Outcome { ) -> Outcome {
let origin = origin.into(); let origin = origin.into();
log::trace!( log::trace!(
@@ -204,24 +204,27 @@ impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Con
message, message,
weight_credit, weight_credit,
); );
let mut properties = Properties { weight_credit, message_id: None };
if let Err(e) = Config::Barrier::should_execute( if let Err(e) = Config::Barrier::should_execute(
&origin, &origin,
message.inner_mut(), message.inner_mut(),
xcm_weight, xcm_weight,
&mut weight_credit, &mut properties,
) { ) {
log::trace!( log::trace!(
target: "xcm::execute_xcm_in_credit", target: "xcm::execute_xcm_in_credit",
"Barrier blocked execution! Error: {:?}. (origin: {:?}, message: {:?}, weight_credit: {:?})", "Barrier blocked execution! Error: {:?}. (origin: {:?}, message: {:?}, properties: {:?})",
e, e,
origin, origin,
message, message,
weight_credit, properties,
); );
return Outcome::Error(XcmError::Barrier) 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() { while !message.0.is_empty() {
let result = vm.process(message); let result = vm.process(message);
@@ -272,12 +275,12 @@ impl From<ExecutorError> for frame_benchmarking::BenchmarkError {
} }
impl<Config: config::Config> XcmExecutor<Config> { 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(); let origin = origin.into();
Self { Self {
holding: Assets::new(), holding: Assets::new(),
holding_limit: Config::MaxAssetsIntoHolding::get() as usize, 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, original_origin: origin,
trader: Config::Trader::new(), trader: Config::Trader::new(),
error: None, error: None,
+1 -1
View File
@@ -40,7 +40,7 @@ pub use token_matching::{
mod on_response; mod on_response;
pub use on_response::{OnResponse, VersionChangeNotifier}; pub use on_response::{OnResponse, VersionChangeNotifier};
mod should_execute; mod should_execute;
pub use should_execute::{CheckSuspension, ShouldExecute}; pub use should_execute::{CheckSuspension, Properties, ShouldExecute};
mod transact_asset; mod transact_asset;
pub use transact_asset::TransactAsset; pub use transact_asset::TransactAsset;
mod weight; mod weight;
@@ -16,7 +16,19 @@
use frame_support::traits::ProcessMessageError; use frame_support::traits::ProcessMessageError;
use sp_std::result::Result; 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. /// 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. /// - `origin`: The origin (sender) of the message.
/// - `instructions`: The message itself. /// - `instructions`: The message itself.
/// - `max_weight`: The (possibly over-) estimation of the weight of execution of the message. /// - `max_weight`: The (possibly over-) estimation of the weight of execution of the message.
/// - `weight_credit`: The pre-established amount of weight that the system has determined this /// - `properties`: Various pre-established properties of the message which may be mutated by
/// message may utilize in its execution. Typically non-zero only because of prior fee /// this API.
/// payment, but could in principle be due to other factors.
fn should_execute<RuntimeCall>( fn should_execute<RuntimeCall>(
origin: &MultiLocation, origin: &MultiLocation,
instructions: &mut [Instruction<RuntimeCall>], instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight, max_weight: Weight,
weight_credit: &mut Weight, properties: &mut Properties,
) -> Result<(), ProcessMessageError>; ) -> Result<(), ProcessMessageError>;
} }
@@ -45,21 +56,21 @@ impl ShouldExecute for Tuple {
origin: &MultiLocation, origin: &MultiLocation,
instructions: &mut [Instruction<RuntimeCall>], instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight, max_weight: Weight,
weight_credit: &mut Weight, properties: &mut Properties,
) -> Result<(), ProcessMessageError> { ) -> Result<(), ProcessMessageError> {
for_tuples!( #( for_tuples!( #(
match Tuple::should_execute(origin, instructions, max_weight, weight_credit) { match Tuple::should_execute(origin, instructions, max_weight, properties) {
Ok(()) => return Ok(()), Ok(()) => return Ok(()),
_ => (), _ => (),
} }
)* ); )* );
log::trace!( log::trace!(
target: "xcm::should_execute", target: "xcm::should_execute",
"did not pass barrier: origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}", "did not pass barrier: origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, origin,
instructions, instructions,
max_weight, max_weight,
weight_credit, properties,
); );
Err(ProcessMessageError::Unsupported) Err(ProcessMessageError::Unsupported)
} }
@@ -79,7 +90,7 @@ pub trait CheckSuspension {
origin: &MultiLocation, origin: &MultiLocation,
instructions: &mut [Instruction<Call>], instructions: &mut [Instruction<Call>],
max_weight: Weight, max_weight: Weight,
weight_credit: &mut Weight, properties: &mut Properties,
) -> bool; ) -> bool;
} }
@@ -89,10 +100,10 @@ impl CheckSuspension for Tuple {
origin: &MultiLocation, origin: &MultiLocation,
instruction: &mut [Instruction<Call>], instruction: &mut [Instruction<Call>],
max_weight: Weight, max_weight: Weight,
weight_credit: &mut Weight, properties: &mut Properties,
) -> bool { ) -> bool {
for_tuples!( #( for_tuples!( #(
if Tuple::is_suspended(origin, instruction, max_weight, weight_credit) { if Tuple::is_suspended(origin, instruction, max_weight, properties) {
return true return true
} }
)* ); )* );
@@ -397,7 +397,7 @@ mod tests {
MultiTransactor::deposit_asset( MultiTransactor::deposit_asset(
&(Here, 1u128).into(), &(Here, 1u128).into(),
&Here.into(), &Here.into(),
&XcmContext::with_message_hash([0; 32]), &XcmContext::with_message_id([0; 32]),
), ),
Err(XcmError::AssetNotFound) Err(XcmError::AssetNotFound)
); );
@@ -411,7 +411,7 @@ mod tests {
MultiTransactor::deposit_asset( MultiTransactor::deposit_asset(
&(Here, 1u128).into(), &(Here, 1u128).into(),
&Here.into(), &Here.into(),
&XcmContext::with_message_hash([0; 32]), &XcmContext::with_message_id([0; 32]),
), ),
Ok(()) Ok(())
); );
@@ -425,7 +425,7 @@ mod tests {
MultiTransactor::deposit_asset( MultiTransactor::deposit_asset(
&(Here, 1u128).into(), &(Here, 1u128).into(),
&Here.into(), &Here.into(),
&XcmContext::with_message_hash([0; 32]), &XcmContext::with_message_id([0; 32]),
), ),
Err(XcmError::Overflow) Err(XcmError::Overflow)
); );
@@ -439,7 +439,7 @@ mod tests {
MultiTransactor::deposit_asset( MultiTransactor::deposit_asset(
&(Here, 1u128).into(), &(Here, 1u128).into(),
&Here.into(), &Here.into(),
&XcmContext::with_message_hash([0; 32]), &XcmContext::with_message_id([0; 32]),
), ),
Ok(()), Ok(()),
); );