1234567891011121314151617import {IKeyringPair} from '@polkadot/types/types';18import {ApiPromise} from '@polkadot/api';19import {expect, itSub, Pallets, usingPlaygrounds} from './util';20import {itEth} from './eth/util';2122async function maintenanceEnabled(api: ApiPromise): Promise<boolean> {23 return (await api.query.maintenance.enabled()).toJSON() as boolean;24}2526describe('Integration Test: Maintenance Mode', () => {27 let superuser: IKeyringPair;28 let donor: IKeyringPair;29 let bob: IKeyringPair;3031 before(async () => {32 await usingPlaygrounds(async (helper, privateKey) => {33 superuser = await privateKey('//Alice');34 donor = await privateKey({filename: __filename});35 [bob] = await helper.arrange.createAccounts([100n], donor);3637 if (await maintenanceEnabled(helper.getApi())) {38 console.warn('\tMaintenance mode was left enabled BEFORE the test suite! Disabling it now.');39 await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled;40 }41 });42 });4344 itSub('Allows superuser to enable and disable maintenance mode - and disallows anyone else', async ({helper}) => {45 46 await expect(helper.executeExtrinsic(superuser, 'api.tx.maintenance.enable', []), 'on commoner enabling MM').to.be.rejected; 4748 49 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);50 expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;5152 53 await expect(helper.executeExtrinsic(bob, 'api.tx.maintenance.disable', []), 'on commoner disabling MM').to.be.rejected; 5455 56 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);57 expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;58 });5960 itSub('MM blocks unique pallet calls', async ({helper}) => {61 62 const nftCollection = await helper.nft.mintCollection(bob, {63 tokenPropertyPermissions: [{key: 'test', permission: {64 collectionAdmin: true,65 tokenOwner: true,66 mutable: true,67 }}],68 });6970 71 const nft = await nftCollection.mintToken(bob);7273 74 const ftCollection = await helper.ft.mintCollection(superuser);7576 77 await expect(ftCollection.mint(superuser)).to.be.fulfilled;7879 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);80 expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;8182 83 await expect(helper.nft.mintCollection(superuser), 'cudo forbidden stuff').to.be.rejected;8485 86 await expect(nft.setProperties(87 bob,88 [{key: 'test', value: 'test-val'}],89 )).to.be.rejected;9091 92 await expect(nftCollection.mintToken(superuser)).to.be.rejected;9394 95 await expect(ftCollection.mint(superuser)).to.be.rejected;9697 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);98 expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;99100 101 await expect(helper.nft.mintCollection(bob), 'MM is disabled, the collection should be created').to.be.fulfilled;102103 104 await nft.setProperties(bob, [{key: 'test', value: 'test-val'}]);105106 107 await nftCollection.mintToken(bob);108109 110 await ftCollection.mint(superuser);111 });112113 itSub.ifWithPallets('MM blocks unique pallet calls (Re-Fungible)', [Pallets.ReFungible], async ({helper}) => {114 115 const rftCollection = await helper.rft.mintCollection(superuser);116117 118 await expect(rftCollection.mintToken(superuser)).to.be.fulfilled;119120 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);121 expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;122123 124 await expect(rftCollection.mintToken(superuser)).to.be.rejected;125 126 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);127 expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;128129 130 await rftCollection.mintToken(superuser);131 });132133 itSub('MM allows native token transfers and RPC calls', async ({helper}) => {134 135 const totalCount = await helper.collection.getTotalCount();136137 138 await expect(helper.balance.transferToSubstrate(superuser, bob.address, 2n)).to.be.fulfilled;139140 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);141 expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;142143 144 expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount);145146 147 await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled;148149 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);150 expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;151152 153 expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount);154155 156 await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled;157 });158159 itSub.ifWithPallets('MM blocks scheduled calls and the scheduler itself', [Pallets.Scheduler], async ({helper}) => {160 const collection = await helper.nft.mintCollection(bob);161162 const nftBeforeMM = await collection.mintToken(bob);163 const nftDuringMM = await collection.mintToken(bob);164 const nftAfterMM = await collection.mintToken(bob);165166 const scheduledIdBeforeMM = '0x' + '0'.repeat(31) + '0';167 const scheduledIdDuringMM = '0x' + '0'.repeat(31) + '1';168 const scheduledIdBunkerThroughMM = '0x' + '0'.repeat(31) + '2';169 const scheduledIdAttemptDuringMM = '0x' + '0'.repeat(31) + '3';170 const scheduledIdAfterMM = '0x' + '0'.repeat(31) + '4';171172 const blocksToWait = 6;173174 175 await nftBeforeMM.scheduleAfter(scheduledIdBeforeMM, blocksToWait)176 .transfer(bob, {Substrate: superuser.address});177178 await helper.wait.newBlocks(blocksToWait + 1);179 expect(await nftBeforeMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});180181 182 await nftDuringMM.scheduleAfter(scheduledIdDuringMM, blocksToWait)183 .transfer(bob, {Substrate: superuser.address});184 185 186 await nftDuringMM.scheduleAfter(scheduledIdBunkerThroughMM, blocksToWait * 2)187 .transfer(bob, {Substrate: superuser.address});188189 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);190 expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;191192 await helper.wait.newBlocks(blocksToWait + 1);193 194 expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: bob.address});195196 197 await expect(nftDuringMM.scheduleAfter(scheduledIdAttemptDuringMM, blocksToWait)198 .transfer(bob, {Substrate: superuser.address})).to.be.rejected;199200 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);201 expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;202203 204 await nftAfterMM.scheduleAfter(scheduledIdAfterMM, blocksToWait)205 .transfer(bob, {Substrate: superuser.address});206 207 await helper.wait.newBlocks(blocksToWait + 1);208209 expect(await nftAfterMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});210 211 expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});212 });213214 itEth('Disallows Ethereum transactions to execute while in maintenance', async ({helper}) => {215 const owner = await helper.eth.createAccountWithBalance(donor);216 const receiver = helper.eth.createAccount();217 218 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'B', 'C', '');219220 221 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);222 expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;223224 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);225 const tokenId = await contract.methods.nextTokenId().call();226 expect(tokenId).to.be.equal('1');227228 229 await contract.methods.mintWithTokenURI(230 receiver,231 'Test URI',232 ).send();233 234235236 await expect(contract.methods.ownerOf(tokenId).call()).rejectedWith(/token not found/);237238 239 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);240 expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;241 });242243 itSub('Allows to enable and disable MM repeatedly', async ({helper}) => {244 245 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);246 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);247 expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;248249 250 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);251 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);252 expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;253 });254255 afterEach(async () => {256 await usingPlaygrounds(async helper => {257 if (await maintenanceEnabled(helper.getApi())) {258 console.warn('\tMaintenance mode was left enabled AFTER a test has finished! Be careful. Disabling it now.');259 await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);260 }261 expect(await maintenanceEnabled(helper.getApi()), 'Disastrous! Exited the test suite with maintenance mode on.').to.be.false;262 });263 });264});