use cumulus_primitives_core::ParaId;
use frame_support::{
pallet_prelude::Get,
traits::{Contains, ContainsPair},
};
use xcm::prelude::*;
use xcm_builder::ensure_is_remote;
frame_support::parameter_types! {
pub LocalLocationPattern: Location = Location::new(0, Here);
pub ParentLocation: Location = Location::parent();
}
pub struct IsForeignConcreteAsset<IsForeign>(core::marker::PhantomData<IsForeign>);
impl<IsForeign: ContainsPair<Location, Location>> ContainsPair<Asset, Location>
for IsForeignConcreteAsset<IsForeign>
{
fn contains(asset: &Asset, origin: &Location) -> bool {
log::trace!(target: "xcm::contains", "IsForeignConcreteAsset asset: {:?}, origin: {:?}", asset, origin);
matches!(asset.id, AssetId(ref id) if IsForeign::contains(id, origin))
}
}
pub struct FromSiblingParachain<SelfParaId, L = Location>(
core::marker::PhantomData<(SelfParaId, L)>,
);
impl<SelfParaId: Get<ParaId>, L: TryFrom<Location> + TryInto<Location> + Clone> ContainsPair<L, L>
for FromSiblingParachain<SelfParaId, L>
{
fn contains(a: &L, b: &L) -> bool {
let a = match ((*a).clone().try_into(), (*b).clone().try_into()) {
(Ok(a), Ok(b)) if a.starts_with(&b) => a, _ => return false,
};
match a.unpack() {
(1, interior) =>
matches!(interior.first(), Some(Parachain(sibling_para_id)) if sibling_para_id.ne(&u32::from(SelfParaId::get()))),
_ => false,
}
}
}
pub struct FromNetwork<UniversalLocation, ExpectedNetworkId, L = Location>(
core::marker::PhantomData<(UniversalLocation, ExpectedNetworkId, L)>,
);
impl<
UniversalLocation: Get<InteriorLocation>,
ExpectedNetworkId: Get<NetworkId>,
L: TryFrom<Location> + TryInto<Location> + Clone,
> ContainsPair<L, L> for FromNetwork<UniversalLocation, ExpectedNetworkId, L>
{
fn contains(a: &L, b: &L) -> bool {
let a = match ((*a).clone().try_into(), (*b).clone().try_into()) {
(Ok(a), Ok(b)) if a.starts_with(&b) => a, _ => return false,
};
let universal_source = UniversalLocation::get();
match ensure_is_remote(universal_source.clone(), a.clone()) {
Ok((network_id, _)) => network_id == ExpectedNetworkId::get(),
Err(e) => {
log::trace!(
target: "xcm::contains",
"FromNetwork origin: {:?} is not remote to the universal_source: {:?} {:?}",
a, universal_source, e
);
false
},
}
}
}
pub struct RemoteAssetFromLocation<AssetsAllowedNetworks, OriginLocation>(
core::marker::PhantomData<(AssetsAllowedNetworks, OriginLocation)>,
);
impl<
L: TryInto<Location> + Clone,
AssetsAllowedNetworks: Contains<Location>,
OriginLocation: Get<Location>,
> ContainsPair<L, L> for RemoteAssetFromLocation<AssetsAllowedNetworks, OriginLocation>
{
fn contains(asset: &L, origin: &L) -> bool {
let Ok(asset) = asset.clone().try_into() else {
return false;
};
let Ok(origin) = origin.clone().try_into() else {
return false;
};
let expected_origin = OriginLocation::get();
if !expected_origin.eq(&origin) {
log::trace!(
target: "xcm::contains",
"RemoteAssetFromLocation asset: {asset:?}, origin: {origin:?} is not from expected {expected_origin:?}"
);
return false;
} else {
log::trace!(
target: "xcm::contains",
"RemoteAssetFromLocation asset: {asset:?}, origin: {origin:?}",
);
}
AssetsAllowedNetworks::contains(&asset)
}
}
impl<AssetsAllowedNetworks: Contains<Location>, OriginLocation: Get<Location>>
ContainsPair<Asset, Location> for RemoteAssetFromLocation<AssetsAllowedNetworks, OriginLocation>
{
fn contains(asset: &Asset, origin: &Location) -> bool {
<Self as ContainsPair<Location, Location>>::contains(&asset.id.0, origin)
}
}
#[cfg(test)]
mod tests {
use super::*;
use frame_support::parameter_types;
use xcm::latest::{ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH};
parameter_types! {
pub UniversalLocation: InteriorLocation = [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(1000)].into();
pub ExpectedNetworkId: NetworkId = ByGenesis(WESTEND_GENESIS_HASH);
}
#[test]
fn from_network_contains_works() {
let asset: Location = (
Parent,
Parent,
GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)),
Parachain(1000),
PalletInstance(1),
GeneralIndex(1),
)
.into();
let origin: Location =
(Parent, Parent, GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000))
.into();
assert!(FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
let asset: Location = (
Parent,
Parent,
GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)),
Parachain(1000),
PalletInstance(1),
GeneralIndex(1),
)
.into();
let origin: Location =
(Parent, Parent, GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(1000))
.into();
assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
let asset: Location = (PalletInstance(1), GeneralIndex(1)).into();
let origin: Location = Here.into();
assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
let asset: Location = (
Parent,
Parent,
GlobalConsensus(Polkadot),
Parachain(1000),
PalletInstance(1),
GeneralIndex(1),
)
.into();
let origin: Location =
(Parent, Parent, GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000))
.into();
assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
let asset: Location = (
Parent,
Parent,
GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)),
Parachain(1000),
PalletInstance(1),
GeneralIndex(1),
)
.into();
let origin: Location = (Parent, Parent, GlobalConsensus(Polkadot), Parachain(1000)).into();
assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
let asset: Location = (
Parent,
Parent,
GlobalConsensus(Polkadot),
Parachain(1000),
PalletInstance(1),
GeneralIndex(1),
)
.into();
let origin: Location = (Parent, Parent, GlobalConsensus(Polkadot), Parachain(1000)).into();
assert!(!FromNetwork::<UniversalLocation, ExpectedNetworkId>::contains(&asset, &origin));
}
}