difftreelog
fix relay can't do root ops
in: master
4 files changed
js-packages/tests/xcm/lowLevelXcmQuartz.test.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import type {IKeyringPair} from '@polkadot/types/types';18import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';19import {QUARTZ_CHAIN, QTZ_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, shidenUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT, SENDER_BUDGET, relayUrl} from './xcm.types.js';20import {hexToString} from '@polkadot/util';2122const testHelper = new XcmTestHelper('quartz');2324describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => {25 let alice: IKeyringPair;26 let randomAccount: IKeyringPair;2728 before(async () => {29 await usingPlaygrounds(async (helper, privateKey) => {30 alice = await privateKey('//Alice');31 [randomAccount] = await helper.arrange.createAccounts([0n], alice);3233 // Set the default version to wrap the first message to other chains.34 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);35 });3637 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {38 const destination = {39 V2: {40 parents: 1,41 interior: {42 X1: {43 Parachain: QUARTZ_CHAIN,44 },45 },46 },47 };4849 const metadata = {50 name: 'Quartz',51 symbol: 'QTZ',52 decimals: 18,53 minimalBalance: 1000000000000000000n,54 };5556 const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>57 hexToString(v.toJSON()['symbol'])) as string[];5859 if(!assets.includes('QTZ')) {60 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);61 } else {62 console.log('QTZ token already registered on Karura assetRegistry pallet');63 }64 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);65 });6667 await usingPlaygrounds(async (helper) => {68 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);69 });70 });7172 itSub('Should connect and send QTZ to Karura', async () => {73 await testHelper.sendUnqTo('karura', randomAccount);74 });7576 itSub('Should connect to Karura and send QTZ back', async () => {77 await testHelper.sendUnqBack('karura', alice, randomAccount);78 });7980 itSub('Karura can send only up to its balance', async () => {81 await testHelper.sendOnlyOwnedBalance('karura', alice);82 });83});84// These tests are relevant only when85// the the corresponding foreign assets are not registered86describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => {87 let alice: IKeyringPair;888990 before(async () => {91 await usingPlaygrounds(async (helper, privateKey) => {92 alice = await privateKey('//Alice');93949596 // Set the default version to wrap the first message to other chains.97 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);98 });99 });100101 itSub('Quartz rejects KAR tokens from Karura', async () => {102 await testHelper.rejectNativeTokensFrom('karura', alice);103 });104105 itSub('Quartz rejects MOVR tokens from Moonriver', async () => {106 await testHelper.rejectNativeTokensFrom('moonriver', alice);107 });108109 itSub('Quartz rejects SDN tokens from Shiden', async () => {110 await testHelper.rejectNativeTokensFrom('shiden', alice);111 });112});113114describeXCM('[XCMLL] Integration test: Exchanging QTZ with Moonriver', () => {115 // Quartz constants116 let alice: IKeyringPair;117 let quartzAssetLocation;118119 let randomAccountQuartz: IKeyringPair;120 let randomAccountMoonriver: IKeyringPair;121122 // Moonriver constants123 let assetId: string;124125 const quartzAssetMetadata = {126 name: 'xcQuartz',127 symbol: 'xcQTZ',128 decimals: 18,129 isFrozen: false,130 minimalBalance: 1n,131 };132133134 before(async () => {135 await usingPlaygrounds(async (helper, privateKey) => {136 alice = await privateKey('//Alice');137 [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);138139140 // Set the default version to wrap the first message to other chains.141 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);142 });143144 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {145 const alithAccount = helper.account.alithAccount();146 const baltatharAccount = helper.account.baltatharAccount();147 const dorothyAccount = helper.account.dorothyAccount();148149 randomAccountMoonriver = helper.account.create();150151 // >>> Sponsoring Dorothy >>>152 console.log('Sponsoring Dorothy.......');153 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);154 console.log('Sponsoring Dorothy.......DONE');155 // <<< Sponsoring Dorothy <<<156157 quartzAssetLocation = {158 XCM: {159 parents: 1,160 interior: {X1: {Parachain: QUARTZ_CHAIN}},161 },162 };163 const existentialDeposit = 1n;164 const isSufficient = true;165 const unitsPerSecond = 1n;166 const numAssetsWeightHint = 0;167 if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {168 console.log('Quartz asset already registered on Moonriver');169 } else {170 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({171 location: quartzAssetLocation,172 metadata: quartzAssetMetadata,173 existentialDeposit,174 isSufficient,175 unitsPerSecond,176 numAssetsWeightHint,177 });178179 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);180181 await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);182 }183 // >>> Acquire Quartz AssetId Info on Moonriver >>>184 console.log('Acquire Quartz AssetId Info on Moonriver.......');185186 assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();187188 console.log('QTZ asset ID is %s', assetId);189 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');190 // >>> Acquire Quartz AssetId Info on Moonriver >>>191192 // >>> Sponsoring random Account >>>193 console.log('Sponsoring random Account.......');194 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);195 console.log('Sponsoring random Account.......DONE');196 // <<< Sponsoring random Account <<<197 });198199 await usingPlaygrounds(async (helper) => {200 await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);201 });202 });203204 itSub('Should connect and send QTZ to Moonriver', async () => {205 await testHelper.sendUnqTo('moonriver', randomAccountQuartz, randomAccountMoonriver);206 });207208 itSub('Should connect to Moonriver and send QTZ back', async () => {209 await testHelper.sendUnqBack('moonriver', alice, randomAccountQuartz);210 });211212 itSub('Moonriver can send only up to its balance', async () => {213 await testHelper.sendOnlyOwnedBalance('moonriver', alice);214 });215216 itSub('Should not accept reserve transfer of QTZ from Moonriver', async () => {217 await testHelper.rejectReserveTransferUNQfrom('moonriver', alice);218 });219});220221describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => {222 let alice: IKeyringPair;223 let randomAccount: IKeyringPair;224225 const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; // The value is taken from the live Shiden226 const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; // The value is taken from the live Shiden227228 // Quartz -> Shiden229 const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden230 const unitsPerSecond = 500_451_000_000_000_000_000n; // The value is taken from the live Shiden231232 before(async () => {233 await usingPlaygrounds(async (helper, privateKey) => {234 alice = await privateKey('//Alice');235 randomAccount = helper.arrange.createEmptyAccount();236 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);237 console.log('sender: ', randomAccount.address);238239 // Set the default version to wrap the first message to other chains.240 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);241 });242243 await usingShidenPlaygrounds(shidenUrl, async (helper) => {244 if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {245 console.log('1. Create foreign asset and metadata');246 await helper.getSudo().assets.forceCreate(247 alice,248 QTZ_ASSET_ID_ON_SHIDEN,249 alice.address,250 QTZ_MINIMAL_BALANCE_ON_SHIDEN,251 );252253 await helper.assets.setMetadata(254 alice,255 QTZ_ASSET_ID_ON_SHIDEN,256 'Quartz',257 'QTZ',258 Number(QTZ_DECIMALS),259 );260261 console.log('2. Register asset location on Shiden');262 const assetLocation = {263 V2: {264 parents: 1,265 interior: {266 X1: {267 Parachain: QUARTZ_CHAIN,268 },269 },270 },271 };272273 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);274275 console.log('3. Set QTZ payment for XCM execution on Shiden');276 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);277 } else {278 console.log('QTZ is already registered on Shiden');279 }280 console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');281 await helper.balance.transferToSubstrate(alice, randomAccount.address, shidenInitialBalance);282 });283 });284285 itSub('Should connect and send QTZ to Shiden', async () => {286 await testHelper.sendUnqTo('shiden', randomAccount);287 });288289 itSub('Should connect to Shiden and send QTZ back', async () => {290 await testHelper.sendUnqBack('shiden', alice, randomAccount);291 });292293 itSub('Shiden can send only up to its balance', async () => {294 await testHelper.sendOnlyOwnedBalance('shiden', alice);295 });296297 itSub('Should not accept reserve transfer of QTZ from Shiden', async () => {298 await testHelper.rejectReserveTransferUNQfrom('shiden', alice);299 });300});js-packages/tests/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/lowLevelXcmUnique.test.ts
+++ b/js-packages/tests/xcm/lowLevelXcmUnique.test.ts
@@ -364,67 +364,3 @@
await testHelper.rejectReserveTransferUNQfrom('astar', alice);
});
});
-
-describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => {
- let sudoer: IKeyringPair;
-
- before(async function () {
- await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => {
- sudoer = await privateKey('//Alice');
- });
- });
-
- // At the moment there is no reliable way
- // to establish the correspondence between the `ExecutedDownward` event
- // and the relay's sent message due to `SetTopic` instruction
- // containing an unpredictable topic silently added by the relay's messages on the router level.
- // This changes the message hash on arrival to our chain.
- //
- // See:
- // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83
- // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36
- //
- // Because of this, we insert time gaps between tests so
- // different `ExecutedDownward` events won't interfere with each other.
- afterEach(async () => {
- await usingPlaygrounds(async (helper) => {
- await helper.wait.newBlocks(3);
- });
- });
-
- itSub('The relay can set storage', async () => {
- await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain');
- });
-
- itSub('The relay can batch set storage', async () => {
- await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch');
- });
-
- itSub('The relay can batchAll set storage', async () => {
- await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll');
- });
-
- itSub('The relay can forceBatch set storage', async () => {
- await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch');
- });
-
- itSub('[negative] The relay cannot set balance', async () => {
- await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain');
- });
-
- itSub('[negative] The relay cannot set balance via batch', async () => {
- await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch');
- });
-
- itSub('[negative] The relay cannot set balance via batchAll', async () => {
- await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll');
- });
-
- itSub('[negative] The relay cannot set balance via forceBatch', async () => {
- await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch');
- });
-
- itSub('[negative] The relay cannot set balance via dispatchAs', async () => {
- await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs');
- });
-});
js-packages/tests/xcm/xcm.types.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcm.types.ts
+++ b/js-packages/tests/xcm/xcm.types.ts
@@ -505,117 +505,4 @@
await expectFailedToTransact(helper, messageSent);
});
}
-
- private async _relayXcmTransactSetStorage(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') {
- // eslint-disable-next-line require-await
- return await usingPlaygrounds(async (helper) => {
- const relayForceKV = () => {
- const random = Math.random();
- const key = `relay-forced-key (instance: ${random})`;
- const val = `relay-forced-value (instance: ${random})`;
- const call = helper.constructApiCall('api.tx.system.setStorage', [[[key, val]]]).method.toHex();
-
- return {
- call,
- key,
- val,
- };
- };
-
- if(variant == 'plain') {
- const kv = relayForceKV();
- return {
- program: helper.arrange.makeUnpaidSudoTransactProgram({
- weightMultiplier: 1,
- call: kv.call,
- }),
- kvs: [kv],
- };
- } else {
- const kv0 = relayForceKV();
- const kv1 = relayForceKV();
-
- const batchCall = helper.constructApiCall(`api.tx.utility.${variant}`, [[kv0.call, kv1.call]]).method.toHex();
- return {
- program: helper.arrange.makeUnpaidSudoTransactProgram({
- weightMultiplier: 2,
- call: batchCall,
- }),
- kvs: [kv0, kv1],
- };
- }
- });
- }
-
- async relayIsPermittedToSetStorage(relaySudoer: IKeyringPair, variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') {
- const {program, kvs} = await this._relayXcmTransactSetStorage(variant);
-
- await usingRelayPlaygrounds(relayUrl, async (helper) => {
- await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [
- this._uniqueChainMultilocationForRelay(),
- program,
- ]);
- });
-
- await usingPlaygrounds(async (helper) => {
- await expectDownwardXcmComplete(helper);
-
- for(const kv of kvs) {
- const forcedValue = await helper.callRpc('api.rpc.state.getStorage', [kv.key]);
- expect(hexToString(forcedValue.toHex())).to.be.equal(kv.val);
- }
- });
- }
-
- private async _relayXcmTransactSetBalance(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs') {
- // eslint-disable-next-line require-await
- return await usingPlaygrounds(async (helper) => {
- const emptyAccount = helper.arrange.createEmptyAccount().address;
-
- const forceSetBalanceCall = helper.constructApiCall('api.tx.balances.forceSetBalance', [emptyAccount, 10_000n]).method.toHex();
-
- let call;
-
- if(variant == 'plain') {
- call = forceSetBalanceCall;
-
- } else if(variant == 'dispatchAs') {
- call = helper.constructApiCall('api.tx.utility.dispatchAs', [
- {
- system: 'Root',
- },
- forceSetBalanceCall,
- ]).method.toHex();
- } else {
- call = helper.constructApiCall(`api.tx.utility.${variant}`, [[forceSetBalanceCall]]).method.toHex();
- }
-
- return {
- program: helper.arrange.makeUnpaidSudoTransactProgram({
- weightMultiplier: 1,
- call,
- }),
- emptyAccount,
- };
- });
- }
-
- async relayIsNotPermittedToSetBalance(
- relaySudoer: IKeyringPair,
- variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs',
- ) {
- const {program, emptyAccount} = await this._relayXcmTransactSetBalance(variant);
-
- await usingRelayPlaygrounds(relayUrl, async (helper) => {
- await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [
- this._uniqueChainMultilocationForRelay(),
- program,
- ]);
- });
-
- await usingPlaygrounds(async (helper) => {
- await expectDownwardXcmNoPermission(helper);
- expect(await helper.balance.getSubstrate(emptyAccount)).to.be.equal(0n);
- });
- }
}
runtime/common/config/xcm/mod.rsdiffbeforeafterboth--- a/runtime/common/config/xcm/mod.rs
+++ b/runtime/common/config/xcm/mod.rs
@@ -17,7 +17,7 @@
use cumulus_primitives_core::ParaId;
use frame_support::{
parameter_types,
- traits::{ConstU32, Contains, Everything, Get, Nothing, ProcessMessageError},
+ traits::{ConstU32, Everything, Get, Nothing, ProcessMessageError},
};
use frame_system::EnsureRoot;
use pallet_xcm::XcmPassthrough;
@@ -29,9 +29,9 @@
v3::Instruction,
};
use staging_xcm_builder::{
- AccountId32Aliases, EnsureXcmOrigin, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset,
- RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
- SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
+ AccountId32Aliases, EnsureXcmOrigin, FixedWeightBounds, ParentIsPreset, RelayChainAsNative,
+ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative,
+ SignedToAccountId32, SovereignSignedViaLocation,
};
use staging_xcm_executor::{
traits::{Properties, ShouldExecute},
@@ -111,9 +111,6 @@
// Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
// recognised.
SiblingParachainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
- // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a
- // transaction from the Root origin.
- ParentAsSuperuser<RuntimeOrigin>,
// Native signed account converter; this just converts an `AccountId32` origin into a normal
// `Origin::Signed` origin of the same 32-byte value.
SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
@@ -166,55 +163,7 @@
}
pub type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
-
-pub struct XcmCallFilter;
-impl XcmCallFilter {
- fn allow_gov_and_sys_call(call: &RuntimeCall) -> bool {
- match call {
- RuntimeCall::System(..) => true,
-
- #[cfg(feature = "governance")]
- RuntimeCall::Identity(..)
- | RuntimeCall::Preimage(..)
- | RuntimeCall::Democracy(..)
- | RuntimeCall::Council(..)
- | RuntimeCall::TechnicalCommittee(..)
- | RuntimeCall::CouncilMembership(..)
- | RuntimeCall::TechnicalCommitteeMembership(..)
- | RuntimeCall::FellowshipCollective(..)
- | RuntimeCall::FellowshipReferenda(..) => true,
- _ => false,
- }
- }
- fn allow_utility_call(call: &RuntimeCall) -> bool {
- match call {
- RuntimeCall::Utility(pallet_utility::Call::batch { calls, .. }) => {
- calls.iter().all(Self::allow_gov_and_sys_call)
- }
- RuntimeCall::Utility(pallet_utility::Call::batch_all { calls, .. }) => {
- calls.iter().all(Self::allow_gov_and_sys_call)
- }
- RuntimeCall::Utility(pallet_utility::Call::as_derivative { call, .. }) => {
- Self::allow_gov_and_sys_call(call)
- }
- RuntimeCall::Utility(pallet_utility::Call::dispatch_as { call, .. }) => {
- Self::allow_gov_and_sys_call(call)
- }
- RuntimeCall::Utility(pallet_utility::Call::force_batch { calls, .. }) => {
- calls.iter().all(Self::allow_gov_and_sys_call)
- }
- _ => false,
- }
- }
-}
-
-impl Contains<RuntimeCall> for XcmCallFilter {
- fn contains(call: &RuntimeCall) -> bool {
- Self::allow_gov_and_sys_call(call) || Self::allow_utility_call(call)
- }
-}
-
pub struct XcmExecutorConfig<T>(PhantomData<T>);
impl<T> staging_xcm_executor::Config for XcmExecutorConfig<T>
where
@@ -244,7 +193,7 @@
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = RuntimeCall;
- type SafeCallFilter = XcmCallFilter;
+ type SafeCallFilter = Nothing;
type Aliasers = Nothing;
}