From a5f4c2dfeae4fcb20aee4b94f797be84261b1d73 Mon Sep 17 00:00:00 2001
From: Branislav Kontur <bkontur@gmail.com>
Date: Tue, 16 May 2023 10:37:31 +0200
Subject: [PATCH] [xcm] Foreign global consensus parachain LocationToAccountId
 converter (#7016)

* **Foreign** global consensus parachain LocationToAccount converter

* Review fix

* Added `UniversalLocation` + `ensure_is_remote` handling

* Added unique id to encoding

* Update xcm/xcm-builder/src/location_conversion.rs

Co-authored-by: Gavin Wood <gavin@parity.io>

* Re-export `ensure_is_remote`

* Test

* fmt

* Update xcm/xcm-builder/src/location_conversion.rs

* Update xcm/xcm-builder/src/universal_exports.rs

Co-authored-by: Gavin Wood <gavin@parity.io>

---------

Co-authored-by: parity-processbot <>
Co-authored-by: Gavin Wood <gavin@parity.io>
---
 polkadot/xcm/xcm-builder/src/lib.rs           |   6 +-
 .../xcm-builder/src/location_conversion.rs    | 167 +++++++++++++++++-
 .../xcm/xcm-builder/src/universal_exports.rs  |   6 +-
 3 files changed, 171 insertions(+), 8 deletions(-)

diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs
index 29e05d42cdf..4bdcd33290b 100644
--- a/polkadot/xcm/xcm-builder/src/lib.rs
+++ b/polkadot/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/polkadot/xcm/xcm-builder/src/location_conversion.rs b/polkadot/xcm/xcm-builder/src/location_conversion.rs
index 4239dc17dbd..66c9fc6dd31 100644
--- a/polkadot/xcm/xcm-builder/src/location_conversion.rs
+++ b/polkadot/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 <http://www.gnu.org/licenses/>.
 
+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<Network: Get<Option<NetworkId>>, 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<UniversalLocation, AccountId>(
+	PhantomData<(UniversalLocation, AccountId)>,
+);
+impl<UniversalLocation: Get<InteriorMultiLocation>, AccountId: From<[u8; 32]> + Clone>
+	Convert<MultiLocation, AccountId>
+	for GlobalConsensusParachainConvertsFor<UniversalLocation, AccountId>
+{
+	fn convert_ref(location: impl Borrow<MultiLocation>) -> Result<AccountId, ()> {
+		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<AccountId>) -> Result<MultiLocation, ()> {
+		// if this will be needed, we could implement some kind of guessing, if we have configuration for supported networkId+paraId
+		Err(())
+	}
+}
+impl<UniversalLocation, AccountId>
+	GlobalConsensusParachainConvertsFor<UniversalLocation, AccountId>
+{
+	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::<UniversalLocation, [u8; 32]>::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::<UniversalLocation, [u8; 32]>::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::<UniversalLocation, [u8; 32]>::convert_ref(
+				MultiLocation::new(2, X2(GlobalConsensus(ByGenesis([3; 32])), Parachain(1000))),
+			)
+			.expect("conversion is ok");
+		let res_gc_a_p1001 =
+			GlobalConsensusParachainConvertsFor::<UniversalLocation, [u8; 32]>::convert_ref(
+				MultiLocation::new(2, X2(GlobalConsensus(ByGenesis([3; 32])), Parachain(1001))),
+			)
+			.expect("conversion is ok");
+		let res_gc_b_p1000 =
+			GlobalConsensusParachainConvertsFor::<UniversalLocation, [u8; 32]>::convert_ref(
+				MultiLocation::new(2, X2(GlobalConsensus(ByGenesis([4; 32])), Parachain(1000))),
+			)
+			.expect("conversion is ok");
+		let res_gc_b_p1001 =
+			GlobalConsensusParachainConvertsFor::<UniversalLocation, [u8; 32]>::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/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs
index a9868021c01..2d1c9f6d4c3 100644
--- a/polkadot/xcm/xcm-builder/src/universal_exports.rs
+++ b/polkadot/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<InteriorMultiLocation>,
 	dest: impl Into<MultiLocation>,
 ) -> Result<(NetworkId, InteriorMultiLocation), MultiLocation> {
-- 
GitLab