mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 10:31:03 +00:00
XCM v2: Scripting, Query responses, Exception handling and Error reporting (#3629)
* Intoduce XCM v2 Also some minor fix for v0/v1 * Minor version cleanup * Minor version cleanup * Introduce SendError for XcmSend trait to avoid cycles with having Outcome in Xcm * comment * Corrent type * Docs * Fix build * Fixes * Introduce the basic impl * Docs * Add function * Basic implementation * Weighed responses and on_report * Make XCM more script-like * Remove BuyExecution::orders * Fixes * Fixes * Fixes * Formatting * Initial draft and make pallet-xcm build * fix XCM tests * Formatting * Fixes * Formatting * spelling * Fixes * Fixes * spelling * tests for translation * extra fields to XCM pallet * Formatting * Fixes * spelling * first integration test * Another integration test * Formatting * fix tests * all tests * Fixes * Fixes * Formatting * Fixes * Fixes * Formatting * Bump * Remove unneeded structuring * add instruction * Fixes * spelling * Fixes * Fixes * Formatting * Fixes * Fixes * Formatting * Introduce and use VersionedResponse * Introduce versioning to dispatchables' params * Fixes * Formatting * Rest of merge * more work * Formatting * Basic logic * Fixes * Fixes * Add test * Fixes * Formatting * Fixes * Fixes * Fixes * Nits * Simplify * Spelling * Formatting * Return weight of unexecuted instructions in case of error as surplus * Formatting * Fixes * Test for instruction count limiting * Formatting * Docs
This commit is contained in:
@@ -19,10 +19,10 @@
|
||||
use frame_support::{ensure, traits::Contains, weights::Weight};
|
||||
use polkadot_parachain::primitives::IsSystem;
|
||||
use sp_std::{marker::PhantomData, result::Result};
|
||||
use xcm::latest::{Junction, Junctions, MultiLocation, Order, Xcm};
|
||||
use xcm::latest::{Instruction::*, Junction, Junctions, MultiLocation, WeightLimit::*, Xcm};
|
||||
use xcm_executor::traits::{OnResponse, ShouldExecute};
|
||||
|
||||
/// Execution barrier that just takes `shallow_weight` from `weight_credit`.
|
||||
/// Execution barrier that just takes `max_weight` from `weight_credit`.
|
||||
///
|
||||
/// Useful to allow XCM execution by local chain users via extrinsics.
|
||||
/// E.g. `pallet_xcm::reserve_asset_transfer` to transfer a reserve asset
|
||||
@@ -30,42 +30,53 @@ use xcm_executor::traits::{OnResponse, ShouldExecute};
|
||||
pub struct TakeWeightCredit;
|
||||
impl ShouldExecute for TakeWeightCredit {
|
||||
fn should_execute<Call>(
|
||||
_origin: &MultiLocation,
|
||||
_origin: &Option<MultiLocation>,
|
||||
_top_level: bool,
|
||||
_message: &Xcm<Call>,
|
||||
shallow_weight: Weight,
|
||||
_message: &mut Xcm<Call>,
|
||||
max_weight: Weight,
|
||||
weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
*weight_credit = weight_credit.checked_sub(shallow_weight).ok_or(())?;
|
||||
*weight_credit = weight_credit.checked_sub(max_weight).ok_or(())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking payments into
|
||||
/// account.
|
||||
/// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking
|
||||
/// payments into account.
|
||||
///
|
||||
/// Only allows for `TeleportAsset`, `WithdrawAsset` and `ReserveAssetDeposit` XCMs because they are the only ones
|
||||
/// that place assets in the Holding Register to pay for execution.
|
||||
/// Only allows for `TeleportAsset`, `WithdrawAsset` and `ReserveAssetDeposit` XCMs because they are
|
||||
/// the only ones that place assets in the Holding Register to pay for execution.
|
||||
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
|
||||
impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
|
||||
fn should_execute<Call>(
|
||||
origin: &MultiLocation,
|
||||
origin: &Option<MultiLocation>,
|
||||
top_level: bool,
|
||||
message: &Xcm<Call>,
|
||||
shallow_weight: Weight,
|
||||
message: &mut Xcm<Call>,
|
||||
max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
let origin = origin.as_ref().ok_or(())?;
|
||||
ensure!(T::contains(origin), ());
|
||||
ensure!(top_level, ());
|
||||
match message {
|
||||
Xcm::ReceiveTeleportedAsset { effects, .. } |
|
||||
Xcm::WithdrawAsset { effects, .. } |
|
||||
Xcm::ReserveAssetDeposited { effects, .. }
|
||||
if matches!(
|
||||
effects.first(),
|
||||
Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight
|
||||
) =>
|
||||
Ok(()),
|
||||
let mut iter = message.0.iter_mut();
|
||||
let i = iter.next().ok_or(())?;
|
||||
match i {
|
||||
ReceiveTeleportedAsset(..) | WithdrawAsset(..) | ReserveAssetDeposited(..) => (),
|
||||
_ => return Err(()),
|
||||
}
|
||||
let mut i = iter.next().ok_or(())?;
|
||||
while let ClearOrigin = i {
|
||||
i = iter.next().ok_or(())?;
|
||||
}
|
||||
match i {
|
||||
BuyExecution { weight_limit: Limited(ref mut weight), .. } if *weight >= max_weight => {
|
||||
*weight = max_weight;
|
||||
Ok(())
|
||||
},
|
||||
BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => {
|
||||
*weight_limit = Limited(max_weight);
|
||||
Ok(())
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
@@ -76,12 +87,13 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFro
|
||||
pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>);
|
||||
impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
|
||||
fn should_execute<Call>(
|
||||
origin: &MultiLocation,
|
||||
origin: &Option<MultiLocation>,
|
||||
_top_level: bool,
|
||||
_message: &Xcm<Call>,
|
||||
_shallow_weight: Weight,
|
||||
_message: &mut Xcm<Call>,
|
||||
_max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
let origin = origin.as_ref().ok_or(())?;
|
||||
ensure!(T::contains(origin), ());
|
||||
Ok(())
|
||||
}
|
||||
@@ -103,14 +115,15 @@ impl<ParaId: IsSystem + From<u32>> Contains<MultiLocation> for IsChildSystemPara
|
||||
pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>);
|
||||
impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
|
||||
fn should_execute<Call>(
|
||||
origin: &MultiLocation,
|
||||
origin: &Option<MultiLocation>,
|
||||
_top_level: bool,
|
||||
message: &Xcm<Call>,
|
||||
_shallow_weight: Weight,
|
||||
message: &mut Xcm<Call>,
|
||||
_max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
match message {
|
||||
Xcm::QueryResponse { query_id, .. }
|
||||
let origin = origin.as_ref().ok_or(())?;
|
||||
match message.0.first() {
|
||||
Some(QueryResponse { query_id, .. })
|
||||
if ResponseHandler::expecting_response(origin, *query_id) =>
|
||||
Ok(()),
|
||||
_ => Err(()),
|
||||
|
||||
@@ -107,7 +107,7 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> {
|
||||
}
|
||||
pub struct TestSendXcm;
|
||||
impl SendXcm for TestSendXcm {
|
||||
fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> XcmResult {
|
||||
fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> SendResult {
|
||||
SENT_XCM.with(|q| q.borrow_mut().push((dest, msg)));
|
||||
Ok(())
|
||||
}
|
||||
@@ -222,9 +222,10 @@ impl OnResponse for TestResponseHandler {
|
||||
})
|
||||
}
|
||||
fn on_response(
|
||||
_origin: MultiLocation,
|
||||
_origin: &MultiLocation,
|
||||
query_id: u64,
|
||||
response: xcm::latest::Response,
|
||||
_max_weight: Weight,
|
||||
) -> Weight {
|
||||
QUERIES.with(|q| {
|
||||
q.borrow_mut().entry(query_id).and_modify(|v| {
|
||||
@@ -258,6 +259,7 @@ parameter_types! {
|
||||
pub static AllowPaidFrom: Vec<MultiLocation> = vec![];
|
||||
// 1_000_000_000_000 => 1 unit of asset for 1 unit of Weight.
|
||||
pub static WeightPrice: (AssetId, u128) = (From::from(Here), 1_000_000_000_000);
|
||||
pub static MaxInstructions: u32 = 100;
|
||||
}
|
||||
|
||||
pub type TestBarrier = (
|
||||
@@ -277,7 +279,7 @@ impl Config for TestConfig {
|
||||
type IsTeleporter = TestIsTeleporter;
|
||||
type LocationInverter = LocationInverter<TestAncestry>;
|
||||
type Barrier = TestBarrier;
|
||||
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall>;
|
||||
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall, MaxInstructions>;
|
||||
type Trader = FixedRateOfFungible<WeightPrice, ()>;
|
||||
type ResponseHandler = TestResponseHandler;
|
||||
}
|
||||
|
||||
@@ -44,31 +44,21 @@ fn basic_setup_works() {
|
||||
|
||||
#[test]
|
||||
fn weigher_should_work() {
|
||||
let mut message = opaque::Xcm::ReserveAssetDeposited {
|
||||
assets: (Parent, 100).into(),
|
||||
effects: vec![
|
||||
Order::BuyExecution {
|
||||
fees: (Parent, 1).into(),
|
||||
weight: 0,
|
||||
debt: 30,
|
||||
halt_on_error: true,
|
||||
instructions: vec![],
|
||||
},
|
||||
Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() },
|
||||
],
|
||||
}
|
||||
.into();
|
||||
assert_eq!(<TestConfig as Config>::Weigher::shallow(&mut message), Ok(30));
|
||||
let mut message = Xcm(vec![
|
||||
ReserveAssetDeposited((Parent, 100).into()),
|
||||
BuyExecution { fees: (Parent, 1).into(), weight_limit: Limited(30) },
|
||||
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() },
|
||||
]);
|
||||
assert_eq!(<TestConfig as Config>::Weigher::weight(&mut message), Ok(30));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_weight_credit_barrier_should_work() {
|
||||
let mut message =
|
||||
opaque::Xcm::TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() };
|
||||
|
||||
Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]);
|
||||
let mut weight_credit = 10;
|
||||
let r = TakeWeightCredit::should_execute(
|
||||
&Parent.into(),
|
||||
&Some(Parent.into()),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -78,7 +68,7 @@ fn take_weight_credit_barrier_should_work() {
|
||||
assert_eq!(weight_credit, 0);
|
||||
|
||||
let r = TakeWeightCredit::should_execute(
|
||||
&Parent.into(),
|
||||
&Some(Parent.into()),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -91,12 +81,12 @@ fn take_weight_credit_barrier_should_work() {
|
||||
#[test]
|
||||
fn allow_unpaid_should_work() {
|
||||
let mut message =
|
||||
opaque::Xcm::TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() };
|
||||
Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]);
|
||||
|
||||
AllowUnpaidFrom::set(vec![Parent.into()]);
|
||||
|
||||
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
||||
&Parachain(1).into(),
|
||||
&Some(Parachain(1).into()),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -105,7 +95,7 @@ fn allow_unpaid_should_work() {
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
||||
&Parent.into(),
|
||||
&Some(Parent.into()),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -119,10 +109,10 @@ fn allow_paid_should_work() {
|
||||
AllowPaidFrom::set(vec![Parent.into()]);
|
||||
|
||||
let mut message =
|
||||
opaque::Xcm::TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() };
|
||||
Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]);
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&Parachain(1).into(),
|
||||
&Some(Parachain(1).into()),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -131,22 +121,14 @@ fn allow_paid_should_work() {
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let fees = (Parent, 1).into();
|
||||
let mut underpaying_message = opaque::Xcm::ReserveAssetDeposited {
|
||||
assets: (Parent, 100).into(),
|
||||
effects: vec![
|
||||
Order::BuyExecution {
|
||||
fees,
|
||||
weight: 0,
|
||||
debt: 20,
|
||||
halt_on_error: true,
|
||||
instructions: vec![],
|
||||
},
|
||||
Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() },
|
||||
],
|
||||
};
|
||||
let mut underpaying_message = Xcm::<()>(vec![
|
||||
ReserveAssetDeposited((Parent, 100).into()),
|
||||
BuyExecution { fees, weight_limit: Limited(20) },
|
||||
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() },
|
||||
]);
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&Parent.into(),
|
||||
&Some(Parent.into()),
|
||||
true,
|
||||
&mut underpaying_message,
|
||||
30,
|
||||
@@ -155,22 +137,14 @@ fn allow_paid_should_work() {
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let fees = (Parent, 1).into();
|
||||
let mut paying_message = opaque::Xcm::ReserveAssetDeposited {
|
||||
assets: (Parent, 100).into(),
|
||||
effects: vec![
|
||||
Order::BuyExecution {
|
||||
fees,
|
||||
weight: 0,
|
||||
debt: 30,
|
||||
halt_on_error: true,
|
||||
instructions: vec![],
|
||||
},
|
||||
Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() },
|
||||
],
|
||||
};
|
||||
let mut paying_message = Xcm::<()>(vec![
|
||||
ReserveAssetDeposited((Parent, 100).into()),
|
||||
BuyExecution { fees, weight_limit: Limited(30) },
|
||||
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() },
|
||||
]);
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&Parachain(1).into(),
|
||||
&Some(Parachain(1).into()),
|
||||
true,
|
||||
&mut paying_message,
|
||||
30,
|
||||
@@ -179,7 +153,7 @@ fn allow_paid_should_work() {
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&Parent.into(),
|
||||
&Some(Parent.into()),
|
||||
true,
|
||||
&mut paying_message,
|
||||
30,
|
||||
@@ -196,23 +170,11 @@ fn paying_reserve_deposit_should_work() {
|
||||
|
||||
let origin = Parent.into();
|
||||
let fees = (Parent, 30).into();
|
||||
let message = Xcm::<TestCall>::ReserveAssetDeposited {
|
||||
assets: (Parent, 100).into(),
|
||||
effects: vec![
|
||||
Order::<TestCall>::BuyExecution {
|
||||
fees,
|
||||
weight: 0,
|
||||
debt: 30,
|
||||
halt_on_error: true,
|
||||
instructions: vec![],
|
||||
},
|
||||
Order::<TestCall>::DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: Here.into(),
|
||||
},
|
||||
],
|
||||
};
|
||||
let message = Xcm(vec![
|
||||
ReserveAssetDeposited((Parent, 100).into()),
|
||||
BuyExecution { fees, weight_limit: Limited(30) },
|
||||
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here.into() },
|
||||
]);
|
||||
let weight_limit = 50;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Complete(30));
|
||||
@@ -228,10 +190,10 @@ fn transfer_should_work() {
|
||||
// They want to transfer 100 of them to their sibling parachain #2
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
Parachain(1).into(),
|
||||
Xcm::TransferAsset {
|
||||
Xcm(vec![TransferAsset {
|
||||
assets: (Here, 100).into(),
|
||||
beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(),
|
||||
},
|
||||
}]),
|
||||
50,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(10));
|
||||
@@ -240,6 +202,132 @@ fn transfer_should_work() {
|
||||
assert_eq!(sent_xcm(), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_should_return_unused_weight() {
|
||||
// we'll let them have message execution for free.
|
||||
AllowUnpaidFrom::set(vec![Here.into()]);
|
||||
// We own 1000 of our tokens.
|
||||
add_asset(3000, (Here, 11));
|
||||
let mut message = Xcm(vec![
|
||||
// First xfer results in an error on the last message only
|
||||
TransferAsset {
|
||||
assets: (Here, 1).into(),
|
||||
beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(),
|
||||
},
|
||||
// Second xfer results in error third message and after
|
||||
TransferAsset {
|
||||
assets: (Here, 2).into(),
|
||||
beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(),
|
||||
},
|
||||
// Third xfer results in error second message and after
|
||||
TransferAsset {
|
||||
assets: (Here, 4).into(),
|
||||
beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(),
|
||||
},
|
||||
]);
|
||||
// Weight limit of 70 is needed.
|
||||
let limit = <TestConfig as Config>::Weigher::weight(&mut message).unwrap();
|
||||
assert_eq!(limit, 30);
|
||||
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(Here.into(), message.clone(), limit);
|
||||
assert_eq!(r, Outcome::Complete(30));
|
||||
assert_eq!(assets(3), vec![(Here, 7).into()]);
|
||||
assert_eq!(assets(3000), vec![(Here, 4).into()]);
|
||||
assert_eq!(sent_xcm(), vec![]);
|
||||
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(Here.into(), message.clone(), limit);
|
||||
assert_eq!(r, Outcome::Incomplete(30, XcmError::NotWithdrawable));
|
||||
assert_eq!(assets(3), vec![(Here, 10).into()]);
|
||||
assert_eq!(assets(3000), vec![(Here, 1).into()]);
|
||||
assert_eq!(sent_xcm(), vec![]);
|
||||
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(Here.into(), message.clone(), limit);
|
||||
assert_eq!(r, Outcome::Incomplete(20, XcmError::NotWithdrawable));
|
||||
assert_eq!(assets(3), vec![(Here, 11).into()]);
|
||||
assert_eq!(assets(3000), vec![]);
|
||||
assert_eq!(sent_xcm(), vec![]);
|
||||
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(Here.into(), message, limit);
|
||||
assert_eq!(r, Outcome::Incomplete(10, XcmError::NotWithdrawable));
|
||||
assert_eq!(assets(3), vec![(Here, 11).into()]);
|
||||
assert_eq!(assets(3000), vec![]);
|
||||
assert_eq!(sent_xcm(), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weight_bounds_should_respect_instructions_limit() {
|
||||
MaxInstructions::set(3);
|
||||
let mut message = Xcm(vec![ClearOrigin; 4]);
|
||||
// 4 instructions are too many.
|
||||
assert_eq!(<TestConfig as Config>::Weigher::weight(&mut message), Err(()));
|
||||
|
||||
let mut message =
|
||||
Xcm(vec![SetErrorHandler(Xcm(vec![ClearOrigin])), SetAppendix(Xcm(vec![ClearOrigin]))]);
|
||||
// 4 instructions are too many, even when hidden within 2.
|
||||
assert_eq!(<TestConfig as Config>::Weigher::weight(&mut message), Err(()));
|
||||
|
||||
let mut message =
|
||||
Xcm(vec![SetErrorHandler(Xcm(vec![SetErrorHandler(Xcm(vec![SetErrorHandler(Xcm(
|
||||
vec![ClearOrigin],
|
||||
))]))]))]);
|
||||
// 4 instructions are too many, even when it's just one that's 3 levels deep.
|
||||
assert_eq!(<TestConfig as Config>::Weigher::weight(&mut message), Err(()));
|
||||
|
||||
let mut message =
|
||||
Xcm(vec![SetErrorHandler(Xcm(vec![SetErrorHandler(Xcm(vec![ClearOrigin]))]))]);
|
||||
// 3 instructions are OK.
|
||||
assert_eq!(<TestConfig as Config>::Weigher::weight(&mut message), Ok(30));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_registers_should_work() {
|
||||
// we'll let them have message execution for free.
|
||||
AllowUnpaidFrom::set(vec![Here.into()]);
|
||||
// We own 1000 of our tokens.
|
||||
add_asset(3000, (Here, 21));
|
||||
let mut message = Xcm(vec![
|
||||
// Set our error handler - this will fire only on the second message, when there's an error
|
||||
SetErrorHandler(Xcm(vec![
|
||||
TransferAsset {
|
||||
assets: (Here, 2).into(),
|
||||
beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(),
|
||||
},
|
||||
// It was handled fine.
|
||||
ClearError,
|
||||
])),
|
||||
// Set the appendix - this will always fire.
|
||||
SetAppendix(Xcm(vec![TransferAsset {
|
||||
assets: (Here, 4).into(),
|
||||
beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(),
|
||||
}])),
|
||||
// First xfer always works ok
|
||||
TransferAsset {
|
||||
assets: (Here, 1).into(),
|
||||
beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(),
|
||||
},
|
||||
// Second xfer results in error on the second message - our error handler will fire.
|
||||
TransferAsset {
|
||||
assets: (Here, 8).into(),
|
||||
beneficiary: X1(AccountIndex64 { index: 3, network: Any }).into(),
|
||||
},
|
||||
]);
|
||||
// Weight limit of 70 is needed.
|
||||
let limit = <TestConfig as Config>::Weigher::weight(&mut message).unwrap();
|
||||
assert_eq!(limit, 70);
|
||||
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(Here.into(), message.clone(), limit);
|
||||
assert_eq!(r, Outcome::Complete(50)); // We don't pay the 20 weight for the error handler.
|
||||
assert_eq!(assets(3), vec![(Here, 13).into()]);
|
||||
assert_eq!(assets(3000), vec![(Here, 8).into()]);
|
||||
assert_eq!(sent_xcm(), vec![]);
|
||||
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(Here.into(), message, limit);
|
||||
assert_eq!(r, Outcome::Complete(70)); // We pay the full weight here.
|
||||
assert_eq!(assets(3), vec![(Here, 20).into()]);
|
||||
assert_eq!(assets(3000), vec![(Here, 1).into()]);
|
||||
assert_eq!(sent_xcm(), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserve_transfer_should_work() {
|
||||
AllowUnpaidFrom::set(vec![X1(Parachain(1)).into()]);
|
||||
@@ -252,15 +340,15 @@ fn reserve_transfer_should_work() {
|
||||
// and let them know to hand it to account #3.
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
Parachain(1).into(),
|
||||
Xcm::TransferReserveAsset {
|
||||
Xcm(vec![TransferReserveAsset {
|
||||
assets: (Here, 100).into(),
|
||||
dest: Parachain(2).into(),
|
||||
effects: vec![Order::DepositAsset {
|
||||
xcm: Xcm::<()>(vec![DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: three.clone(),
|
||||
}],
|
||||
},
|
||||
}]),
|
||||
}]),
|
||||
50,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(10));
|
||||
@@ -270,14 +358,11 @@ fn reserve_transfer_should_work() {
|
||||
sent_xcm(),
|
||||
vec![(
|
||||
Parachain(2).into(),
|
||||
Xcm::ReserveAssetDeposited {
|
||||
assets: (Parent, 100).into(),
|
||||
effects: vec![Order::DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: three
|
||||
}],
|
||||
}
|
||||
Xcm::<()>(vec![
|
||||
ReserveAssetDeposited((Parent, 100).into()),
|
||||
ClearOrigin,
|
||||
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: three },
|
||||
]),
|
||||
)]
|
||||
);
|
||||
}
|
||||
@@ -287,11 +372,11 @@ fn transacting_should_work() {
|
||||
AllowUnpaidFrom::set(vec![Parent.into()]);
|
||||
|
||||
let origin = Parent.into();
|
||||
let message = Xcm::<TestCall>::Transact {
|
||||
let message = Xcm::<TestCall>(vec![Transact {
|
||||
origin_type: OriginKind::Native,
|
||||
require_weight_at_most: 50,
|
||||
call: TestCall::Any(50, None).encode().into(),
|
||||
};
|
||||
}]);
|
||||
let weight_limit = 60;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Complete(60));
|
||||
@@ -302,14 +387,14 @@ fn transacting_should_respect_max_weight_requirement() {
|
||||
AllowUnpaidFrom::set(vec![Parent.into()]);
|
||||
|
||||
let origin = Parent.into();
|
||||
let message = Xcm::<TestCall>::Transact {
|
||||
let message = Xcm::<TestCall>(vec![Transact {
|
||||
origin_type: OriginKind::Native,
|
||||
require_weight_at_most: 40,
|
||||
call: TestCall::Any(50, None).encode().into(),
|
||||
};
|
||||
}]);
|
||||
let weight_limit = 60;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Incomplete(60, XcmError::TooMuchWeightRequired));
|
||||
assert_eq!(r, Outcome::Incomplete(50, XcmError::TooMuchWeightRequired));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -317,11 +402,11 @@ fn transacting_should_refund_weight() {
|
||||
AllowUnpaidFrom::set(vec![Parent.into()]);
|
||||
|
||||
let origin = Parent.into();
|
||||
let message = Xcm::<TestCall>::Transact {
|
||||
let message = Xcm::<TestCall>(vec![Transact {
|
||||
origin_type: OriginKind::Native,
|
||||
require_weight_at_most: 50,
|
||||
call: TestCall::Any(50, Some(30)).encode().into(),
|
||||
};
|
||||
}]);
|
||||
let weight_limit = 60;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Complete(40));
|
||||
@@ -336,32 +421,22 @@ fn paid_transacting_should_refund_payment_for_unused_weight() {
|
||||
|
||||
let origin = one.clone();
|
||||
let fees = (Parent, 100).into();
|
||||
let message = Xcm::<TestCall>::WithdrawAsset {
|
||||
assets: (Parent, 100).into(), // enough for 100 units of weight.
|
||||
effects: vec![
|
||||
Order::<TestCall>::BuyExecution {
|
||||
fees,
|
||||
weight: 70,
|
||||
debt: 30,
|
||||
halt_on_error: true,
|
||||
instructions: vec![Xcm::<TestCall>::Transact {
|
||||
origin_type: OriginKind::Native,
|
||||
require_weight_at_most: 60,
|
||||
// call estimated at 70 but only takes 10.
|
||||
call: TestCall::Any(60, Some(10)).encode().into(),
|
||||
}],
|
||||
},
|
||||
Order::<TestCall>::DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: one.clone(),
|
||||
},
|
||||
],
|
||||
};
|
||||
let message = Xcm::<TestCall>(vec![
|
||||
WithdrawAsset((Parent, 100).into()), // enough for 100 units of weight.
|
||||
BuyExecution { fees, weight_limit: Limited(100) },
|
||||
Transact {
|
||||
origin_type: OriginKind::Native,
|
||||
require_weight_at_most: 50,
|
||||
// call estimated at 50 but only takes 10.
|
||||
call: TestCall::Any(50, Some(10)).encode().into(),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: one.clone() },
|
||||
]);
|
||||
let weight_limit = 100;
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||
assert_eq!(r, Outcome::Complete(50));
|
||||
assert_eq!(assets(1), vec![(Parent, 50).into()]);
|
||||
assert_eq!(r, Outcome::Complete(60));
|
||||
assert_eq!(assets(1), vec![(Parent, 40).into()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -372,7 +447,11 @@ fn prepaid_result_of_query_should_get_free_execution() {
|
||||
expect_response(query_id, origin.clone());
|
||||
|
||||
let the_response = Response::Assets((Parent, 100).into());
|
||||
let message = Xcm::<TestCall>::QueryResponse { query_id, response: the_response.clone() };
|
||||
let message = Xcm::<TestCall>(vec![QueryResponse {
|
||||
query_id,
|
||||
response: the_response.clone(),
|
||||
max_weight: 10,
|
||||
}]);
|
||||
let weight_limit = 10;
|
||||
|
||||
// First time the response gets through since we're expecting it...
|
||||
@@ -382,7 +461,7 @@ fn prepaid_result_of_query_should_get_free_execution() {
|
||||
|
||||
// Second time it doesn't, since we're not.
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin.clone(), message.clone(), weight_limit);
|
||||
assert_eq!(r, Outcome::Incomplete(10, XcmError::Barrier));
|
||||
assert_eq!(r, Outcome::Error(XcmError::Barrier));
|
||||
}
|
||||
|
||||
fn fungible_multi_asset(location: MultiLocation, amount: u128) -> MultiAsset {
|
||||
|
||||
@@ -21,76 +21,43 @@ use frame_support::{
|
||||
use parity_scale_codec::Decode;
|
||||
use sp_runtime::traits::{SaturatedConversion, Saturating, Zero};
|
||||
use sp_std::{convert::TryInto, marker::PhantomData, result::Result};
|
||||
use xcm::latest::{AssetId, AssetId::Concrete, Error, MultiAsset, MultiLocation, Order, Xcm};
|
||||
use xcm::latest::prelude::*;
|
||||
use xcm_executor::{
|
||||
traits::{WeightBounds, WeightTrader},
|
||||
Assets,
|
||||
};
|
||||
|
||||
pub struct FixedWeightBounds<T, C>(PhantomData<(T, C)>);
|
||||
impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeightBounds<T, C> {
|
||||
fn shallow(message: &mut Xcm<C>) -> Result<Weight, ()> {
|
||||
Ok(match message {
|
||||
Xcm::Transact { call, .. } =>
|
||||
call.ensure_decoded()?.get_dispatch_info().weight.saturating_add(T::get()),
|
||||
Xcm::RelayedFrom { ref mut message, .. } =>
|
||||
T::get().saturating_add(Self::shallow(message.as_mut())?),
|
||||
Xcm::WithdrawAsset { effects, .. } |
|
||||
Xcm::ReserveAssetDeposited { effects, .. } |
|
||||
Xcm::ReceiveTeleportedAsset { effects, .. } => {
|
||||
let mut extra = T::get();
|
||||
for order in effects.iter_mut() {
|
||||
extra.saturating_accrue(Self::shallow_order(order)?);
|
||||
}
|
||||
extra
|
||||
},
|
||||
_ => T::get(),
|
||||
})
|
||||
pub struct FixedWeightBounds<T, C, M>(PhantomData<(T, C, M)>);
|
||||
impl<T: Get<Weight>, C: Decode + GetDispatchInfo, M: Get<u32>> WeightBounds<C>
|
||||
for FixedWeightBounds<T, C, M>
|
||||
{
|
||||
fn weight(message: &mut Xcm<C>) -> Result<Weight, ()> {
|
||||
let mut instructions_left = M::get();
|
||||
Self::weight_with_limit(message, &mut instructions_left)
|
||||
}
|
||||
fn deep(message: &mut Xcm<C>) -> Result<Weight, ()> {
|
||||
Ok(match message {
|
||||
Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?,
|
||||
Xcm::WithdrawAsset { effects, .. } |
|
||||
Xcm::ReserveAssetDeposited { effects, .. } |
|
||||
Xcm::ReceiveTeleportedAsset { effects, .. } => {
|
||||
let mut extra = 0;
|
||||
for order in effects.iter_mut() {
|
||||
extra.saturating_accrue(Self::deep_order(order)?);
|
||||
}
|
||||
extra
|
||||
},
|
||||
_ => 0,
|
||||
})
|
||||
fn instr_weight(message: &Instruction<C>) -> Result<Weight, ()> {
|
||||
Self::instr_weight_with_limit(message, &mut u32::max_value())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Get<Weight>, C: Decode + GetDispatchInfo> FixedWeightBounds<T, C> {
|
||||
fn shallow_order(order: &mut Order<C>) -> Result<Weight, ()> {
|
||||
Ok(match order {
|
||||
Order::BuyExecution { .. } => {
|
||||
// On success, execution of this will result in more weight being consumed but
|
||||
// we don't count it here since this is only the *shallow*, non-negotiable weight
|
||||
// spend and doesn't count weight placed behind a `BuyExecution` since it will not
|
||||
// be definitely consumed from any existing weight credit if execution of the message
|
||||
// is attempted.
|
||||
T::get()
|
||||
},
|
||||
_ => T::get(),
|
||||
})
|
||||
impl<T: Get<Weight>, C: Decode + GetDispatchInfo, M> FixedWeightBounds<T, C, M> {
|
||||
fn weight_with_limit(message: &Xcm<C>, instrs_limit: &mut u32) -> Result<Weight, ()> {
|
||||
let mut r = 0;
|
||||
*instrs_limit = instrs_limit.checked_sub(message.0.len() as u32).ok_or(())?;
|
||||
for m in message.0.iter() {
|
||||
r += Self::instr_weight_with_limit(m, instrs_limit)?;
|
||||
}
|
||||
Ok(r)
|
||||
}
|
||||
fn deep_order(order: &mut Order<C>) -> Result<Weight, ()> {
|
||||
Ok(match order {
|
||||
Order::BuyExecution { instructions, .. } => {
|
||||
let mut extra = 0;
|
||||
for instruction in instructions.iter_mut() {
|
||||
extra.saturating_accrue(
|
||||
Self::shallow(instruction)?.saturating_add(Self::deep(instruction)?),
|
||||
);
|
||||
}
|
||||
extra
|
||||
},
|
||||
fn instr_weight_with_limit(
|
||||
message: &Instruction<C>,
|
||||
instrs_limit: &mut u32,
|
||||
) -> Result<Weight, ()> {
|
||||
Ok(T::get().saturating_add(match message {
|
||||
Transact { require_weight_at_most, .. } => *require_weight_at_most,
|
||||
SetErrorHandler(xcm) | SetAppendix(xcm) => Self::weight_with_limit(xcm, instrs_limit)?,
|
||||
_ => 0,
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,10 +91,11 @@ impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> WeightTrader
|
||||
Self(0, 0, PhantomData)
|
||||
}
|
||||
|
||||
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, Error> {
|
||||
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
|
||||
let (id, units_per_second) = T::get();
|
||||
let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128);
|
||||
let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?;
|
||||
let unused =
|
||||
payment.checked_sub((id, amount).into()).map_err(|_| XcmError::TooExpensive)?;
|
||||
self.0 = self.0.saturating_add(weight);
|
||||
self.1 = self.1.saturating_add(amount);
|
||||
Ok(unused)
|
||||
@@ -169,13 +137,14 @@ impl<T: Get<(AssetId, u128)>, R: TakeRevenue> WeightTrader for FixedRateOfFungib
|
||||
Self(0, 0, PhantomData)
|
||||
}
|
||||
|
||||
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, Error> {
|
||||
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
|
||||
let (id, units_per_second) = T::get();
|
||||
let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128);
|
||||
if amount == 0 {
|
||||
return Ok(payment)
|
||||
}
|
||||
let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?;
|
||||
let unused =
|
||||
payment.checked_sub((id, amount).into()).map_err(|_| XcmError::TooExpensive)?;
|
||||
self.0 = self.0.saturating_add(weight);
|
||||
self.1 = self.1.saturating_add(amount);
|
||||
Ok(unused)
|
||||
@@ -228,11 +197,11 @@ impl<
|
||||
Self(0, Zero::zero(), PhantomData)
|
||||
}
|
||||
|
||||
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, Error> {
|
||||
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
|
||||
let amount = WeightToFee::calc(&weight);
|
||||
let u128_amount: u128 = amount.try_into().map_err(|_| Error::Overflow)?;
|
||||
let u128_amount: u128 = amount.try_into().map_err(|_| XcmError::Overflow)?;
|
||||
let required = (Concrete(AssetId::get()), u128_amount).into();
|
||||
let unused = payment.checked_sub(required).map_err(|_| Error::TooExpensive)?;
|
||||
let unused = payment.checked_sub(required).map_err(|_| XcmError::TooExpensive)?;
|
||||
self.0 = self.0.saturating_add(weight);
|
||||
self.1 = self.1.saturating_add(amount);
|
||||
Ok(unused)
|
||||
|
||||
@@ -47,7 +47,7 @@ pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> {
|
||||
}
|
||||
pub struct TestSendXcm;
|
||||
impl SendXcm for TestSendXcm {
|
||||
fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> XcmResult {
|
||||
fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> SendResult {
|
||||
SENT_XCM.with(|q| q.borrow_mut().push((dest, msg)));
|
||||
Ok(())
|
||||
}
|
||||
@@ -150,6 +150,7 @@ pub type Barrier = (
|
||||
parameter_types! {
|
||||
pub const KusamaForStatemint: (MultiAssetFilter, MultiLocation) =
|
||||
(MultiAssetFilter::Wild(WildMultiAsset::AllOf { id: Concrete(MultiLocation::here()), fun: WildFungible }), X1(Parachain(1000)).into());
|
||||
pub const MaxInstructions: u32 = 100;
|
||||
}
|
||||
pub type TrustedTeleporters = (xcm_builder::Case<KusamaForStatemint>,);
|
||||
|
||||
@@ -163,7 +164,7 @@ impl xcm_executor::Config for XcmConfig {
|
||||
type IsTeleporter = TrustedTeleporters;
|
||||
type LocationInverter = LocationInverter<Ancestry>;
|
||||
type Barrier = Barrier;
|
||||
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
|
||||
type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>;
|
||||
type Trader = FixedRateOfFungible<KsmPerSecond, ()>;
|
||||
type ResponseHandler = ();
|
||||
}
|
||||
@@ -181,7 +182,9 @@ impl pallet_xcm::Config for Runtime {
|
||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||
type XcmTeleportFilter = Everything;
|
||||
type XcmReserveTransferFilter = Everything;
|
||||
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
|
||||
type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>;
|
||||
type Call = Call;
|
||||
type Origin = Origin;
|
||||
}
|
||||
|
||||
impl origin::Config for Runtime {}
|
||||
@@ -198,7 +201,7 @@ construct_runtime!(
|
||||
System: frame_system::{Pallet, Call, Storage, Config, Event<T>},
|
||||
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
|
||||
ParasOrigin: origin::{Pallet, Origin},
|
||||
XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event<T>},
|
||||
XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event<T>, Origin},
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
mod mock;
|
||||
|
||||
use frame_support::weights::Weight;
|
||||
use mock::{
|
||||
kusama_like_with_balances, AccountId, Balance, Balances, BaseXcmWeight, XcmConfig, CENTS,
|
||||
};
|
||||
@@ -31,15 +30,8 @@ pub const INITIAL_BALANCE: u128 = 100_000_000_000;
|
||||
pub const REGISTER_AMOUNT: Balance = 10 * CENTS;
|
||||
|
||||
// Construct a `BuyExecution` order.
|
||||
fn buy_execution<C>(debt: Weight) -> Order<C> {
|
||||
use xcm::latest::prelude::*;
|
||||
Order::BuyExecution {
|
||||
fees: (Here, REGISTER_AMOUNT).into(),
|
||||
weight: 0,
|
||||
debt,
|
||||
halt_on_error: false,
|
||||
instructions: vec![],
|
||||
}
|
||||
fn buy_execution<C>() -> Instruction<C> {
|
||||
BuyExecution { fees: (Here, REGISTER_AMOUNT).into(), weight_limit: Unlimited }
|
||||
}
|
||||
|
||||
/// Scenario:
|
||||
@@ -56,17 +48,15 @@ fn withdraw_and_deposit_works() {
|
||||
let weight = 3 * BaseXcmWeight::get();
|
||||
let r = XcmExecutor::<XcmConfig>::execute_xcm(
|
||||
Parachain(PARA_ID).into(),
|
||||
Xcm::WithdrawAsset {
|
||||
assets: vec![(Here, amount).into()].into(),
|
||||
effects: vec![
|
||||
buy_execution(weight),
|
||||
Order::DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: Parachain(other_para_id).into(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Xcm(vec![
|
||||
WithdrawAsset((Here, amount).into()),
|
||||
buy_execution(),
|
||||
DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: Parachain(other_para_id).into(),
|
||||
},
|
||||
]),
|
||||
weight,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(weight));
|
||||
@@ -94,31 +84,31 @@ fn query_holding_works() {
|
||||
let amount = REGISTER_AMOUNT;
|
||||
let query_id = 1234;
|
||||
let weight = 4 * BaseXcmWeight::get();
|
||||
let max_response_weight = 1_000_000_000;
|
||||
let r = XcmExecutor::<XcmConfig>::execute_xcm(
|
||||
Parachain(PARA_ID).into(),
|
||||
Xcm::WithdrawAsset {
|
||||
assets: vec![(Here, amount).into()].into(),
|
||||
effects: vec![
|
||||
buy_execution(weight),
|
||||
Order::DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: OnlyChild.into(), // invalid destination
|
||||
},
|
||||
// is not triggered becasue the deposit fails
|
||||
Order::QueryHolding {
|
||||
query_id,
|
||||
dest: Parachain(PARA_ID).into(),
|
||||
assets: All.into(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Xcm(vec![
|
||||
WithdrawAsset((Here, amount).into()),
|
||||
buy_execution(),
|
||||
DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: OnlyChild.into(), // invalid destination
|
||||
},
|
||||
// is not triggered becasue the deposit fails
|
||||
QueryHolding {
|
||||
query_id,
|
||||
dest: Parachain(PARA_ID).into(),
|
||||
assets: All.into(),
|
||||
max_response_weight,
|
||||
},
|
||||
]),
|
||||
weight,
|
||||
);
|
||||
assert_eq!(
|
||||
r,
|
||||
Outcome::Incomplete(
|
||||
weight,
|
||||
weight - BaseXcmWeight::get(),
|
||||
XcmError::FailedToTransactAsset("AccountIdConversionFailed")
|
||||
)
|
||||
);
|
||||
@@ -129,23 +119,22 @@ fn query_holding_works() {
|
||||
// now do a successful transfer
|
||||
let r = XcmExecutor::<XcmConfig>::execute_xcm(
|
||||
Parachain(PARA_ID).into(),
|
||||
Xcm::WithdrawAsset {
|
||||
assets: vec![(Here, amount).into()].into(),
|
||||
effects: vec![
|
||||
buy_execution(weight),
|
||||
Order::DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: Parachain(other_para_id).into(),
|
||||
},
|
||||
// used to get a notification in case of success
|
||||
Order::QueryHolding {
|
||||
query_id,
|
||||
dest: Parachain(PARA_ID).into(),
|
||||
assets: All.into(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Xcm(vec![
|
||||
WithdrawAsset((Here, amount).into()),
|
||||
buy_execution(),
|
||||
DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: Parachain(other_para_id).into(),
|
||||
},
|
||||
// used to get a notification in case of success
|
||||
QueryHolding {
|
||||
query_id,
|
||||
dest: Parachain(PARA_ID).into(),
|
||||
assets: All.into(),
|
||||
max_response_weight: 1_000_000_000,
|
||||
},
|
||||
]),
|
||||
weight,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(weight));
|
||||
@@ -156,7 +145,11 @@ fn query_holding_works() {
|
||||
mock::sent_xcm(),
|
||||
vec![(
|
||||
Parachain(PARA_ID).into(),
|
||||
Xcm::QueryResponse { query_id, response: Response::Assets(vec![].into()) }
|
||||
Xcm(vec![QueryResponse {
|
||||
query_id,
|
||||
response: Response::Assets(vec![].into()),
|
||||
max_weight: 1_000_000_000,
|
||||
}]),
|
||||
)]
|
||||
);
|
||||
});
|
||||
@@ -180,8 +173,8 @@ fn teleport_to_statemine_works() {
|
||||
let other_para_id = 3000;
|
||||
let amount = REGISTER_AMOUNT;
|
||||
let teleport_effects = vec![
|
||||
buy_execution(5), // unchecked mock value
|
||||
Order::DepositAsset {
|
||||
buy_execution(), // unchecked mock value
|
||||
DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: (1, Parachain(PARA_ID)).into(),
|
||||
@@ -192,17 +185,15 @@ fn teleport_to_statemine_works() {
|
||||
// teleports are allowed to community chains, even in the absence of trust from their side.
|
||||
let r = XcmExecutor::<XcmConfig>::execute_xcm(
|
||||
Parachain(PARA_ID).into(),
|
||||
Xcm::WithdrawAsset {
|
||||
assets: vec![(Here, amount).into()].into(),
|
||||
effects: vec![
|
||||
buy_execution(weight),
|
||||
Order::InitiateTeleport {
|
||||
assets: All.into(),
|
||||
dest: Parachain(other_para_id).into(),
|
||||
effects: teleport_effects.clone(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Xcm(vec![
|
||||
WithdrawAsset((Here, amount).into()),
|
||||
buy_execution(),
|
||||
InitiateTeleport {
|
||||
assets: All.into(),
|
||||
dest: Parachain(other_para_id).into(),
|
||||
xcm: Xcm(teleport_effects.clone()),
|
||||
},
|
||||
]),
|
||||
weight,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(weight));
|
||||
@@ -210,27 +201,25 @@ fn teleport_to_statemine_works() {
|
||||
mock::sent_xcm(),
|
||||
vec![(
|
||||
Parachain(other_para_id).into(),
|
||||
Xcm::ReceiveTeleportedAsset {
|
||||
assets: vec![(Parent, amount).into()].into(),
|
||||
effects: teleport_effects.clone(),
|
||||
}
|
||||
Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin,]
|
||||
.into_iter()
|
||||
.chain(teleport_effects.clone().into_iter())
|
||||
.collect())
|
||||
)]
|
||||
);
|
||||
|
||||
// teleports are allowed from statemine to kusama.
|
||||
let r = XcmExecutor::<XcmConfig>::execute_xcm(
|
||||
Parachain(PARA_ID).into(),
|
||||
Xcm::WithdrawAsset {
|
||||
assets: vec![(Here, amount).into()].into(),
|
||||
effects: vec![
|
||||
buy_execution(weight),
|
||||
Order::InitiateTeleport {
|
||||
assets: All.into(),
|
||||
dest: Parachain(statemine_id).into(),
|
||||
effects: teleport_effects.clone(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Xcm(vec![
|
||||
WithdrawAsset((Here, amount).into()),
|
||||
buy_execution(),
|
||||
InitiateTeleport {
|
||||
assets: All.into(),
|
||||
dest: Parachain(statemine_id).into(),
|
||||
xcm: Xcm(teleport_effects.clone()),
|
||||
},
|
||||
]),
|
||||
weight,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(weight));
|
||||
@@ -241,17 +230,17 @@ fn teleport_to_statemine_works() {
|
||||
vec![
|
||||
(
|
||||
Parachain(other_para_id).into(),
|
||||
Xcm::ReceiveTeleportedAsset {
|
||||
assets: vec![(Parent, amount).into()].into(),
|
||||
effects: teleport_effects.clone(),
|
||||
}
|
||||
Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin,]
|
||||
.into_iter()
|
||||
.chain(teleport_effects.clone().into_iter())
|
||||
.collect()),
|
||||
),
|
||||
(
|
||||
Parachain(statemine_id).into(),
|
||||
Xcm::ReceiveTeleportedAsset {
|
||||
assets: vec![(Parent, amount).into()].into(),
|
||||
effects: teleport_effects,
|
||||
}
|
||||
Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin,]
|
||||
.into_iter()
|
||||
.chain(teleport_effects.clone().into_iter())
|
||||
.collect()),
|
||||
)
|
||||
]
|
||||
);
|
||||
@@ -273,8 +262,8 @@ fn reserve_based_transfer_works() {
|
||||
let other_para_id = 3000;
|
||||
let amount = REGISTER_AMOUNT;
|
||||
let transfer_effects = vec![
|
||||
buy_execution(5), // unchecked mock value
|
||||
Order::DepositAsset {
|
||||
buy_execution(), // unchecked mock value
|
||||
DepositAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
beneficiary: (1, Parachain(PARA_ID)).into(),
|
||||
@@ -283,18 +272,16 @@ fn reserve_based_transfer_works() {
|
||||
let weight = 3 * BaseXcmWeight::get();
|
||||
let r = XcmExecutor::<XcmConfig>::execute_xcm(
|
||||
Parachain(PARA_ID).into(),
|
||||
Xcm::WithdrawAsset {
|
||||
assets: vec![(Here, amount).into()].into(),
|
||||
effects: vec![
|
||||
buy_execution(weight),
|
||||
Order::DepositReserveAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
dest: Parachain(other_para_id).into(),
|
||||
effects: transfer_effects.clone(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Xcm(vec![
|
||||
WithdrawAsset((Here, amount).into()),
|
||||
buy_execution(),
|
||||
DepositReserveAsset {
|
||||
assets: All.into(),
|
||||
max_assets: 1,
|
||||
dest: Parachain(other_para_id).into(),
|
||||
xcm: Xcm(transfer_effects.clone()),
|
||||
},
|
||||
]),
|
||||
weight,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(weight));
|
||||
@@ -303,10 +290,10 @@ fn reserve_based_transfer_works() {
|
||||
mock::sent_xcm(),
|
||||
vec![(
|
||||
Parachain(other_para_id).into(),
|
||||
Xcm::ReserveAssetDeposited {
|
||||
assets: vec![(Parent, amount).into()].into(),
|
||||
effects: transfer_effects,
|
||||
}
|
||||
Xcm(vec![ReserveAssetDeposited((Parent, amount).into()), ClearOrigin,]
|
||||
.into_iter()
|
||||
.chain(transfer_effects.into_iter())
|
||||
.collect())
|
||||
)]
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user