--- a/tests/src/eth/scheduling.test.ts +++ b/tests/src/eth/scheduling.test.ts @@ -15,7 +15,7 @@ // along with Unique Network. If not, see . import {expect} from 'chai'; -import {EthUniqueHelper, itEth} from './util'; +import {EthUniqueHelper, itSchedEth} from './util'; import {Pallets, usingPlaygrounds} from '../util'; describe('Scheduing EVM smart contracts', () => { @@ -26,11 +26,11 @@ }); }); - itEth.ifWithPallets('Successfully schedules and periodically executes an EVM contract', [Pallets.Scheduler], async ({helper, privateKey}) => { + itSchedEth.ifWithPallets('Successfully schedules and periodically executes an EVM contract', [Pallets.Scheduler], async (scheduleKind, {helper, privateKey}) => { const donor = await privateKey({filename: __filename}); const [alice] = await helper.arrange.createAccounts([1000n], donor); - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; const deployer = await helper.eth.createAccountWithBalance(alice); const flipper = await helper.eth.deployFlipper(deployer); @@ -44,7 +44,7 @@ repetitions: 2, }; - await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic}) + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) .eth.sendEVM( alice, flipper.options.address, --- a/tests/src/eth/util/index.ts +++ b/tests/src/eth/util/index.ts @@ -8,6 +8,7 @@ import {EthUniqueHelper} from './playgrounds/unique.dev'; import {SilentLogger, SilentConsole} from '../../util/playgrounds/unique.dev'; +import {SchedKind} from '../../util'; export {EthUniqueHelper} from './playgrounds/unique.dev'; @@ -81,3 +82,19 @@ itEthIfWithPallet.only = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itEthIfWithPallet(name, required, cb, {only: true}); itEthIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itEthIfWithPallet(name, required, cb, {skip: true}); itEth.ifWithPallets = itEthIfWithPallet; + +export function itSchedEth( + name: string, + cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any, + opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}, +) { + itEth(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts); + itEth(name + ' (named scheduling)', (apis) => cb('named', apis), opts); +} +itSchedEth.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itSchedEth(name, cb, {only: true}); +itSchedEth.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any) => itSchedEth(name, cb, {skip: true}); +itSchedEth.ifWithPallets = itSchedIfWithPallets; + +function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { + return itSchedEth(name, cb, {requiredPallets: required, ...opts}); +} --- a/tests/src/maintenanceMode.seqtest.ts +++ b/tests/src/maintenanceMode.seqtest.ts @@ -16,7 +16,7 @@ import {IKeyringPair} from '@polkadot/types/types'; import {ApiPromise} from '@polkadot/api'; -import {expect, itSub, Pallets, usingPlaygrounds} from './util'; +import {expect, itSched, itSub, Pallets, usingPlaygrounds} from './util'; import {itEth} from './eth/util'; async function maintenanceEnabled(api: ApiPromise): Promise { @@ -162,7 +162,7 @@ await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled; }); - itSub.ifWithPallets('MM blocks scheduled calls and the scheduler itself', [Pallets.Scheduler], async ({helper}) => { + itSched.ifWithPallets('MM blocks scheduled calls and the scheduler itself', [Pallets.Scheduler], async (scheduleKind, {helper}) => { const collection = await helper.nft.mintCollection(bob); const nftBeforeMM = await collection.mintToken(bob); @@ -175,23 +175,25 @@ scheduledIdBunkerThroughMM, scheduledIdAttemptDuringMM, scheduledIdAfterMM, - ] = await helper.arrange.makeScheduledIds(5); + ] = scheduleKind == 'named' + ? helper.arrange.makeScheduledIds(5) + : new Array(5); const blocksToWait = 6; // Scheduling works before the maintenance - await nftBeforeMM.scheduleAfter(scheduledIdBeforeMM, blocksToWait) + await nftBeforeMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdBeforeMM}) .transfer(bob, {Substrate: superuser.address}); await helper.wait.newBlocks(blocksToWait + 1); expect(await nftBeforeMM.getOwner()).to.be.deep.equal({Substrate: superuser.address}); // Schedule a transaction that should occur *during* the maintenance - await nftDuringMM.scheduleAfter(scheduledIdDuringMM, blocksToWait) + await nftDuringMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdDuringMM}) .transfer(bob, {Substrate: superuser.address}); // Schedule a transaction that should occur *after* the maintenance - await nftDuringMM.scheduleAfter(scheduledIdBunkerThroughMM, blocksToWait * 2) + await nftDuringMM.scheduleAfter(blocksToWait * 2, {scheduledId: scheduledIdBunkerThroughMM}) .transfer(bob, {Substrate: superuser.address}); await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []); @@ -202,7 +204,7 @@ expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: bob.address}); // Any attempts to schedule a tx during the MM should be rejected - await expect(nftDuringMM.scheduleAfter(scheduledIdAttemptDuringMM, blocksToWait) + await expect(nftDuringMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAttemptDuringMM}) .transfer(bob, {Substrate: superuser.address})) .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/); @@ -210,7 +212,7 @@ expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false; // Scheduling works after the maintenance - await nftAfterMM.scheduleAfter(scheduledIdAfterMM, blocksToWait) + await nftAfterMM.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAfterMM}) .transfer(bob, {Substrate: superuser.address}); await helper.wait.newBlocks(blocksToWait + 1); --- a/tests/src/scheduler.seqtest.ts +++ b/tests/src/scheduler.seqtest.ts @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util'; +import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util'; import {IKeyringPair} from '@polkadot/types/types'; import {DevUniqueHelper} from './util/playgrounds/unique.dev'; @@ -36,13 +36,19 @@ }); }); - itSub('Can delay a transfer of an owned token', async ({helper}) => { + beforeEach(async () => { + await usingPlaygrounds(async (helper) => { + await helper.wait.noScheduledTasks(); + }); + }); + + itSched('Can delay a transfer of an owned token', async (scheduleKind, {helper}) => { const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); const token = await collection.mintToken(alice); - const schedulerId = await helper.arrange.makeScheduledId(); + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; const blocksBeforeExecution = 4; - await token.scheduleAfter(schedulerId, blocksBeforeExecution) + await token.scheduleAfter(blocksBeforeExecution, {scheduledId}) .transfer(alice, {Substrate: bob.address}); const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1; @@ -53,8 +59,8 @@ expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); }); - itSub('Can transfer funds periodically', async ({helper}) => { - const scheduledId = await helper.arrange.makeScheduledId(); + itSched('Can transfer funds periodically', async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; const waitForBlocks = 1; const amount = 1n * helper.balance.getOneTokenNominal(); @@ -65,7 +71,7 @@ const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); - await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic}) + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) .balance.transferToSubstrate(alice, bob.address, amount); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -92,12 +98,12 @@ const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); const token = await collection.mintToken(alice); - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = helper.arrange.makeScheduledId(); const waitForBlocks = 4; expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address}); - await token.scheduleAfter(scheduledId, waitForBlocks) + await token.scheduleAfter(waitForBlocks, {scheduledId}) .transfer(alice, {Substrate: bob.address}); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -115,13 +121,13 @@ repetitions: 2, }; - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = helper.arrange.makeScheduledId(); const amount = 1n * helper.balance.getOneTokenNominal(); const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address); - await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic}) + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) .balance.transferToSubstrate(alice, bob.address, amount); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -146,11 +152,16 @@ ); }); - itSub('scheduler will not insert more tasks than allowed', async ({helper}) => { + itSched('scheduler will not insert more tasks than allowed', async (scheduleKind, {helper}) => { const maxScheduledPerBlock = 50; - const scheduledIds = await helper.arrange.makeScheduledIds(maxScheduledPerBlock + 1); - const fillScheduledIds = scheduledIds.slice(0, maxScheduledPerBlock); - const extraScheduledId = scheduledIds[maxScheduledPerBlock]; + let fillScheduledIds = new Array(maxScheduledPerBlock); + let extraScheduledId = undefined; + + if (scheduleKind == 'named') { + const scheduledIds = helper.arrange.makeScheduledIds(maxScheduledPerBlock + 1); + fillScheduledIds = scheduledIds.slice(0, maxScheduledPerBlock); + extraScheduledId = scheduledIds[maxScheduledPerBlock]; + } // Since the dev node has Instant Seal, // we need a larger gap between the current block and the target one. @@ -168,12 +179,12 @@ // Fill the target block for (let i = 0; i < maxScheduledPerBlock; i++) { - await helper.scheduler.scheduleAt(fillScheduledIds[i], executionBlock) + await helper.scheduler.scheduleAt(executionBlock, {scheduledId: fillScheduledIds[i]}) .balance.transferToSubstrate(superuser, bob.address, amount); } // Try to schedule a task into a full block - await expect(helper.scheduler.scheduleAt(extraScheduledId, executionBlock) + await expect(helper.scheduler.scheduleAt(executionBlock, {scheduledId: extraScheduledId}) .balance.transferToSubstrate(superuser, bob.address, amount)) .to.be.rejectedWith(/scheduler\.AgendaIsExhausted/); @@ -187,8 +198,8 @@ expect(diff).to.be.equal(amount * BigInt(maxScheduledPerBlock)); }); - itSub.ifWithPallets('Scheduled tasks are transactional', [Pallets.TestUtils], async ({helper}) => { - const scheduledId = await helper.arrange.makeScheduledId(); + itSched.ifWithPallets('Scheduled tasks are transactional', [Pallets.TestUtils], async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; const waitForBlocks = 4; const initTestVal = 42; @@ -196,7 +207,7 @@ await helper.testUtils.setTestValue(alice, initTestVal); - await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks) + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId}) .testUtils.setTestValueAndRollback(alice, changedTestVal); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -207,8 +218,8 @@ .to.be.equal(initTestVal); }); - itSub.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.TestUtils], async function({helper}) { - const scheduledId = await helper.arrange.makeScheduledId(); + itSched.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.TestUtils], async function(scheduleKind, {helper}) { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; const waitForBlocks = 4; const periodic = { period: 2, @@ -221,7 +232,7 @@ const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen)) .partialFee.toBigInt(); - await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic}) + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) .testUtils.justTakeFee(alice); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -267,7 +278,7 @@ const [ scheduledId, scheduledCancelId, - ] = await helper.arrange.makeScheduledIds(2); + ] = helper.arrange.makeScheduledIds(2); const periodic = { period: 5, @@ -280,14 +291,14 @@ await helper.testUtils.setTestValue(alice, initTestVal); - await helper.scheduler.scheduleAt(scheduledId, firstExecutionBlockNumber, {periodic}) + await helper.scheduler.scheduleAt(firstExecutionBlockNumber, {scheduledId, periodic}) .testUtils.incTestValue(alice); // Cancel the inc tx after 2 executions // *in the same block* in which the second execution is scheduled await helper.scheduler.scheduleAt( - scheduledCancelId, firstExecutionBlockNumber + periodic.period, + {scheduledId: scheduledCancelId}, ).scheduler.cancelScheduled(alice, scheduledId); await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber); @@ -310,7 +321,7 @@ }); itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.TestUtils], async ({helper}) => { - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = helper.arrange.makeScheduledId(); const waitForBlocks = 4; const periodic = { period: 2, @@ -322,7 +333,7 @@ await helper.testUtils.setTestValue(alice, initTestVal); - await helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {periodic}) + await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic}) .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -349,10 +360,10 @@ const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'}); const token = await collection.mintToken(bob); - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = helper.arrange.makeScheduledId(); const waitForBlocks = 4; - await token.scheduleAfter(scheduledId, waitForBlocks) + await token.scheduleAfter(waitForBlocks, {scheduledId}) .transfer(bob, {Substrate: alice.address}); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -363,8 +374,8 @@ expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); }); - itSub('Root can set prioritized scheduled operation', async ({helper}) => { - const scheduledId = await helper.arrange.makeScheduledId(); + itSched('Root can set prioritized scheduled operation', async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; const waitForBlocks = 4; const amount = 42n * helper.balance.getOneTokenNominal(); @@ -372,7 +383,7 @@ const balanceBefore = await helper.balance.getSubstrate(charlie.address); await helper.getSudo() - .scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42}) + .scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42}) .balance.forceTransferToSubstrate(superuser, bob.address, charlie.address, amount); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -390,10 +401,10 @@ const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'}); const token = await collection.mintToken(bob); - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = helper.arrange.makeScheduledId(); const waitForBlocks = 6; - await token.scheduleAfter(scheduledId, waitForBlocks) + await token.scheduleAfter(waitForBlocks, {scheduledId}) .transfer(bob, {Substrate: alice.address}); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -419,7 +430,7 @@ const [ scheduledFirstId, scheduledSecondId, - ] = await helper.arrange.makeScheduledIds(2); + ] = helper.arrange.makeScheduledIds(2); const currentBlockNumber = await helper.chain.getLatestBlockNumber(); const blocksBeforeExecution = 6; @@ -436,11 +447,17 @@ const amount = 1n * helper.balance.getOneTokenNominal(); // Scheduler a task with a lower priority first, then with a higher priority - await helper.getSudo().scheduler.scheduleAt(scheduledFirstId, firstExecutionBlockNumber, {priority: prioLow, periodic}) - .balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount); + await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, { + scheduledId: scheduledFirstId, + priority: prioLow, + periodic, + }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount); - await helper.getSudo().scheduler.scheduleAt(scheduledSecondId, firstExecutionBlockNumber, {priority: prioHigh, periodic}) - .balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount); + await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, { + scheduledId: scheduledSecondId, + priority: prioHigh, + periodic, + }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount); const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched'); @@ -467,13 +484,15 @@ expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId); }); - itSub('Periodic operations always can be rescheduled', async ({helper}) => { + itSched('Periodic operations always can be rescheduled', async (scheduleKind, {helper}) => { const maxScheduledPerBlock = 50; const numFilledBlocks = 3; - const ids = await helper.arrange.makeScheduledIds(numFilledBlocks * maxScheduledPerBlock + 1); - const periodicId = ids[0]; + const ids = helper.arrange.makeScheduledIds(numFilledBlocks * maxScheduledPerBlock + 1); + const periodicId = scheduleKind == 'named' ? ids[0] : undefined; const fillIds = ids.slice(1); + const fillScheduleFn = scheduleKind == 'named' ? 'scheduleNamed' : 'schedule'; + const initTestVal = 0; const firstExecTestVal = 1; const secondExecTestVal = 2; @@ -498,15 +517,22 @@ const scheduledTx = helper.constructApiCall('api.tx.balances.transfer', [bob.address, 1n]); const when = firstExecutionBlockNumber + period + offset; - const tx = helper.constructApiCall('api.tx.scheduler.scheduleNamed', [fillIds[i + offset * maxScheduledPerBlock], when, null, null, scheduledTx]); + const mandatoryArgs = [when, null, null, scheduledTx]; + const scheduleArgs = scheduleKind == 'named' + ? [fillIds[i + offset * maxScheduledPerBlock], ...mandatoryArgs] + : mandatoryArgs; + + const tx = helper.constructApiCall(`api.tx.scheduler.${fillScheduleFn}`, scheduleArgs); txs.push(tx); } } await helper.executeExtrinsic(alice, 'api.tx.testUtils.batchAll', [txs], true); - await helper.scheduler.scheduleAt(periodicId, firstExecutionBlockNumber, {periodic}) - .testUtils.incTestValue(alice); + await helper.scheduler.scheduleAt(firstExecutionBlockNumber, { + scheduledId: periodicId, + periodic, + }).testUtils.incTestValue(alice); await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber); expect(await helper.testUtils.testValue()).to.be.equal(firstExecTestVal); @@ -522,12 +548,12 @@ expect(await helper.testUtils.testValue()).to.be.equal(secondExecTestVal); }); - itSub('scheduled operations does not change nonce', async ({helper}) => { - const scheduledId = await helper.arrange.makeScheduledId(); + itSched('scheduled operations does not change nonce', async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; const blocksBeforeExecution = 4; await helper.scheduler - .scheduleAfter(scheduledId, blocksBeforeExecution) + .scheduleAfter(blocksBeforeExecution, {scheduledId}) .balance.transferToSubstrate(alice, bob.address, 1n); const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1; @@ -560,14 +586,14 @@ const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); const token = await collection.mintToken(alice); - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = helper.arrange.makeScheduledId(); const waitForBlocks = 4; - await token.scheduleAfter(scheduledId, waitForBlocks) + await token.scheduleAfter(waitForBlocks, {scheduledId}) .transfer(alice, {Substrate: bob.address}); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; - const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks); + const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId}); await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal())) .to.be.rejectedWith(/scheduler\.FailedToSchedule/); @@ -582,7 +608,7 @@ }); itSub("Can't cancel an operation which is not scheduled", async ({helper}) => { - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = helper.arrange.makeScheduledId(); await expect(helper.scheduler.cancelScheduled(alice, scheduledId)) .to.be.rejectedWith(/scheduler\.NotFound/); }); @@ -591,10 +617,10 @@ const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'}); const token = await collection.mintToken(alice); - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = helper.arrange.makeScheduledId(); const waitForBlocks = 4; - await token.scheduleAfter(scheduledId, waitForBlocks) + await token.scheduleAfter(waitForBlocks, {scheduledId}) .transfer(alice, {Substrate: bob.address}); const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1; @@ -606,15 +632,15 @@ expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address}); }); - itSub("Regular user can't set prioritized scheduled operation", async ({helper}) => { - const scheduledId = await helper.arrange.makeScheduledId(); + itSched("Regular user can't set prioritized scheduled operation", async (scheduleKind, {helper}) => { + const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined; const waitForBlocks = 4; const amount = 42n * helper.balance.getOneTokenNominal(); const balanceBefore = await helper.balance.getSubstrate(bob.address); - const scheduled = helper.scheduler.scheduleAfter(scheduledId, waitForBlocks, {priority: 42}); + const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42}); await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount)) .to.be.rejectedWith(/BadOrigin/); @@ -632,10 +658,10 @@ const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'}); const token = await collection.mintToken(bob); - const scheduledId = await helper.arrange.makeScheduledId(); + const scheduledId = helper.arrange.makeScheduledId(); const waitForBlocks = 4; - await token.scheduleAfter(scheduledId, waitForBlocks) + await token.scheduleAfter(waitForBlocks, {scheduledId}) .transfer(bob, {Substrate: alice.address}); const priority = 112; --- a/tests/src/util/index.ts +++ b/tests/src/util/index.ts @@ -130,6 +130,24 @@ itSubIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSubIfWithPallet(name, required, cb, {skip: true}); itSub.ifWithPallets = itSubIfWithPallet; +export type SchedKind = 'anon' | 'named'; + +export function itSched( + name: string, + cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, + opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}, +) { + itSub(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts); + itSub(name + ' (named scheduling)', (apis) => cb('named', apis), opts); +} +itSched.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSched(name, cb, {only: true}); +itSched.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any) => itSched(name, cb, {skip: true}); +itSched.ifWithPallets = itSchedIfWithPallets; + +function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) { + return itSched(name, cb, {requiredPallets: required, ...opts}); +} + export function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) { (process.env.RUN_XCM_TESTS && !opts.skip ? describe --- a/tests/src/util/playgrounds/types.ts +++ b/tests/src/util/playgrounds/types.ts @@ -172,6 +172,7 @@ } export interface ISchedulerOptions { + scheduledId?: string, priority?: number, periodic?: { period: number, --- a/tests/src/util/playgrounds/unique.dev.ts +++ b/tests/src/util/playgrounds/unique.dev.ts @@ -308,9 +308,7 @@ return encodeAddress(address); } - async makeScheduledIds(num: number): Promise { - await this.helper.wait.noScheduledTasks(); - + makeScheduledIds(num: number): string[] { function makeId(slider: number) { const scheduledIdSize = 64; const hexId = slider.toString(16); @@ -330,8 +328,8 @@ return ids; } - async makeScheduledId(): Promise { - return (await this.makeScheduledIds(1))[0]; + makeScheduledId(): string { + return (this.makeScheduledIds(1))[0]; } async captureEvents(eventSection: string, eventMethod: string): Promise { --- a/tests/src/util/playgrounds/unique.ts +++ b/tests/src/util/playgrounds/unique.ts @@ -2560,24 +2560,21 @@ } scheduleAt( - scheduledId: string, executionBlockNumber: number, options: ISchedulerOptions = {}, ) { - return this.schedule('scheduleNamed', scheduledId, executionBlockNumber, options); + return this.schedule('schedule', executionBlockNumber, options); } scheduleAfter( - scheduledId: string, blocksBeforeExecution: number, options: ISchedulerOptions = {}, ) { - return this.schedule('scheduleNamedAfter', scheduledId, blocksBeforeExecution, options); + return this.schedule('scheduleAfter', blocksBeforeExecution, options); } schedule( - scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter', - scheduledId: string, + scheduleFn: 'schedule' | 'scheduleAfter', blocksNum: number, options: ISchedulerOptions = {}, ) { @@ -2585,7 +2582,6 @@ const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase); return this.helper.clone(ScheduledHelperType, { scheduleFn, - scheduledId, blocksNum, options, }) as T; @@ -2874,16 +2870,14 @@ // eslint-disable-next-line @typescript-eslint/naming-convention function ScheduledUniqueHelper(Base: T) { return class extends Base { - scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter'; - scheduledId: string; + scheduleFn: 'schedule' | 'scheduleAfter'; blocksNum: number; options: ISchedulerOptions; constructor(...args: any[]) { const logger = args[0] as ILogger; const options = args[1] as { - scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter', - scheduledId: string, + scheduleFn: 'schedule' | 'scheduleAfter', blocksNum: number, options: ISchedulerOptions }; @@ -2891,25 +2885,42 @@ super(logger); this.scheduleFn = options.scheduleFn; - this.scheduledId = options.scheduledId; this.blocksNum = options.blocksNum; this.options = options.options; } executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise { const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams); - const extrinsic = 'api.tx.scheduler.' + this.scheduleFn; + + const mandatorySchedArgs = [ + this.blocksNum, + this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null, + this.options.priority ?? null, + scheduledTx, + ]; + + let schedArgs; + let scheduleFn; + + if (this.options.scheduledId) { + schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs]; + if (this.scheduleFn == 'schedule') { + scheduleFn = 'scheduleNamed'; + } else if (this.scheduleFn == 'scheduleAfter') { + scheduleFn = 'scheduleNamedAfter'; + } + } else { + schedArgs = mandatorySchedArgs; + scheduleFn = this.scheduleFn; + } + + const extrinsic = 'api.tx.scheduler.' + scheduleFn; + return super.executeExtrinsic( sender, extrinsic, - [ - this.scheduledId, - this.blocksNum, - this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null, - this.options.priority ?? null, - scheduledTx, - ], + schedArgs, expectSuccess, ); } @@ -3046,20 +3057,18 @@ } scheduleAt( - scheduledId: string, executionBlockNumber: number, options: ISchedulerOptions = {}, ) { - const scheduledHelper = this.helper.scheduler.scheduleAt(scheduledId, executionBlockNumber, options); + const scheduledHelper = this.helper.scheduler.scheduleAt(executionBlockNumber, options); return new UniqueBaseCollection(this.collectionId, scheduledHelper); } scheduleAfter( - scheduledId: string, blocksBeforeExecution: number, options: ISchedulerOptions = {}, ) { - const scheduledHelper = this.helper.scheduler.scheduleAfter(scheduledId, blocksBeforeExecution, options); + const scheduledHelper = this.helper.scheduler.scheduleAfter(blocksBeforeExecution, options); return new UniqueBaseCollection(this.collectionId, scheduledHelper); } @@ -3155,20 +3164,18 @@ } scheduleAt( - scheduledId: string, executionBlockNumber: number, options: ISchedulerOptions = {}, ) { - const scheduledHelper = this.helper.scheduler.scheduleAt(scheduledId, executionBlockNumber, options); + const scheduledHelper = this.helper.scheduler.scheduleAt(executionBlockNumber, options); return new UniqueNFTCollection(this.collectionId, scheduledHelper); } scheduleAfter( - scheduledId: string, blocksBeforeExecution: number, options: ISchedulerOptions = {}, ) { - const scheduledHelper = this.helper.scheduler.scheduleAfter(scheduledId, blocksBeforeExecution, options); + const scheduledHelper = this.helper.scheduler.scheduleAfter(blocksBeforeExecution, options); return new UniqueNFTCollection(this.collectionId, scheduledHelper); } @@ -3260,20 +3267,18 @@ } scheduleAt( - scheduledId: string, executionBlockNumber: number, options: ISchedulerOptions = {}, ) { - const scheduledHelper = this.helper.scheduler.scheduleAt(scheduledId, executionBlockNumber, options); + const scheduledHelper = this.helper.scheduler.scheduleAt(executionBlockNumber, options); return new UniqueRFTCollection(this.collectionId, scheduledHelper); } scheduleAfter( - scheduledId: string, blocksBeforeExecution: number, options: ISchedulerOptions = {}, ) { - const scheduledHelper = this.helper.scheduler.scheduleAfter(scheduledId, blocksBeforeExecution, options); + const scheduledHelper = this.helper.scheduler.scheduleAfter(blocksBeforeExecution, options); return new UniqueRFTCollection(this.collectionId, scheduledHelper); } @@ -3329,20 +3334,18 @@ } scheduleAt( - scheduledId: string, executionBlockNumber: number, options: ISchedulerOptions = {}, ) { - const scheduledHelper = this.helper.scheduler.scheduleAt(scheduledId, executionBlockNumber, options); + const scheduledHelper = this.helper.scheduler.scheduleAt(executionBlockNumber, options); return new UniqueFTCollection(this.collectionId, scheduledHelper); } scheduleAfter( - scheduledId: string, blocksBeforeExecution: number, options: ISchedulerOptions = {}, ) { - const scheduledHelper = this.helper.scheduler.scheduleAfter(scheduledId, blocksBeforeExecution, options); + const scheduledHelper = this.helper.scheduler.scheduleAfter(blocksBeforeExecution, options); return new UniqueFTCollection(this.collectionId, scheduledHelper); } @@ -3388,20 +3391,18 @@ } scheduleAt( - scheduledId: string, executionBlockNumber: number, options: ISchedulerOptions = {}, ) { - const scheduledCollection = this.collection.scheduleAt(scheduledId, executionBlockNumber, options); + const scheduledCollection = this.collection.scheduleAt(executionBlockNumber, options); return new UniqueBaseToken(this.tokenId, scheduledCollection); } scheduleAfter( - scheduledId: string, blocksBeforeExecution: number, options: ISchedulerOptions = {}, ) { - const scheduledCollection = this.collection.scheduleAfter(scheduledId, blocksBeforeExecution, options); + const scheduledCollection = this.collection.scheduleAfter(blocksBeforeExecution, options); return new UniqueBaseToken(this.tokenId, scheduledCollection); } @@ -3468,20 +3469,18 @@ } scheduleAt( - scheduledId: string, executionBlockNumber: number, options: ISchedulerOptions = {}, ) { - const scheduledCollection = this.collection.scheduleAt(scheduledId, executionBlockNumber, options); + const scheduledCollection = this.collection.scheduleAt(executionBlockNumber, options); return new UniqueNFToken(this.tokenId, scheduledCollection); } scheduleAfter( - scheduledId: string, blocksBeforeExecution: number, options: ISchedulerOptions = {}, ) { - const scheduledCollection = this.collection.scheduleAfter(scheduledId, blocksBeforeExecution, options); + const scheduledCollection = this.collection.scheduleAfter(blocksBeforeExecution, options); return new UniqueNFToken(this.tokenId, scheduledCollection); } @@ -3543,20 +3542,18 @@ } scheduleAt( - scheduledId: string, executionBlockNumber: number, options: ISchedulerOptions = {}, ) { - const scheduledCollection = this.collection.scheduleAt(scheduledId, executionBlockNumber, options); + const scheduledCollection = this.collection.scheduleAt(executionBlockNumber, options); return new UniqueRFToken(this.tokenId, scheduledCollection); } scheduleAfter( - scheduledId: string, blocksBeforeExecution: number, options: ISchedulerOptions = {}, ) { - const scheduledCollection = this.collection.scheduleAfter(scheduledId, blocksBeforeExecution, options); + const scheduledCollection = this.collection.scheduleAfter(blocksBeforeExecution, options); return new UniqueRFToken(this.tokenId, scheduledCollection); }