diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs
index 29e05d42cdf8..4bdcd33290ba 100644
--- a/xcm/xcm-builder/src/lib.rs
+++ b/xcm/xcm-builder/src/lib.rs
@@ -84,7 +84,7 @@ pub use filter_asset_location::{Case, NativeAsset};
mod universal_exports;
pub use universal_exports::{
- BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError, ExporterFor, HaulBlob,
- HaulBlobError, HaulBlobExporter, NetworkExportTable, SovereignPaidRemoteExporter,
- UnpaidLocalExporter, UnpaidRemoteExporter,
+ ensure_is_remote, BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError,
+ ExporterFor, HaulBlob, HaulBlobError, HaulBlobExporter, NetworkExportTable,
+ SovereignPaidRemoteExporter, UnpaidLocalExporter, UnpaidRemoteExporter,
};
diff --git a/xcm/xcm-builder/src/location_conversion.rs b/xcm/xcm-builder/src/location_conversion.rs
index 4239dc17dbdc..66c9fc6dd31d 100644
--- a/xcm/xcm-builder/src/location_conversion.rs
+++ b/xcm/xcm-builder/src/location_conversion.rs
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see .
+use crate::universal_exports::ensure_is_remote;
use frame_support::traits::Get;
use parity_scale_codec::{Decode, Encode};
use sp_io::hashing::blake2_256;
@@ -22,15 +23,15 @@ use sp_std::{borrow::Borrow, marker::PhantomData};
use xcm::latest::prelude::*;
use xcm_executor::traits::Convert;
-/// Prefix for generating alias account for accounts coming
+/// Prefix for generating alias account for accounts coming
/// from chains that use 32 byte long representations.
pub const FOREIGN_CHAIN_PREFIX_PARA_32: [u8; 37] = *b"ForeignChainAliasAccountPrefix_Para32";
-/// Prefix for generating alias account for accounts coming
+/// Prefix for generating alias account for accounts coming
/// from chains that use 20 byte long representations.
pub const FOREIGN_CHAIN_PREFIX_PARA_20: [u8; 37] = *b"ForeignChainAliasAccountPrefix_Para20";
-/// Prefix for generating alias account for accounts coming
+/// Prefix for generating alias account for accounts coming
/// from the relay chain using 32 byte long representations.
pub const FOREIGN_CHAIN_PREFIX_RELAY: [u8; 36] = *b"ForeignChainAliasAccountPrefix_Relay";
@@ -48,7 +49,7 @@ pub const FOREIGN_CHAIN_PREFIX_RELAY: [u8; 36] = *b"ForeignChainAliasAccountPref
/// ```notrust
/// R
/// / \
-/// / \
+/// / \
/// P1 P2
/// / \ / \
/// / \ / \
@@ -252,6 +253,58 @@ impl>, AccountId: From<[u8; 20]> + Into<[u8; 20]>
}
}
+/// Converts a location which is a top-level parachain (i.e. a parachain held on a
+/// Relay-chain which provides its own consensus) into a 32-byte `AccountId`.
+///
+/// This will always result in the *same account ID* being returned for the same
+/// parachain index under the same Relay-chain, regardless of the relative security of
+/// this Relay-chain compared to the local chain.
+///
+/// Note: No distinction is made when the local chain happens to be the parachain in
+/// question or its Relay-chain.
+///
+/// WARNING: This results in the same `AccountId` value being generated regardless
+/// of the relative security of the local chain and the Relay-chain of the input
+/// location. This may not have any immediate security risks, however since it creates
+/// commonalities between chains with different security characteristics, it could
+/// possibly form part of a more sophisticated attack scenario.
+pub struct GlobalConsensusParachainConvertsFor(
+ PhantomData<(UniversalLocation, AccountId)>,
+);
+impl, AccountId: From<[u8; 32]> + Clone>
+ Convert
+ for GlobalConsensusParachainConvertsFor
+{
+ fn convert_ref(location: impl Borrow) -> Result {
+ let universal_source = UniversalLocation::get();
+ log::trace!(
+ target: "xcm::location_conversion",
+ "GlobalConsensusParachainConvertsFor universal_source: {:?}, location: {:?}",
+ universal_source, location.borrow(),
+ );
+ let devolved = ensure_is_remote(universal_source, *location.borrow()).map_err(|_| ())?;
+ let (remote_network, remote_location) = devolved;
+
+ match remote_location {
+ X1(Parachain(remote_network_para_id)) =>
+ Ok(AccountId::from(Self::from_params(&remote_network, &remote_network_para_id))),
+ _ => Err(()),
+ }
+ }
+
+ fn reverse_ref(_: impl Borrow) -> Result {
+ // if this will be needed, we could implement some kind of guessing, if we have configuration for supported networkId+paraId
+ Err(())
+ }
+}
+impl
+ GlobalConsensusParachainConvertsFor
+{
+ fn from_params(network: &NetworkId, para_id: &u32) -> [u8; 32] {
+ (b"glblcnsnss/prchn_", network, para_id).using_encoded(blake2_256)
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -331,6 +384,112 @@ mod tests {
assert_eq!(inverted, Err(()));
}
+ #[test]
+ fn global_consensus_parachain_converts_for_works() {
+ parameter_types! {
+ pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(ByGenesis([9; 32])), Parachain(1234));
+ }
+
+ let test_data = vec![
+ (MultiLocation::parent(), false),
+ (MultiLocation::new(0, X1(Parachain(1000))), false),
+ (MultiLocation::new(1, X1(Parachain(1000))), false),
+ (
+ MultiLocation::new(
+ 2,
+ X3(
+ GlobalConsensus(ByGenesis([0; 32])),
+ Parachain(1000),
+ AccountId32 { network: None, id: [1; 32].into() },
+ ),
+ ),
+ false,
+ ),
+ (MultiLocation::new(2, X1(GlobalConsensus(ByGenesis([0; 32])))), false),
+ (
+ MultiLocation::new(0, X2(GlobalConsensus(ByGenesis([0; 32])), Parachain(1000))),
+ false,
+ ),
+ (
+ MultiLocation::new(1, X2(GlobalConsensus(ByGenesis([0; 32])), Parachain(1000))),
+ false,
+ ),
+ (MultiLocation::new(2, X2(GlobalConsensus(ByGenesis([0; 32])), Parachain(1000))), true),
+ (
+ MultiLocation::new(3, X2(GlobalConsensus(ByGenesis([0; 32])), Parachain(1000))),
+ false,
+ ),
+ (
+ MultiLocation::new(9, X2(GlobalConsensus(ByGenesis([0; 32])), Parachain(1000))),
+ false,
+ ),
+ ];
+
+ for (location, expected_result) in test_data {
+ let result =
+ GlobalConsensusParachainConvertsFor::::convert_ref(
+ &location,
+ );
+ match result {
+ Ok(account) => {
+ assert_eq!(
+ true, expected_result,
+ "expected_result: {}, but conversion passed: {:?}, location: {:?}",
+ expected_result, account, location
+ );
+ match &location {
+ MultiLocation { interior: X2(GlobalConsensus(network), Parachain(para_id)), .. } =>
+ assert_eq!(
+ account,
+ GlobalConsensusParachainConvertsFor::::from_params(network, para_id),
+ "expected_result: {}, but conversion passed: {:?}, location: {:?}", expected_result, account, location
+ ),
+ _ => assert_eq!(
+ true,
+ expected_result,
+ "expected_result: {}, conversion passed: {:?}, but MultiLocation does not match expected pattern, location: {:?}", expected_result, account, location
+ )
+ }
+ },
+ Err(_) => {
+ assert_eq!(
+ false, expected_result,
+ "expected_result: {} - but conversion failed, location: {:?}",
+ expected_result, location
+ );
+ },
+ }
+ }
+
+ // all success
+ let res_gc_a_p1000 =
+ GlobalConsensusParachainConvertsFor::::convert_ref(
+ MultiLocation::new(2, X2(GlobalConsensus(ByGenesis([3; 32])), Parachain(1000))),
+ )
+ .expect("conversion is ok");
+ let res_gc_a_p1001 =
+ GlobalConsensusParachainConvertsFor::::convert_ref(
+ MultiLocation::new(2, X2(GlobalConsensus(ByGenesis([3; 32])), Parachain(1001))),
+ )
+ .expect("conversion is ok");
+ let res_gc_b_p1000 =
+ GlobalConsensusParachainConvertsFor::::convert_ref(
+ MultiLocation::new(2, X2(GlobalConsensus(ByGenesis([4; 32])), Parachain(1000))),
+ )
+ .expect("conversion is ok");
+ let res_gc_b_p1001 =
+ GlobalConsensusParachainConvertsFor::::convert_ref(
+ MultiLocation::new(2, X2(GlobalConsensus(ByGenesis([4; 32])), Parachain(1001))),
+ )
+ .expect("conversion is ok");
+ assert_ne!(res_gc_a_p1000, res_gc_a_p1001);
+ assert_ne!(res_gc_a_p1000, res_gc_b_p1000);
+ assert_ne!(res_gc_a_p1000, res_gc_b_p1001);
+ assert_ne!(res_gc_b_p1000, res_gc_b_p1001);
+ assert_ne!(res_gc_b_p1000, res_gc_a_p1001);
+ assert_ne!(res_gc_b_p1001, res_gc_a_p1001);
+ }
+
#[test]
fn remote_account_convert_on_para_sending_para_32() {
let mul = MultiLocation {
diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs
index a9868021c011..2d1c9f6d4c35 100644
--- a/xcm/xcm-builder/src/universal_exports.rs
+++ b/xcm/xcm-builder/src/universal_exports.rs
@@ -23,7 +23,11 @@ use xcm::prelude::*;
use xcm_executor::traits::{validate_export, ExportXcm};
use SendError::*;
-fn ensure_is_remote(
+/// Returns the network ID and consensus location within that network of the remote
+/// location `dest` which is itself specified as a location relative to the local
+/// 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,
dest: impl Into,
) -> Result<(NetworkId, InteriorMultiLocation), MultiLocation> {