Skip to content

Commit

Permalink
pallet-xcm: use XcmTeleportFilter for teleported fees in reserve tran…
Browse files Browse the repository at this point in the history
…sfers (#2322)

Disallow reserve transfers that use teleportable fees if `(origin,
fees)` matches `XcmTeleportFilter`.

Add regression tests for filtering based on `XcmTeleportFilter` for both
`(limited_)reserve_transfer_assets()` and `(limited_)teleport_assets`
extrinsics.
  • Loading branch information
acatangiu authored Nov 15, 2023
1 parent f517900 commit f536043
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 3 deletions.
6 changes: 5 additions & 1 deletion polkadot/xcm/pallet-xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,7 @@ impl<T: Config> Pallet<T> {
TransferType::DestinationReserve =>
Self::destination_reserve_fees_instructions(dest, fees, weight_limit)?,
TransferType::Teleport =>
Self::teleport_fees_instructions(dest, fees, weight_limit)?,
Self::teleport_fees_instructions(origin_location, dest, fees, weight_limit)?,
TransferType::RemoteReserve(_) =>
return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
});
Expand Down Expand Up @@ -1715,10 +1715,14 @@ impl<T: Config> Pallet<T> {
}

fn teleport_fees_instructions(
origin: MultiLocation,
dest: MultiLocation,
fees: MultiAsset,
weight_limit: WeightLimit,
) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
let value = (origin, vec![fees.clone()]);
ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);

let context = T::UniversalLocation::get();
let reanchored_fees = fees
.clone()
Expand Down
29 changes: 27 additions & 2 deletions polkadot/xcm/pallet-xcm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use codec::Encode;
use frame_support::{
construct_runtime, match_types, parameter_types,
traits::{
AsEnsureOriginWithArg, ConstU128, ConstU32, Equals, Everything, EverythingBut, Nothing,
AsEnsureOriginWithArg, ConstU128, ConstU32, Contains, Equals, Everything, EverythingBut,
Nothing,
},
weights::Weight,
};
Expand Down Expand Up @@ -341,6 +342,9 @@ pub const USDT_PARA_ID: u32 = 2003;
// This child parachain is not configured as trusted reserve or teleport location for any assets.
pub const OTHER_PARA_ID: u32 = 2009;

// This child parachain is used for filtered/disallowed assets.
pub const FILTERED_PARA_ID: u32 = 2010;

