difftreelog
Merge pull request #1044 from UniqueNetwork/fix/forbid-relay-as-root
in: master
Forbid relay as root
5 files changed
js-packages/tests/sub/governance/electsudo.test.tsdiffbeforeafterboth--- /dev/null
+++ b/js-packages/tests/sub/governance/electsudo.test.ts
@@ -0,0 +1,99 @@
+import type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../../util/index.js';
+import {Event} from '@unique/playgrounds/unique.dev.js';
+import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, clearCouncil, clearTechComm, initTechComm, ITechComms} from './util.js';
+import type {ICounselors} from './util.js';
+
+describeGov('Governance: Elect Sudo', () => {
+ let sudoer: IKeyringPair;
+ let donor: IKeyringPair;
+ let counselors: ICounselors;
+ let techComm: ITechComms;
+
+ const moreThanHalfCouncilThreshold = 3;
+
+ before(async function() {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ requirePalletsOrSkip(this, helper, [Pallets.Council]);
+
+ sudoer = await privateKey('//Alice');
+ donor = await privateKey({url: import.meta.url});
+ counselors = await initCouncil(donor, sudoer);
+ techComm = await initTechComm(donor, sudoer);
+ });
+ });
+
+ after(async () => {
+ await clearCouncil(sudoer);
+ await clearTechComm(sudoer);
+ });
+
+ itSub('Democracy can elect a sudo account', async ({helper}) => {
+ const [newAccount] = await helper.arrange.createAccounts([1000n], donor);
+ const newSudoKey = newAccount.address;
+
+ // Have to use `afterEach` here instead of `after` to ensure it will be executed before `describe.after`.
+ afterEach(async () => {
+ // For some reason, the outer helper API is not initialized inside `afterEach`.
+ await usingPlaygrounds(async (helper) => {
+ await helper.executeExtrinsic(
+ newAccount,
+ 'api.tx.sudo.setKey',
+ [sudoer.address],
+ false,
+ );
+ });
+ });
+
+ const democracyProposal = helper.constructApiCall('api.tx.utility.dispatchAs', [
+ {
+ system: {
+ Signed: sudoer.address,
+ },
+ },
+ helper.constructApiCall('api.tx.sudo.setKey', [newSudoKey]),
+ ]);
+
+ const councilProposal = await helper.democracy.externalProposeDefaultCall(democracyProposal);
+
+ const proposeResult = await helper.council.collective.propose(
+ counselors.filip,
+ councilProposal,
+ moreThanHalfCouncilThreshold,
+ );
+
+ const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+ const proposalIndex = councilProposedEvent.proposalIndex;
+ const proposalHash = councilProposedEvent.proposalHash;
+
+ await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
+ await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);
+ await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);
+
+ await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
+
+ const democracyStartedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+ const democracyReferendumIndex = democracyStartedEvent.referendumIndex;
+ const democracyThreshold = democracyStartedEvent.threshold;
+
+ expect(democracyThreshold).to.be.equal('SuperMajorityAgainst');
+
+ await helper.democracy.vote(newAccount, democracyReferendumIndex, {
+ Standard: {
+ vote: {
+ aye: true,
+ conviction: 1,
+ },
+ balance: 800n,
+ },
+ });
+
+ const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed);
+ expect(passedReferendumEvent.referendumIndex).to.be.equal(democracyReferendumIndex);
+
+ await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched);
+ const currentSudoKey = await helper.callRpc('api.query.sudo.key', [])
+ .then(k => k.toString());
+ expect(currentSudoKey).to.be.equal(newSudoKey);
+ });
+});
js-packages/tests/xcm/lowLevelXcmQuartz.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/lowLevelXcmQuartz.test.ts
+++ b/js-packages/tests/xcm/lowLevelXcmQuartz.test.ts
@@ -298,67 +298,3 @@
await testHelper.rejectReserveTransferUNQfrom('shiden', 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/lowLevelXcmUnique.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 config from '../config.js';19import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';20import {nToBigInt} from '@polkadot/util';21import {hexToString} from '@polkadot/util';22import {ASTAR_DECIMALS, SAFE_XCM_VERSION, SENDER_BUDGET, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl, moonbeamUrl, polkadexUrl, relayUrl, uniqueAssetId} from './xcm.types.js';2324const testHelper = new XcmTestHelper('unique');2526272829describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {30 let alice: IKeyringPair;31 let randomAccount: IKeyringPair;3233 before(async () => {34 await usingPlaygrounds(async (helper, privateKey) => {35 alice = await privateKey('//Alice');36 console.log(config.acalaUrl);37 randomAccount = helper.arrange.createEmptyAccount();3839 // Set the default version to wrap the first message to other chains.40 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);41 });4243 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {44 const destination = {45 V2: {46 parents: 1,47 interior: {48 X1: {49 Parachain: UNIQUE_CHAIN,50 },51 },52 },53 };5455 const metadata = {56 name: 'Unique Network',57 symbol: 'UNQ',58 decimals: 18,59 minimalBalance: 1250_000_000_000_000_000n,60 };61 const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>62 hexToString(v.toJSON()['symbol'])) as string[];6364 if(!assets.includes('UNQ')) {65 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);66 } else {67 console.log('UNQ token already registered on Acala assetRegistry pallet');68 }69 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);70 });7172 await usingPlaygrounds(async (helper) => {73 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);74 });75 });7677 itSub('Should connect and send UNQ to Acala', async () => {78 await testHelper.sendUnqTo('acala', randomAccount);79 });8081 itSub('Should connect to Acala and send UNQ back', async () => {82 await testHelper.sendUnqBack('acala', alice, randomAccount);83 });8485 itSub('Acala can send only up to its balance', async () => {86 await testHelper.sendOnlyOwnedBalance('acala', alice);87 });8889 itSub('Should not accept reserve transfer of UNQ from Acala', async () => {90 await testHelper.rejectReserveTransferUNQfrom('acala', alice);91 });92});9394describeXCM('[XCMLL] Integration test: Exchanging tokens with Polkadex', () => {95 let alice: IKeyringPair;96 let randomAccount: IKeyringPair;9798 before(async () => {99 await usingPlaygrounds(async (helper, privateKey) => {100 alice = await privateKey('//Alice');101 randomAccount = helper.arrange.createEmptyAccount();102103 // Set the default version to wrap the first message to other chains.104 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);105 });106107 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {108 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))109 .toJSON() as [])110 .map(nToBigInt).length != 0;111 /*112 Check whether the Unique token has been added113 to the whitelist, since an error will occur114 if it is added again. Needed for debugging115 when this test is run multiple times.116 */117 if(isWhitelisted) {118 console.log('UNQ token is already whitelisted on Polkadex');119 } else {120 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);121 }122123 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);124 });125126 await usingPlaygrounds(async (helper) => {127 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);128 });129 });130131 itSub('Should connect and send UNQ to Polkadex', async () => {132 await testHelper.sendUnqTo('polkadex', randomAccount);133 });134135136 itSub('Should connect to Polkadex and send UNQ back', async () => {137 await testHelper.sendUnqBack('polkadex', alice, randomAccount);138 });139140 itSub('Polkadex can send only up to its balance', async () => {141 await testHelper.sendOnlyOwnedBalance('polkadex', alice);142 });143144 itSub('Should not accept reserve transfer of UNQ from Polkadex', async () => {145 await testHelper.rejectReserveTransferUNQfrom('polkadex', alice);146 });147});148149// These tests are relevant only when150// the the corresponding foreign assets are not registered151describeXCM('[XCMLL] Integration test: Unique rejects non-native tokens', () => {152 let alice: IKeyringPair;153154 before(async () => {155 await usingPlaygrounds(async (helper, privateKey) => {156 alice = await privateKey('//Alice');157158 // Set the default version to wrap the first message to other chains.159 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);160 });161 });162163 itSub('Unique rejects ACA tokens from Acala', async () => {164 await testHelper.rejectNativeTokensFrom('acala', alice);165 });166167 itSub('Unique rejects GLMR tokens from Moonbeam', async () => {168 await testHelper.rejectNativeTokensFrom('moonbeam', alice);169 });170171 itSub('Unique rejects ASTR tokens from Astar', async () => {172 await testHelper.rejectNativeTokensFrom('astar', alice);173 });174175 itSub('Unique rejects PDX tokens from Polkadex', async () => {176 await testHelper.rejectNativeTokensFrom('polkadex', alice);177 });178});179180describeXCM('[XCMLL] Integration test: Exchanging UNQ with Moonbeam', () => {181 // Unique constants182 let alice: IKeyringPair;183 let uniqueAssetLocation;184185 let randomAccountUnique: IKeyringPair;186 let randomAccountMoonbeam: IKeyringPair;187188 // Moonbeam constants189 let assetId: string;190191 const uniqueAssetMetadata = {192 name: 'xcUnique',193 symbol: 'xcUNQ',194 decimals: 18,195 isFrozen: false,196 minimalBalance: 1n,197 };198199200 before(async () => {201 await usingPlaygrounds(async (helper, privateKey) => {202 alice = await privateKey('//Alice');203 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);204205206 // Set the default version to wrap the first message to other chains.207 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);208 });209210 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {211 const alithAccount = helper.account.alithAccount();212 const baltatharAccount = helper.account.baltatharAccount();213 const dorothyAccount = helper.account.dorothyAccount();214215 randomAccountMoonbeam = helper.account.create();216217 // >>> Sponsoring Dorothy >>>218 console.log('Sponsoring Dorothy.......');219 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);220 console.log('Sponsoring Dorothy.......DONE');221 // <<< Sponsoring Dorothy <<<222 uniqueAssetLocation = {223 XCM: {224 parents: 1,225 interior: {X1: {Parachain: UNIQUE_CHAIN}},226 },227 };228 const existentialDeposit = 1n;229 const isSufficient = true;230 const unitsPerSecond = 1n;231 const numAssetsWeightHint = 0;232233 if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {234 console.log('Unique asset already registered on Moonbeam');235 } else {236 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({237 location: uniqueAssetLocation,238 metadata: uniqueAssetMetadata,239 existentialDeposit,240 isSufficient,241 unitsPerSecond,242 numAssetsWeightHint,243 });244245 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);246247 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);248 }249250 // >>> Acquire Unique AssetId Info on Moonbeam >>>251 console.log('Acquire Unique AssetId Info on Moonbeam.......');252253 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();254255 console.log('UNQ asset ID is %s', assetId);256 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');257258 // >>> Sponsoring random Account >>>259 console.log('Sponsoring random Account.......');260 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);261 console.log('Sponsoring random Account.......DONE');262 // <<< Sponsoring random Account <<<263 });264265 await usingPlaygrounds(async (helper) => {266 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET);267 });268 });269270 itSub('Should connect and send UNQ to Moonbeam', async () => {271 await testHelper.sendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam);272 });273274 itSub('Should connect to Moonbeam and send UNQ back', async () => {275 await testHelper.sendUnqBack('moonbeam', alice, randomAccountUnique);276 });277278 itSub('Moonbeam can send only up to its balance', async () => {279 await testHelper.sendOnlyOwnedBalance('moonbeam', alice);280 });281282 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async () => {283 await testHelper.rejectReserveTransferUNQfrom('moonbeam', alice);284 });285});286287describeXCM('[XCMLL] Integration test: Exchanging tokens with Astar', () => {288 let alice: IKeyringPair;289 let randomAccount: IKeyringPair;290291 const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar292 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar293294 // Unique -> Astar295 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.296 const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar297298 before(async () => {299 await usingPlaygrounds(async (helper, privateKey) => {300 alice = await privateKey('//Alice');301 randomAccount = helper.arrange.createEmptyAccount();302 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);303 console.log('randomAccount', randomAccount.address);304305 // Set the default version to wrap the first message to other chains.306 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);307 });308309 await usingAstarPlaygrounds(astarUrl, async (helper) => {310 if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {311 console.log('1. Create foreign asset and metadata');312 await helper.getSudo().assets.forceCreate(313 alice,314 UNQ_ASSET_ID_ON_ASTAR,315 alice.address,316 UNQ_MINIMAL_BALANCE_ON_ASTAR,317 );318319 await helper.assets.setMetadata(320 alice,321 UNQ_ASSET_ID_ON_ASTAR,322 'Unique Network',323 'UNQ',324 Number(UNQ_DECIMALS),325 );326327 console.log('2. Register asset location on Astar');328 const assetLocation = {329 V2: {330 parents: 1,331 interior: {332 X1: {333 Parachain: UNIQUE_CHAIN,334 },335 },336 },337 };338339 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);340341 console.log('3. Set UNQ payment for XCM execution on Astar');342 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);343 } else {344 console.log('UNQ is already registered on Astar');345 }346 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');347 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);348 });349 });350351 itSub('Should connect and send UNQ to Astar', async () => {352 await testHelper.sendUnqTo('astar', randomAccount);353 });354355 itSub('Should connect to Astar and send UNQ back', async () => {356 await testHelper.sendUnqBack('astar', alice, randomAccount);357 });358359 itSub('Astar can send only up to its balance', async () => {360 await testHelper.sendOnlyOwnedBalance('astar', alice);361 });362363 itSub('Should not accept reserve transfer of UNQ from Astar', async () => {364 await testHelper.rejectReserveTransferUNQfrom('astar', alice);365 });366});367368describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => {369 let sudoer: IKeyringPair;370371 before(async function () {372 await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => {373 sudoer = await privateKey('//Alice');374 });375 });376377 // At the moment there is no reliable way378 // to establish the correspondence between the `ExecutedDownward` event379 // and the relay's sent message due to `SetTopic` instruction380 // containing an unpredictable topic silently added by the relay's messages on the router level.381 // This changes the message hash on arrival to our chain.382 //383 // See:384 // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83385 // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36386 //387 // Because of this, we insert time gaps between tests so388 // different `ExecutedDownward` events won't interfere with each other.389 afterEach(async () => {390 await usingPlaygrounds(async (helper) => {391 await helper.wait.newBlocks(3);392 });393 });394395 itSub('The relay can set storage', async () => {396 await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain');397 });398399 itSub('The relay can batch set storage', async () => {400 await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch');401 });402403 itSub('The relay can batchAll set storage', async () => {404 await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll');405 });406407 itSub('The relay can forceBatch set storage', async () => {408 await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch');409 });410411 itSub('[negative] The relay cannot set balance', async () => {412 await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain');413 });414415 itSub('[negative] The relay cannot set balance via batch', async () => {416 await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch');417 });418419 itSub('[negative] The relay cannot set balance via batchAll', async () => {420 await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll');421 });422423 itSub('[negative] The relay cannot set balance via forceBatch', async () => {424 await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch');425 });426427 itSub('[negative] The relay cannot set balance via dispatchAs', async () => {428 await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs');429 });430});1// 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 config from '../config.js';19import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';20import {nToBigInt} from '@polkadot/util';21import {hexToString} from '@polkadot/util';22import {ASTAR_DECIMALS, SAFE_XCM_VERSION, SENDER_BUDGET, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl, moonbeamUrl, polkadexUrl, relayUrl, uniqueAssetId} from './xcm.types.js';2324const testHelper = new XcmTestHelper('unique');2526272829describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {30 let alice: IKeyringPair;31 let randomAccount: IKeyringPair;3233 before(async () => {34 await usingPlaygrounds(async (helper, privateKey) => {35 alice = await privateKey('//Alice');36 console.log(config.acalaUrl);37 randomAccount = helper.arrange.createEmptyAccount();3839 // Set the default version to wrap the first message to other chains.40 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);41 });4243 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {44 const destination = {45 V2: {46 parents: 1,47 interior: {48 X1: {49 Parachain: UNIQUE_CHAIN,50 },51 },52 },53 };5455 const metadata = {56 name: 'Unique Network',57 symbol: 'UNQ',58 decimals: 18,59 minimalBalance: 1250_000_000_000_000_000n,60 };61 const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>62 hexToString(v.toJSON()['symbol'])) as string[];6364 if(!assets.includes('UNQ')) {65 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);66 } else {67 console.log('UNQ token already registered on Acala assetRegistry pallet');68 }69 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);70 });7172 await usingPlaygrounds(async (helper) => {73 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);74 });75 });7677 itSub('Should connect and send UNQ to Acala', async () => {78 await testHelper.sendUnqTo('acala', randomAccount);79 });8081 itSub('Should connect to Acala and send UNQ back', async () => {82 await testHelper.sendUnqBack('acala', alice, randomAccount);83 });8485 itSub('Acala can send only up to its balance', async () => {86 await testHelper.sendOnlyOwnedBalance('acala', alice);87 });8889 itSub('Should not accept reserve transfer of UNQ from Acala', async () => {90 await testHelper.rejectReserveTransferUNQfrom('acala', alice);91 });92});9394describeXCM('[XCMLL] Integration test: Exchanging tokens with Polkadex', () => {95 let alice: IKeyringPair;96 let randomAccount: IKeyringPair;9798 before(async () => {99 await usingPlaygrounds(async (helper, privateKey) => {100 alice = await privateKey('//Alice');101 randomAccount = helper.arrange.createEmptyAccount();102103 // Set the default version to wrap the first message to other chains.104 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);105 });106107 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {108 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))109 .toJSON() as [])110 .map(nToBigInt).length != 0;111 /*112 Check whether the Unique token has been added113 to the whitelist, since an error will occur114 if it is added again. Needed for debugging115 when this test is run multiple times.116 */117 if(isWhitelisted) {118 console.log('UNQ token is already whitelisted on Polkadex');119 } else {120 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);121 }122123 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);124 });125126 await usingPlaygrounds(async (helper) => {127 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);128 });129 });130131 itSub('Should connect and send UNQ to Polkadex', async () => {132 await testHelper.sendUnqTo('polkadex', randomAccount);133 });134135136 itSub('Should connect to Polkadex and send UNQ back', async () => {137 await testHelper.sendUnqBack('polkadex', alice, randomAccount);138 });139140 itSub('Polkadex can send only up to its balance', async () => {141 await testHelper.sendOnlyOwnedBalance('polkadex', alice);142 });143144 itSub('Should not accept reserve transfer of UNQ from Polkadex', async () => {145 await testHelper.rejectReserveTransferUNQfrom('polkadex', alice);146 });147});148149// These tests are relevant only when150// the the corresponding foreign assets are not registered151describeXCM('[XCMLL] Integration test: Unique rejects non-native tokens', () => {152 let alice: IKeyringPair;153154 before(async () => {155 await usingPlaygrounds(async (helper, privateKey) => {156 alice = await privateKey('//Alice');157158 // Set the default version to wrap the first message to other chains.159 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);160 });161 });162163 itSub('Unique rejects ACA tokens from Acala', async () => {164 await testHelper.rejectNativeTokensFrom('acala', alice);165 });166167 itSub('Unique rejects GLMR tokens from Moonbeam', async () => {168 await testHelper.rejectNativeTokensFrom('moonbeam', alice);169 });170171 itSub('Unique rejects ASTR tokens from Astar', async () => {172 await testHelper.rejectNativeTokensFrom('astar', alice);173 });174175 itSub('Unique rejects PDX tokens from Polkadex', async () => {176 await testHelper.rejectNativeTokensFrom('polkadex', alice);177 });178});179180describeXCM('[XCMLL] Integration test: Exchanging UNQ with Moonbeam', () => {181 // Unique constants182 let alice: IKeyringPair;183 let uniqueAssetLocation;184185 let randomAccountUnique: IKeyringPair;186 let randomAccountMoonbeam: IKeyringPair;187188 // Moonbeam constants189 let assetId: string;190191 const uniqueAssetMetadata = {192 name: 'xcUnique',193 symbol: 'xcUNQ',194 decimals: 18,195 isFrozen: false,196 minimalBalance: 1n,197 };198199200 before(async () => {201 await usingPlaygrounds(async (helper, privateKey) => {202 alice = await privateKey('//Alice');203 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);204205206 // Set the default version to wrap the first message to other chains.207 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);208 });209210 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {211 const alithAccount = helper.account.alithAccount();212 const baltatharAccount = helper.account.baltatharAccount();213 const dorothyAccount = helper.account.dorothyAccount();214215 randomAccountMoonbeam = helper.account.create();216217 // >>> Sponsoring Dorothy >>>218 console.log('Sponsoring Dorothy.......');219 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);220 console.log('Sponsoring Dorothy.......DONE');221 // <<< Sponsoring Dorothy <<<222 uniqueAssetLocation = {223 XCM: {224 parents: 1,225 interior: {X1: {Parachain: UNIQUE_CHAIN}},226 },227 };228 const existentialDeposit = 1n;229 const isSufficient = true;230 const unitsPerSecond = 1n;231 const numAssetsWeightHint = 0;232233 if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {234 console.log('Unique asset already registered on Moonbeam');235 } else {236 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({237 location: uniqueAssetLocation,238 metadata: uniqueAssetMetadata,239 existentialDeposit,240 isSufficient,241 unitsPerSecond,242 numAssetsWeightHint,243 });244245 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);246247 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);248 }249250 // >>> Acquire Unique AssetId Info on Moonbeam >>>251 console.log('Acquire Unique AssetId Info on Moonbeam.......');252253 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();254255 console.log('UNQ asset ID is %s', assetId);256 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');257258 // >>> Sponsoring random Account >>>259 console.log('Sponsoring random Account.......');260 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);261 console.log('Sponsoring random Account.......DONE');262 // <<< Sponsoring random Account <<<263 });264265 await usingPlaygrounds(async (helper) => {266 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET);267 });268 });269270 itSub('Should connect and send UNQ to Moonbeam', async () => {271 await testHelper.sendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam);272 });273274 itSub('Should connect to Moonbeam and send UNQ back', async () => {275 await testHelper.sendUnqBack('moonbeam', alice, randomAccountUnique);276 });277278 itSub('Moonbeam can send only up to its balance', async () => {279 await testHelper.sendOnlyOwnedBalance('moonbeam', alice);280 });281282 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async () => {283 await testHelper.rejectReserveTransferUNQfrom('moonbeam', alice);284 });285});286287describeXCM('[XCMLL] Integration test: Exchanging tokens with Astar', () => {288 let alice: IKeyringPair;289 let randomAccount: IKeyringPair;290291 const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar292 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar293294 // Unique -> Astar295 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.296 const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar297298 before(async () => {299 await usingPlaygrounds(async (helper, privateKey) => {300 alice = await privateKey('//Alice');301 randomAccount = helper.arrange.createEmptyAccount();302 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);303 console.log('randomAccount', randomAccount.address);304305 // Set the default version to wrap the first message to other chains.306 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);307 });308309 await usingAstarPlaygrounds(astarUrl, async (helper) => {310 if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {311 console.log('1. Create foreign asset and metadata');312 await helper.getSudo().assets.forceCreate(313 alice,314 UNQ_ASSET_ID_ON_ASTAR,315 alice.address,316 UNQ_MINIMAL_BALANCE_ON_ASTAR,317 );318319 await helper.assets.setMetadata(320 alice,321 UNQ_ASSET_ID_ON_ASTAR,322 'Unique Network',323 'UNQ',324 Number(UNQ_DECIMALS),325 );326327 console.log('2. Register asset location on Astar');328 const assetLocation = {329 V2: {330 parents: 1,331 interior: {332 X1: {333 Parachain: UNIQUE_CHAIN,334 },335 },336 },337 };338339 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);340341 console.log('3. Set UNQ payment for XCM execution on Astar');342 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);343 } else {344 console.log('UNQ is already registered on Astar');345 }346 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');347 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);348 });349 });350351 itSub('Should connect and send UNQ to Astar', async () => {352 await testHelper.sendUnqTo('astar', randomAccount);353 });354355 itSub('Should connect to Astar and send UNQ back', async () => {356 await testHelper.sendUnqBack('astar', alice, randomAccount);357 });358359 itSub('Astar can send only up to its balance', async () => {360 await testHelper.sendOnlyOwnedBalance('astar', alice);361 });362363 itSub('Should not accept reserve transfer of UNQ from Astar', async () => {364 await testHelper.rejectReserveTransferUNQfrom('astar', alice);365 });366});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;
}