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

[xcm] Foreign global consensus parachain LocationToAccountId converter #7016

Merged
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
};
170 changes: 166 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,61 @@ 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(GlobalConsensusParachainConvertsFor::<
UniversalLocation,
AccountId,
>::from_params(&remote_network, &remote_network_para_id))),
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
_ => 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 +387,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
2 changes: 1 addition & 1 deletion xcm/xcm-builder/src/universal_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use xcm::prelude::*;
use xcm_executor::traits::{validate_export, ExportXcm};
use SendError::*;

fn ensure_is_remote(
pub fn ensure_is_remote(
bkontur marked this conversation as resolved.
Show resolved Hide resolved
universal_local: impl Into<InteriorMultiLocation>,
dest: impl Into<MultiLocation>,
) -> Result<(NetworkId, InteriorMultiLocation), MultiLocation> {
Expand Down