Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
[xcm] Foreign global consensus parachain LocationToAccountId converter (
Browse files Browse the repository at this point in the history
#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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: parity-processbot <>
Co-authored-by: Gavin Wood <[email protected]>
  • Loading branch information
bkontur and gavofyork authored May 16, 2023
1 parent 54c9ff1 commit 19fdd19
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 8 deletions.
6 changes: 3 additions & 3 deletions xcm/xcm-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
167 changes: 163 additions & 4 deletions xcm/xcm-builder/src/location_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";

Expand All @@ -48,7 +49,7 @@ pub const FOREIGN_CHAIN_PREFIX_RELAY: [u8; 36] = *b"ForeignChainAliasAccountPref
/// ```notrust
/// R
/// / \
/// / \
/// / \
/// P1 P2
/// / \ / \
/// / \ / \
Expand Down Expand Up @@ -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::*;
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 5 additions & 1 deletion xcm/xcm-builder/src/universal_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down

0 comments on commit 19fdd19

Please sign in to comment.