mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 14:01:02 +00:00
XCM: Allow reclaim of assets dropped from holding (#3727)
* XCM: Introduce AssetTrap * Revert reversions * Remove attempts at weighing and add test * Less storage use for asset trapping * Add missing file * Fixes * Fixes * Formatting * Fixes * Docs * Filter types to allow runtimes to dictate which assets/origins should be trapped * Formatting * Tests * Formatting * Fixes * Docs
This commit is contained in:
@@ -30,7 +30,7 @@ use xcm_executor::traits::{OnResponse, ShouldExecute};
|
||||
pub struct TakeWeightCredit;
|
||||
impl ShouldExecute for TakeWeightCredit {
|
||||
fn should_execute<Call>(
|
||||
_origin: &Option<MultiLocation>,
|
||||
_origin: &MultiLocation,
|
||||
_top_level: bool,
|
||||
_message: &mut Xcm<Call>,
|
||||
max_weight: Weight,
|
||||
@@ -49,19 +49,21 @@ impl ShouldExecute for TakeWeightCredit {
|
||||
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
|
||||
impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
|
||||
fn should_execute<Call>(
|
||||
origin: &Option<MultiLocation>,
|
||||
origin: &MultiLocation,
|
||||
top_level: bool,
|
||||
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, ());
|
||||
let mut iter = message.0.iter_mut();
|
||||
let i = iter.next().ok_or(())?;
|
||||
match i {
|
||||
ReceiveTeleportedAsset(..) | WithdrawAsset(..) | ReserveAssetDeposited(..) => (),
|
||||
ReceiveTeleportedAsset(..) |
|
||||
WithdrawAsset(..) |
|
||||
ReserveAssetDeposited(..) |
|
||||
ClaimAsset { .. } => (),
|
||||
_ => return Err(()),
|
||||
}
|
||||
let mut i = iter.next().ok_or(())?;
|
||||
@@ -87,13 +89,12 @@ 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: &Option<MultiLocation>,
|
||||
origin: &MultiLocation,
|
||||
_top_level: bool,
|
||||
_message: &mut Xcm<Call>,
|
||||
_max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
let origin = origin.as_ref().ok_or(())?;
|
||||
ensure!(T::contains(origin), ());
|
||||
Ok(())
|
||||
}
|
||||
@@ -115,13 +116,12 @@ 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: &Option<MultiLocation>,
|
||||
origin: &MultiLocation,
|
||||
_top_level: bool,
|
||||
message: &mut Xcm<Call>,
|
||||
_max_weight: Weight,
|
||||
_weight_credit: &mut Weight,
|
||||
) -> Result<(), ()> {
|
||||
let origin = origin.as_ref().ok_or(())?;
|
||||
match message.0.first() {
|
||||
Some(QueryResponse { query_id, .. })
|
||||
if ResponseHandler::expecting_response(origin, *query_id) =>
|
||||
|
||||
@@ -35,6 +35,7 @@ pub use sp_std::{
|
||||
marker::PhantomData,
|
||||
};
|
||||
pub use xcm::latest::prelude::*;
|
||||
use xcm_executor::traits::{ClaimAssets, DropAssets};
|
||||
pub use xcm_executor::{
|
||||
traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset},
|
||||
Assets, Config,
|
||||
@@ -269,6 +270,37 @@ pub type TestBarrier = (
|
||||
AllowUnpaidExecutionFrom<IsInVec<AllowUnpaidFrom>>,
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
pub static TrappedAssets: Vec<(MultiLocation, MultiAssets)> = vec![];
|
||||
}
|
||||
|
||||
pub struct TestAssetTrap;
|
||||
|
||||
impl DropAssets for TestAssetTrap {
|
||||
fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight {
|
||||
let mut t: Vec<(MultiLocation, MultiAssets)> = TrappedAssets::get();
|
||||
t.push((origin.clone(), assets.into()));
|
||||
TrappedAssets::set(t);
|
||||
5
|
||||
}
|
||||
}
|
||||
|
||||
impl ClaimAssets for TestAssetTrap {
|
||||
fn claim_assets(origin: &MultiLocation, ticket: &MultiLocation, what: &MultiAssets) -> bool {
|
||||
let mut t: Vec<(MultiLocation, MultiAssets)> = TrappedAssets::get();
|
||||
if let (0, X1(GeneralIndex(i))) = (ticket.parents, &ticket.interior) {
|
||||
if let Some((l, a)) = t.get(*i as usize) {
|
||||
if l == origin && a == what {
|
||||
t.swap_remove(*i as usize);
|
||||
TrappedAssets::set(t);
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestConfig;
|
||||
impl Config for TestConfig {
|
||||
type Call = TestCall;
|
||||
@@ -282,4 +314,6 @@ impl Config for TestConfig {
|
||||
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall, MaxInstructions>;
|
||||
type Trader = FixedRateOfFungible<WeightPrice, ()>;
|
||||
type ResponseHandler = TestResponseHandler;
|
||||
type AssetTrap = TestAssetTrap;
|
||||
type AssetClaims = TestAssetTrap;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ fn take_weight_credit_barrier_should_work() {
|
||||
Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]);
|
||||
let mut weight_credit = 10;
|
||||
let r = TakeWeightCredit::should_execute(
|
||||
&Some(Parent.into()),
|
||||
&Parent.into(),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -68,7 +68,7 @@ fn take_weight_credit_barrier_should_work() {
|
||||
assert_eq!(weight_credit, 0);
|
||||
|
||||
let r = TakeWeightCredit::should_execute(
|
||||
&Some(Parent.into()),
|
||||
&Parent.into(),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -86,7 +86,7 @@ fn allow_unpaid_should_work() {
|
||||
AllowUnpaidFrom::set(vec![Parent.into()]);
|
||||
|
||||
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
||||
&Some(Parachain(1).into()),
|
||||
&Parachain(1).into(),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -95,7 +95,7 @@ fn allow_unpaid_should_work() {
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let r = AllowUnpaidExecutionFrom::<IsInVec<AllowUnpaidFrom>>::should_execute(
|
||||
&Some(Parent.into()),
|
||||
&Parent.into(),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -112,7 +112,7 @@ fn allow_paid_should_work() {
|
||||
Xcm::<()>(vec![TransferAsset { assets: (Parent, 100).into(), beneficiary: Here.into() }]);
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&Some(Parachain(1).into()),
|
||||
&Parachain(1).into(),
|
||||
true,
|
||||
&mut message,
|
||||
10,
|
||||
@@ -128,7 +128,7 @@ fn allow_paid_should_work() {
|
||||
]);
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&Some(Parent.into()),
|
||||
&Parent.into(),
|
||||
true,
|
||||
&mut underpaying_message,
|
||||
30,
|
||||
@@ -144,7 +144,7 @@ fn allow_paid_should_work() {
|
||||
]);
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&Some(Parachain(1).into()),
|
||||
&Parachain(1).into(),
|
||||
true,
|
||||
&mut paying_message,
|
||||
30,
|
||||
@@ -153,7 +153,7 @@ fn allow_paid_should_work() {
|
||||
assert_eq!(r, Err(()));
|
||||
|
||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||
&Some(Parent.into()),
|
||||
&Parent.into(),
|
||||
true,
|
||||
&mut paying_message,
|
||||
30,
|
||||
@@ -202,6 +202,119 @@ fn transfer_should_work() {
|
||||
assert_eq!(sent_xcm(), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_asset_trap_should_work() {
|
||||
// we'll let them have message execution for free.
|
||||
AllowUnpaidFrom::set(vec![X1(Parachain(1)).into(), X1(Parachain(2)).into()]);
|
||||
|
||||
// Child parachain #1 owns 1000 tokens held by us in reserve.
|
||||
add_asset(1001, (Here, 1000));
|
||||
// They want to transfer 100 of them to their sibling parachain #2 but have a problem
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
Parachain(1).into(),
|
||||
Xcm(vec![
|
||||
WithdrawAsset((Here, 100).into()),
|
||||
DepositAsset {
|
||||
assets: Wild(All),
|
||||
max_assets: 0, //< Whoops!
|
||||
beneficiary: AccountIndex64 { index: 3, network: Any }.into(),
|
||||
},
|
||||
]),
|
||||
20,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(25));
|
||||
assert_eq!(assets(1001), vec![(Here, 900).into()]);
|
||||
assert_eq!(assets(3), vec![]);
|
||||
|
||||
// Incorrect ticket doesn't work.
|
||||
let old_trapped_assets = TrappedAssets::get();
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
Parachain(1).into(),
|
||||
Xcm(vec![
|
||||
ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(1).into() },
|
||||
DepositAsset {
|
||||
assets: Wild(All),
|
||||
max_assets: 1,
|
||||
beneficiary: AccountIndex64 { index: 3, network: Any }.into(),
|
||||
},
|
||||
]),
|
||||
20,
|
||||
);
|
||||
assert_eq!(r, Outcome::Incomplete(10, XcmError::UnknownClaim));
|
||||
assert_eq!(assets(1001), vec![(Here, 900).into()]);
|
||||
assert_eq!(assets(3), vec![]);
|
||||
assert_eq!(old_trapped_assets, TrappedAssets::get());
|
||||
|
||||
// Incorrect origin doesn't work.
|
||||
let old_trapped_assets = TrappedAssets::get();
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
Parachain(2).into(),
|
||||
Xcm(vec![
|
||||
ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(0).into() },
|
||||
DepositAsset {
|
||||
assets: Wild(All),
|
||||
max_assets: 1,
|
||||
beneficiary: AccountIndex64 { index: 3, network: Any }.into(),
|
||||
},
|
||||
]),
|
||||
20,
|
||||
);
|
||||
assert_eq!(r, Outcome::Incomplete(10, XcmError::UnknownClaim));
|
||||
assert_eq!(assets(1001), vec![(Here, 900).into()]);
|
||||
assert_eq!(assets(3), vec![]);
|
||||
assert_eq!(old_trapped_assets, TrappedAssets::get());
|
||||
|
||||
// Incorrect assets doesn't work.
|
||||
let old_trapped_assets = TrappedAssets::get();
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
Parachain(1).into(),
|
||||
Xcm(vec![
|
||||
ClaimAsset { assets: (Here, 101).into(), ticket: GeneralIndex(0).into() },
|
||||
DepositAsset {
|
||||
assets: Wild(All),
|
||||
max_assets: 1,
|
||||
beneficiary: AccountIndex64 { index: 3, network: Any }.into(),
|
||||
},
|
||||
]),
|
||||
20,
|
||||
);
|
||||
assert_eq!(r, Outcome::Incomplete(10, XcmError::UnknownClaim));
|
||||
assert_eq!(assets(1001), vec![(Here, 900).into()]);
|
||||
assert_eq!(assets(3), vec![]);
|
||||
assert_eq!(old_trapped_assets, TrappedAssets::get());
|
||||
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
Parachain(1).into(),
|
||||
Xcm(vec![
|
||||
ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(0).into() },
|
||||
DepositAsset {
|
||||
assets: Wild(All),
|
||||
max_assets: 1,
|
||||
beneficiary: AccountIndex64 { index: 3, network: Any }.into(),
|
||||
},
|
||||
]),
|
||||
20,
|
||||
);
|
||||
assert_eq!(r, Outcome::Complete(20));
|
||||
assert_eq!(assets(1001), vec![(Here, 900).into()]);
|
||||
assert_eq!(assets(3), vec![(Here, 100).into()]);
|
||||
|
||||
// Same again doesn't work :-)
|
||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||
Parachain(1).into(),
|
||||
Xcm(vec![
|
||||
ClaimAsset { assets: (Here, 100).into(), ticket: GeneralIndex(0).into() },
|
||||
DepositAsset {
|
||||
assets: Wild(All),
|
||||
max_assets: 1,
|
||||
beneficiary: AccountIndex64 { index: 3, network: Any }.into(),
|
||||
},
|
||||
]),
|
||||
20,
|
||||
);
|
||||
assert_eq!(r, Outcome::Incomplete(10, XcmError::UnknownClaim));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_should_return_unused_weight() {
|
||||
// we'll let them have message execution for free.
|
||||
|
||||
@@ -166,7 +166,9 @@ impl xcm_executor::Config for XcmConfig {
|
||||
type Barrier = Barrier;
|
||||
type Weigher = FixedWeightBounds<BaseXcmWeight, Call, MaxInstructions>;
|
||||
type Trader = FixedRateOfFungible<KsmPerSecond, ()>;
|
||||
type ResponseHandler = ();
|
||||
type ResponseHandler = XcmPallet;
|
||||
type AssetTrap = XcmPallet;
|
||||
type AssetClaims = XcmPallet;
|
||||
}
|
||||
|
||||
pub type LocalOriginToLocation = SignedToAccountId32<Origin, AccountId, KusamaNetwork>;
|
||||
|
||||
Reference in New Issue
Block a user