parameter_types! {
pub const RelayLocation: MultiLocation = Here.into_location();
pub const NativeAsset: MultiAsset = MultiAsset {
Expand Down Expand Up @@ -384,6 +388,17 @@ parameter_types! {
interior: X1(Parachain(USDT_PARA_ID)),
}),
};
pub const FilteredTeleportLocation: MultiLocation = MultiLocation {
parents: 0,
interior: X1(Parachain(FILTERED_PARA_ID))
};
pub const FilteredTeleportAsset: MultiAsset = MultiAsset {
fun: Fungible(10),
id: Concrete(MultiLocation {
parents: 0,
interior: X1(Parachain(FILTERED_PARA_ID)),
}),
};
pub const AnyNetwork: Option<NetworkId> = None;
pub UniversalLocation: InteriorMultiLocation = Here;
pub UnitWeightCost: u64 = 1_000;
Expand Down Expand Up @@ -430,6 +445,7 @@ parameter_types! {
pub TrustedLocal: (MultiAssetFilter, MultiLocation) = (All.into(), Here.into());
pub TrustedSystemPara: (MultiAssetFilter, MultiLocation) = (NativeAsset::get().into(), SystemParachainLocation::get());
pub TrustedUsdt: (MultiAssetFilter, MultiLocation) = (Usdt::get().into(), UsdtTeleportLocation::get());
pub TrustedFilteredTeleport: (MultiAssetFilter, MultiLocation) = (FilteredTeleportAsset::get().into(), FilteredTeleportLocation::get());
pub TeleportUsdtToForeign: (MultiAssetFilter, MultiLocation) = (Usdt::get().into(), ForeignReserveLocation::get());
pub TrustedForeign: (MultiAssetFilter, MultiLocation) = (ForeignAsset::get().into(), ForeignReserveLocation::get());
pub TrustedUsdc: (MultiAssetFilter, MultiLocation) = (Usdc::get().into(), UsdcReserveLocation::get());
Expand Down Expand Up @@ -466,6 +482,7 @@ impl xcm_executor::Config for XcmConfig {
Case<TrustedSystemPara>,
Case<TrustedUsdt>,
Case<TeleportUsdtToForeign>,
Case<TrustedFilteredTeleport>,
);
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
Expand Down Expand Up @@ -496,14 +513,22 @@ parameter_types! {
pub static AdvertisedXcmVersion: pallet_xcm::XcmVersion = 3;
}

pub struct XcmTeleportFiltered;
impl Contains<(MultiLocation, Vec<MultiAsset>)> for XcmTeleportFiltered {
fn contains(t: &(MultiLocation, Vec<MultiAsset>)) -> bool {
let filtered = FilteredTeleportAsset::get();
t.1.iter().any(|asset| asset == &filtered)
}
}

impl pallet_xcm::Config for Test {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmRouter = XcmRouter;
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmExecuteFilter = Everything;
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = Everything;
type XcmTeleportFilter = EverythingBut<XcmTeleportFiltered>;
type XcmReserveTransferFilter = Everything;
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
type UniversalLocation = UniversalLocation;
Expand Down
54 changes: 54 additions & 0 deletions polkadot/xcm/pallet-xcm/src/tests/assets_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,30 @@ fn limited_teleport_assets_works() {
);
}

/// `limited_teleport_assets` should fail for filtered assets
#[test]
fn limited_teleport_filtered_assets_disallowed() {
let beneficiary: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into();
new_test_ext_with_balances(vec![(ALICE, INITIAL_BALANCE)]).execute_with(|| {
let result = XcmPallet::limited_teleport_assets(
RuntimeOrigin::signed(ALICE),
Box::new(FilteredTeleportLocation::get().into()),
Box::new(beneficiary.into()),
Box::new(FilteredTeleportAsset::get().into()),
0,
Unlimited,
);
assert_eq!(
result,
Err(DispatchError::Module(ModuleError {
index: 4,
error: [2, 0, 0, 0],
message: Some("Filtered")
}))
);
});
}

/// Test `reserve_transfer_assets_with_paid_router_works`
///
/// Asserts that the sender's balance is decreased and the beneficiary's balance
Expand Down Expand Up @@ -1403,3 +1427,33 @@ fn reserve_transfer_assets_with_teleportable_asset_fails() {
assert_eq!(Assets::active_issuance(usdt_id_multilocation), usdt_initial_local_amount);
});
}

/// Test `reserve_transfer_assets` with teleportable fee that is filtered - should fail.
#[test]
fn reserve_transfer_assets_with_filtered_teleported_fee_disallowed() {
let beneficiary: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into();
new_test_ext_with_balances(vec![(ALICE, INITIAL_BALANCE)]).execute_with(|| {
let (assets, fee_index, _, _) = into_multiassets_checked(
// FilteredTeleportAsset for fees - teleportable but filtered
FilteredTeleportAsset::get().into(),
// native asset to transfer (not used for fees) - local reserve
(MultiLocation::here(), SEND_AMOUNT).into(),
);
let result = XcmPallet::limited_reserve_transfer_assets(
RuntimeOrigin::signed(ALICE),
Box::new(FilteredTeleportLocation::get().into()),
Box::new(beneficiary.into()),
Box::new(assets.into()),
fee_index as u32,
Unlimited,
);
assert_eq!(
result,
Err(DispatchError::Module(ModuleError {
index: 4,
error: [2, 0, 0, 0],
message: Some("Filtered")
}))
);
});
}

0 comments on commit f536043

Please sign in to comment.