mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-01 02:11:01 +00:00
XCMv4 (#1230)
# Note for reviewer
Most changes are just syntax changes necessary for the new version.
Most important files should be the ones under the `xcm` folder.
# Description
Added XCMv4.
## Removed `Multi` prefix
The following types have been renamed:
- MultiLocation -> Location
- MultiAsset -> Asset
- MultiAssets -> Assets
- InteriorMultiLocation -> InteriorLocation
- MultiAssetFilter -> AssetFilter
- VersionedMultiAsset -> VersionedAsset
- WildMultiAsset -> WildAsset
- VersionedMultiLocation -> VersionedLocation
In order to fix a name conflict, the `Assets` in `xcm-executor` were
renamed to `HoldingAssets`, as they represent assets in holding.
## Removed `Abstract` asset id
It was not being used anywhere and this simplifies the code.
Now assets are just constructed as follows:
```rust
let asset: Asset = (AssetId(Location::new(1, Here)), 100u128).into();
```
No need for specifying `Concrete` anymore.
## Outcome is now a named fields struct
Instead of
```rust
pub enum Outcome {
Complete(Weight),
Incomplete(Weight, Error),
Error(Error),
}
```
we now have
```rust
pub enum Outcome {
Complete { used: Weight },
Incomplete { used: Weight, error: Error },
Error { error: Error },
}
```
## Added Reanchorable trait
Now both locations and assets implement this trait, making it easier to
reanchor both.
## New syntax for building locations and junctions
Now junctions are built using the following methods:
```rust
let location = Location {
parents: 1,
interior: [Parachain(1000), PalletInstance(50), GeneralIndex(1984)].into()
};
```
or
```rust
let location = Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]);
```
And they are matched like so:
```rust
match location.unpack() {
(1, [Parachain(id)]) => ...
(0, Here) => ...,
(1, [_]) => ...,
}
```
This syntax is mandatory in v4, and has been also implemented for v2 and
v3 for easier migration.
This was needed to make all sizes smaller.
# TODO
- [x] Scaffold v4
- [x] Port github.com/paritytech/polkadot/pull/7236
- [x] Remove `Multi` prefix
- [x] Remove `Abstract` asset id
---------
Co-authored-by: command-bot <>
Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
This commit is contained in:
committed by
GitHub
parent
ec7bfae00a
commit
8428f678fe
@@ -28,18 +28,18 @@ use SendError::*;
|
||||
/// chain, itself situated at `universal_local` within the consensus universe. If
|
||||
/// `dest` is not a location in remote consensus, then an error is returned.
|
||||
pub fn ensure_is_remote(
|
||||
universal_local: impl Into<InteriorMultiLocation>,
|
||||
dest: impl Into<MultiLocation>,
|
||||
) -> Result<(NetworkId, InteriorMultiLocation), MultiLocation> {
|
||||
universal_local: impl Into<InteriorLocation>,
|
||||
dest: impl Into<Location>,
|
||||
) -> Result<(NetworkId, InteriorLocation), Location> {
|
||||
let dest = dest.into();
|
||||
let universal_local = universal_local.into();
|
||||
let local_net = match universal_local.global_consensus() {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Err(dest),
|
||||
};
|
||||
let universal_destination: InteriorMultiLocation = universal_local
|
||||
let universal_destination: InteriorLocation = universal_local
|
||||
.into_location()
|
||||
.appended_with(dest)
|
||||
.appended_with(dest.clone())
|
||||
.map_err(|x| x.1)?
|
||||
.try_into()?;
|
||||
let (remote_dest, remote_net) = match universal_destination.split_first() {
|
||||
@@ -59,18 +59,18 @@ pub fn ensure_is_remote(
|
||||
pub struct UnpaidLocalExporter<Exporter, UniversalLocation>(
|
||||
PhantomData<(Exporter, UniversalLocation)>,
|
||||
);
|
||||
impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorMultiLocation>> SendXcm
|
||||
impl<Exporter: ExportXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
|
||||
for UnpaidLocalExporter<Exporter, UniversalLocation>
|
||||
{
|
||||
type Ticket = Exporter::Ticket;
|
||||
|
||||
fn validate(
|
||||
dest: &mut Option<MultiLocation>,
|
||||
dest: &mut Option<Location>,
|
||||
xcm: &mut Option<Xcm<()>>,
|
||||
) -> SendResult<Exporter::Ticket> {
|
||||
let d = dest.take().ok_or(MissingArgument)?;
|
||||
let universal_source = UniversalLocation::get();
|
||||
let devolved = match ensure_is_remote(universal_source, d) {
|
||||
let devolved = match ensure_is_remote(universal_source.clone(), d) {
|
||||
Ok(x) => x,
|
||||
Err(d) => {
|
||||
*dest = Some(d);
|
||||
@@ -96,18 +96,18 @@ pub trait ExporterFor {
|
||||
/// the bridge chain as well as payment for the use of the `ExportMessage` instruction.
|
||||
fn exporter_for(
|
||||
network: &NetworkId,
|
||||
remote_location: &InteriorMultiLocation,
|
||||
remote_location: &InteriorLocation,
|
||||
message: &Xcm<()>,
|
||||
) -> Option<(MultiLocation, Option<MultiAsset>)>;
|
||||
) -> Option<(Location, Option<Asset>)>;
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
impl ExporterFor for Tuple {
|
||||
fn exporter_for(
|
||||
network: &NetworkId,
|
||||
remote_location: &InteriorMultiLocation,
|
||||
remote_location: &InteriorLocation,
|
||||
message: &Xcm<()>,
|
||||
) -> Option<(MultiLocation, Option<MultiAsset>)> {
|
||||
) -> Option<(Location, Option<Asset>)> {
|
||||
for_tuples!( #(
|
||||
if let Some(r) = Tuple::exporter_for(network, remote_location, message) {
|
||||
return Some(r);
|
||||
@@ -125,21 +125,21 @@ pub struct NetworkExportTableItem {
|
||||
/// If `Some`, the requested remote location must be equal to one of the items in the vector.
|
||||
/// These are locations in the remote network.
|
||||
/// If `None`, then the check is skipped.
|
||||
pub remote_location_filter: Option<Vec<InteriorMultiLocation>>,
|
||||
pub remote_location_filter: Option<Vec<InteriorLocation>>,
|
||||
/// Locally-routable bridge with bridging capabilities to the `remote_network` and
|
||||
/// `remote_location`. See [`ExporterFor`] for more details.
|
||||
pub bridge: MultiLocation,
|
||||
pub bridge: Location,
|
||||
/// The local payment.
|
||||
/// See [`ExporterFor`] for more details.
|
||||
pub payment: Option<MultiAsset>,
|
||||
pub payment: Option<Asset>,
|
||||
}
|
||||
|
||||
impl NetworkExportTableItem {
|
||||
pub fn new(
|
||||
remote_network: NetworkId,
|
||||
remote_location_filter: Option<Vec<InteriorMultiLocation>>,
|
||||
bridge: MultiLocation,
|
||||
payment: Option<MultiAsset>,
|
||||
remote_location_filter: Option<Vec<InteriorLocation>>,
|
||||
bridge: Location,
|
||||
payment: Option<Asset>,
|
||||
) -> Self {
|
||||
Self { remote_network, remote_location_filter, bridge, payment }
|
||||
}
|
||||
@@ -152,9 +152,9 @@ pub struct NetworkExportTable<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Get<Vec<NetworkExportTableItem>>> ExporterFor for NetworkExportTable<T> {
|
||||
fn exporter_for(
|
||||
network: &NetworkId,
|
||||
remote_location: &InteriorMultiLocation,
|
||||
remote_location: &InteriorLocation,
|
||||
_: &Xcm<()>,
|
||||
) -> Option<(MultiLocation, Option<MultiAsset>)> {
|
||||
) -> Option<(Location, Option<Asset>)> {
|
||||
T::get()
|
||||
.into_iter()
|
||||
.find(|item| {
|
||||
@@ -194,16 +194,16 @@ pub fn forward_id_for(original_id: &XcmHash) -> XcmHash {
|
||||
pub struct UnpaidRemoteExporter<Bridges, Router, UniversalLocation>(
|
||||
PhantomData<(Bridges, Router, UniversalLocation)>,
|
||||
);
|
||||
impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMultiLocation>> SendXcm
|
||||
impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
|
||||
for UnpaidRemoteExporter<Bridges, Router, UniversalLocation>
|
||||
{
|
||||
type Ticket = Router::Ticket;
|
||||
|
||||
fn validate(
|
||||
dest: &mut Option<MultiLocation>,
|
||||
dest: &mut Option<Location>,
|
||||
msg: &mut Option<Xcm<()>>,
|
||||
) -> SendResult<Router::Ticket> {
|
||||
let d = dest.ok_or(MissingArgument)?;
|
||||
let d = dest.clone().ok_or(MissingArgument)?;
|
||||
let devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|_| NotApplicable)?;
|
||||
let (remote_network, remote_location) = devolved;
|
||||
let xcm = msg.take().ok_or(MissingArgument)?;
|
||||
@@ -261,17 +261,18 @@ impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMulti
|
||||
pub struct SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>(
|
||||
PhantomData<(Bridges, Router, UniversalLocation)>,
|
||||
);
|
||||
impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMultiLocation>> SendXcm
|
||||
impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorLocation>> SendXcm
|
||||
for SovereignPaidRemoteExporter<Bridges, Router, UniversalLocation>
|
||||
{
|
||||
type Ticket = Router::Ticket;
|
||||
|
||||
fn validate(
|
||||
dest: &mut Option<MultiLocation>,
|
||||
dest: &mut Option<Location>,
|
||||
msg: &mut Option<Xcm<()>>,
|
||||
) -> SendResult<Router::Ticket> {
|
||||
let d = *dest.as_ref().ok_or(MissingArgument)?;
|
||||
let devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|_| NotApplicable)?;
|
||||
let d = dest.as_ref().ok_or(MissingArgument)?;
|
||||
let devolved =
|
||||
ensure_is_remote(UniversalLocation::get(), d.clone()).map_err(|_| NotApplicable)?;
|
||||
let (remote_network, remote_location) = devolved;
|
||||
let xcm = msg.take().ok_or(MissingArgument)?;
|
||||
|
||||
@@ -299,7 +300,7 @@ impl<Bridges: ExporterFor, Router: SendXcm, UniversalLocation: Get<InteriorMulti
|
||||
let mut message = Xcm(if let Some(ref payment) = maybe_payment {
|
||||
let fees = payment
|
||||
.clone()
|
||||
.reanchored(&bridge, UniversalLocation::get())
|
||||
.reanchored(&bridge, &UniversalLocation::get())
|
||||
.map_err(|_| Unroutable)?;
|
||||
vec![
|
||||
WithdrawAsset(fees.clone().into()),
|
||||
@@ -366,7 +367,7 @@ pub struct BridgeMessage {
|
||||
/// The message destination as a *Universal Location*. This means it begins with a
|
||||
/// `GlobalConsensus` junction describing the network under which global consensus happens.
|
||||
/// If this does not match our global consensus then it's a fatal error.
|
||||
pub universal_dest: VersionedInteriorMultiLocation,
|
||||
pub universal_dest: VersionedInteriorLocation,
|
||||
pub message: VersionedXcm<()>,
|
||||
}
|
||||
|
||||
@@ -386,8 +387,8 @@ pub struct BridgeBlobDispatcher<Router, OurPlace, OurPlaceBridgeInstance>(
|
||||
);
|
||||
impl<
|
||||
Router: SendXcm,
|
||||
OurPlace: Get<InteriorMultiLocation>,
|
||||
OurPlaceBridgeInstance: Get<Option<InteriorMultiLocation>>,
|
||||
OurPlace: Get<InteriorLocation>,
|
||||
OurPlaceBridgeInstance: Get<Option<InteriorLocation>>,
|
||||
> DispatchBlob for BridgeBlobDispatcher<Router, OurPlace, OurPlaceBridgeInstance>
|
||||
{
|
||||
fn dispatch_blob(blob: Vec<u8>) -> Result<(), DispatchBlobError> {
|
||||
@@ -396,7 +397,7 @@ impl<
|
||||
our_universal.global_consensus().map_err(|()| DispatchBlobError::Unbridgable)?;
|
||||
let BridgeMessage { universal_dest, message } =
|
||||
Decode::decode(&mut &blob[..]).map_err(|_| DispatchBlobError::InvalidEncoding)?;
|
||||
let universal_dest: InteriorMultiLocation = universal_dest
|
||||
let universal_dest: InteriorLocation = universal_dest
|
||||
.try_into()
|
||||
.map_err(|_| DispatchBlobError::UnsupportedLocationVersion)?;
|
||||
// `universal_dest` is the desired destination within the universe: first we need to check
|
||||
@@ -437,9 +438,9 @@ pub struct HaulBlobExporter<Bridge, BridgedNetwork, DestinationVersion, Price>(
|
||||
/// ```
|
||||
impl<
|
||||
Bridge: HaulBlob,
|
||||
BridgedNetwork: Get<MultiLocation>,
|
||||
BridgedNetwork: Get<Location>,
|
||||
DestinationVersion: GetVersion,
|
||||
Price: Get<MultiAssets>,
|
||||
Price: Get<Assets>,
|
||||
> ExportXcm for HaulBlobExporter<Bridge, BridgedNetwork, DestinationVersion, Price>
|
||||
{
|
||||
type Ticket = (Vec<u8>, XcmHash);
|
||||
@@ -447,12 +448,12 @@ impl<
|
||||
fn validate(
|
||||
network: NetworkId,
|
||||
_channel: u32,
|
||||
universal_source: &mut Option<InteriorMultiLocation>,
|
||||
destination: &mut Option<InteriorMultiLocation>,
|
||||
universal_source: &mut Option<InteriorLocation>,
|
||||
destination: &mut Option<InteriorLocation>,
|
||||
message: &mut Option<Xcm<()>>,
|
||||
) -> Result<((Vec<u8>, XcmHash), MultiAssets), SendError> {
|
||||
) -> Result<((Vec<u8>, XcmHash), Assets), SendError> {
|
||||
let (bridged_network, bridged_network_location_parents) = {
|
||||
let MultiLocation { parents, interior: mut junctions } = BridgedNetwork::get();
|
||||
let Location { parents, interior: mut junctions } = BridgedNetwork::get();
|
||||
match junctions.take_first() {
|
||||
Some(GlobalConsensus(network)) => (network, parents),
|
||||
_ => return Err(SendError::NotApplicable),
|
||||
@@ -467,8 +468,8 @@ impl<
|
||||
let (universal_dest, version) =
|
||||
match dest.pushed_front_with(GlobalConsensus(bridged_network)) {
|
||||
Ok(d) => {
|
||||
let version = DestinationVersion::get_version_for(&MultiLocation::from(
|
||||
AncestorThen(bridged_network_location_parents, d),
|
||||
let version = DestinationVersion::get_version_for(&Location::from(
|
||||
AncestorThen(bridged_network_location_parents, d.clone()),
|
||||
))
|
||||
.ok_or(SendError::DestinationUnsupported)?;
|
||||
(d, version)
|
||||
@@ -501,7 +502,7 @@ impl<
|
||||
let message = VersionedXcm::from(message)
|
||||
.into_version(version)
|
||||
.map_err(|()| SendError::DestinationUnsupported)?;
|
||||
let universal_dest = VersionedInteriorMultiLocation::from(universal_dest)
|
||||
let universal_dest = VersionedInteriorLocation::from(universal_dest)
|
||||
.into_version(version)
|
||||
.map_err(|()| SendError::DestinationUnsupported)?;
|
||||
|
||||
@@ -548,10 +549,10 @@ mod tests {
|
||||
type Ticket = ();
|
||||
|
||||
fn validate(
|
||||
_destination: &mut Option<MultiLocation>,
|
||||
_destination: &mut Option<Location>,
|
||||
_message: &mut Option<Xcm<()>>,
|
||||
) -> SendResult<Self::Ticket> {
|
||||
Ok(((), MultiAssets::new()))
|
||||
Ok(((), Assets::new()))
|
||||
}
|
||||
|
||||
fn deliver(_ticket: Self::Ticket) -> Result<XcmHash, SendError> {
|
||||
@@ -562,10 +563,10 @@ mod tests {
|
||||
/// Generic test case asserting that dest and msg is not consumed by `validate` implementation
|
||||
/// of `SendXcm` in case of expected result.
|
||||
fn ensure_validate_does_not_consume_dest_or_msg<S: SendXcm>(
|
||||
dest: MultiLocation,
|
||||
dest: Location,
|
||||
assert_result: impl Fn(SendResult<S::Ticket>),
|
||||
) {
|
||||
let mut dest_wrapper = Some(dest);
|
||||
let mut dest_wrapper = Some(dest.clone());
|
||||
let msg = Xcm::<()>::new();
|
||||
let mut msg_wrapper = Some(msg.clone());
|
||||
|
||||
@@ -580,19 +581,19 @@ mod tests {
|
||||
fn remote_exporters_does_not_consume_dest_or_msg_on_not_applicable() {
|
||||
frame_support::parameter_types! {
|
||||
pub Local: NetworkId = ByGenesis([0; 32]);
|
||||
pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(Local::get()), Parachain(1234));
|
||||
pub UniversalLocation: InteriorLocation = [GlobalConsensus(Local::get()), Parachain(1234)].into();
|
||||
pub DifferentRemote: NetworkId = ByGenesis([22; 32]);
|
||||
// no routers
|
||||
pub BridgeTable: Vec<NetworkExportTableItem> = vec![];
|
||||
}
|
||||
|
||||
// check with local destination (should be remote)
|
||||
let local_dest = (Parent, Parachain(5678)).into();
|
||||
assert!(ensure_is_remote(UniversalLocation::get(), local_dest).is_err());
|
||||
let local_dest: Location = (Parent, Parachain(5678)).into();
|
||||
assert!(ensure_is_remote(UniversalLocation::get(), local_dest.clone()).is_err());
|
||||
|
||||
ensure_validate_does_not_consume_dest_or_msg::<
|
||||
UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, OkSender, UniversalLocation>,
|
||||
>(local_dest, |result| assert_eq!(Err(NotApplicable), result));
|
||||
>(local_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
|
||||
|
||||
ensure_validate_does_not_consume_dest_or_msg::<
|
||||
SovereignPaidRemoteExporter<
|
||||
@@ -603,12 +604,12 @@ mod tests {
|
||||
>(local_dest, |result| assert_eq!(Err(NotApplicable), result));
|
||||
|
||||
// check with not applicable destination
|
||||
let remote_dest = (Parent, Parent, DifferentRemote::get()).into();
|
||||
assert!(ensure_is_remote(UniversalLocation::get(), remote_dest).is_ok());
|
||||
let remote_dest: Location = (Parent, Parent, DifferentRemote::get()).into();
|
||||
assert!(ensure_is_remote(UniversalLocation::get(), remote_dest.clone()).is_ok());
|
||||
|
||||
ensure_validate_does_not_consume_dest_or_msg::<
|
||||
UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, OkSender, UniversalLocation>,
|
||||
>(remote_dest, |result| assert_eq!(Err(NotApplicable), result));
|
||||
>(remote_dest.clone(), |result| assert_eq!(Err(NotApplicable), result));
|
||||
|
||||
ensure_validate_does_not_consume_dest_or_msg::<
|
||||
SovereignPaidRemoteExporter<
|
||||
@@ -623,15 +624,15 @@ mod tests {
|
||||
fn network_export_table_works() {
|
||||
frame_support::parameter_types! {
|
||||
pub NetworkA: NetworkId = ByGenesis([0; 32]);
|
||||
pub Parachain1000InNetworkA: InteriorMultiLocation = X1(Parachain(1000));
|
||||
pub Parachain2000InNetworkA: InteriorMultiLocation = X1(Parachain(2000));
|
||||
pub Parachain1000InNetworkA: InteriorLocation = [Parachain(1000)].into();
|
||||
pub Parachain2000InNetworkA: InteriorLocation = [Parachain(2000)].into();
|
||||
|
||||
pub NetworkB: NetworkId = ByGenesis([1; 32]);
|
||||
|
||||
pub BridgeToALocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1234)));
|
||||
pub BridgeToBLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(4321)));
|
||||
pub BridgeToALocation: Location = Location::new(1, [Parachain(1234)]);
|
||||
pub BridgeToBLocation: Location = Location::new(1, [Parachain(4321)]);
|
||||
|
||||
pub PaymentForNetworkAAndParachain2000: MultiAsset = (MultiLocation::parent(), 150).into();
|
||||
pub PaymentForNetworkAAndParachain2000: Asset = (Location::parent(), 150).into();
|
||||
|
||||
pub BridgeTable: sp_std::vec::Vec<NetworkExportTableItem> = sp_std::vec![
|
||||
// NetworkA allows `Parachain(1000)` as remote location WITHOUT payment.
|
||||
@@ -658,19 +659,19 @@ mod tests {
|
||||
];
|
||||
}
|
||||
|
||||
let test_data = vec![
|
||||
(NetworkA::get(), X1(Parachain(1000)), Some((BridgeToALocation::get(), None))),
|
||||
(NetworkA::get(), X2(Parachain(1000), GeneralIndex(1)), None),
|
||||
let test_data: Vec<(NetworkId, InteriorLocation, Option<(Location, Option<Asset>)>)> = vec![
|
||||
(NetworkA::get(), [Parachain(1000)].into(), Some((BridgeToALocation::get(), None))),
|
||||
(NetworkA::get(), [Parachain(1000), GeneralIndex(1)].into(), None),
|
||||
(
|
||||
NetworkA::get(),
|
||||
X1(Parachain(2000)),
|
||||
[Parachain(2000)].into(),
|
||||
Some((BridgeToALocation::get(), Some(PaymentForNetworkAAndParachain2000::get()))),
|
||||
),
|
||||
(NetworkA::get(), X2(Parachain(2000), GeneralIndex(1)), None),
|
||||
(NetworkA::get(), X1(Parachain(3000)), None),
|
||||
(NetworkB::get(), X1(Parachain(1000)), Some((BridgeToBLocation::get(), None))),
|
||||
(NetworkB::get(), X1(Parachain(2000)), Some((BridgeToBLocation::get(), None))),
|
||||
(NetworkB::get(), X1(Parachain(3000)), Some((BridgeToBLocation::get(), None))),
|
||||
(NetworkA::get(), [Parachain(2000), GeneralIndex(1)].into(), None),
|
||||
(NetworkA::get(), [Parachain(3000)].into(), None),
|
||||
(NetworkB::get(), [Parachain(1000)].into(), Some((BridgeToBLocation::get(), None))),
|
||||
(NetworkB::get(), [Parachain(2000)].into(), Some((BridgeToBLocation::get(), None))),
|
||||
(NetworkB::get(), [Parachain(3000)].into(), Some((BridgeToBLocation::get(), None))),
|
||||
];
|
||||
|
||||
for (network, remote_location, expected_result) in test_data {
|
||||
|
||||
Reference in New Issue
Block a user