git.delta.rocks / unique-network / refs/commits / 12b8d3cee6e7

difftreelog

test(Scheduler) EVM test + amendments

Fahrrader2022-03-31parent: #c2a4d96.patch.diff
in: master

5 files changed

modifiedpallets/scheduler/src/lib.rsdiffbeforeafterboth
440 Err(err) => Err(err.error),440 Err(err) => Err(err.error),
441 },441 },
442 Err(_) => {442 Err(_) => {
443 log::info!(443 log::error!(
444 target: "runtime::scheduler",444 target: "runtime::scheduler",
445 "Warning: Scheduler has failed to execute a post-dispatch transaction. \445 "Warning: Scheduler has failed to execute a post-dispatch transaction. \
446 This block might have become invalid.");446 This block might have become invalid.");
modifiedtests/package.jsondiffbeforeafterboth
64 "testSetVariableMetadataSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetadataSponsoringRateLimit.test.ts",64 "testSetVariableMetadataSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetadataSponsoringRateLimit.test.ts",
65 "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts",65 "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts",
66 "testScheduler": "mocha --timeout 9999999 -r ts-node/register ./**/scheduler.test.ts",66 "testScheduler": "mocha --timeout 9999999 -r ts-node/register ./**/scheduler.test.ts",
67 "testSchedulingEVM": "mocha --timeout 9999999 -r ts-node/register ./**/eth/scheduling.test.ts",
67 "testXcmTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/xcmTransfer.test.ts",68 "testXcmTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/xcmTransfer.test.ts",
68 "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",69 "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",
69 "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",70 "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",
addedtests/src/eth/scheduling.test.tsdiffbeforeafterboth

no changes

modifiedtests/src/scheduler.test.tsdiffbeforeafterboth
20import {20import {
21 default as usingApi, 21 default as usingApi,
22 submitTransactionAsync,22 submitTransactionAsync,
23 submitTransactionExpectFailAsync,
24} from './substrate/substrate-api';23} from './substrate/substrate-api';
25import {24import {
26 createItemExpectSuccess,25 createItemExpectSuccess,
40 scheduleTransferFundsPeriodicExpectSuccess,39 scheduleTransferFundsPeriodicExpectSuccess,
41 getFreeBalance,40 getFreeBalance,
42 confirmSponsorshipByKeyExpectSuccess,41 confirmSponsorshipByKeyExpectSuccess,
42 scheduleExpectFailure,
43} from './util/helpers';43} from './util/helpers';
44import {IKeyringPair} from '@polkadot/types/types';44import {IKeyringPair} from '@polkadot/types/types';
45import {getBalanceSingle} from './substrate/get-balance';
4645
47chai.use(chaiAsPromised);46chai.use(chaiAsPromised);
4847
49describe('Scheduling token and balance transfers', () => {48describe('Scheduling token and balance transfers', () => {
50 let alice: IKeyringPair;49 let alice: IKeyringPair;
51 let bob: IKeyringPair;50 let bob: IKeyringPair;
51 let scheduledIdBase: string;
52 let scheduledIdSlider: number;
5253
53 before(async() => {54 before(async() => {
54 await usingApi(async () => {55 await usingApi(async () => {
55 alice = privateKey('//Alice');56 alice = privateKey('//Alice');
56 bob = privateKey('//Bob');57 bob = privateKey('//Bob');
57 });58 });
59
60 scheduledIdBase = '0x' + '0'.repeat(31);
61 scheduledIdSlider = 0;
58 });62 });
63
64 // Loop scheduledId around 10. Unless there are concurrent tasks with long periods/repetitions, tests' tasks' ids shouldn't ovelap.
65 function makeScheduledId(): string {
66 return scheduledIdBase + ((scheduledIdSlider++) % 10);
67 }
5968
60 it('Can schedule a transfer of an owned token with delay', async () => {69 it('Can schedule a transfer of an owned token with delay', async () => {
61 await usingApi(async () => {70 await usingApi(async () => {
62 // nft
63 const nftCollectionId = await createCollectionExpectSuccess();71 const nftCollectionId = await createCollectionExpectSuccess();
64 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');72 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');
65 await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);73 await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);
66 await confirmSponsorshipExpectSuccess(nftCollectionId);74 await confirmSponsorshipExpectSuccess(nftCollectionId);
6775
68 await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4);76 await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4, makeScheduledId());
69 });77 });
70 });78 });
7179
72 it('Can transfer funds periodically', async () => {80 it('Can transfer funds periodically', async () => {
73 await usingApi(async (api) => {81 await usingApi(async () => {
74 const waitForBlocks = 4;82 const waitForBlocks = 4;
75 const period = 2;83 const period = 2;
76 await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, period, 2);84 await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, makeScheduledId(), period, 2);
77 const bobsBalanceBefore = await getBalanceSingle(api, bob.address);85 const bobsBalanceBefore = await getFreeBalance(bob);
7886
79 // discounting already waited-for operations87 // discounting already waited-for operations
80 await waitNewBlocks(waitForBlocks - 2);88 await waitNewBlocks(waitForBlocks - 2);
81 const bobsBalanceAfterFirst = await getBalanceSingle(api, bob.address);89 const bobsBalanceAfterFirst = await getFreeBalance(bob);
82 expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;90 expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;
8391
84 await waitNewBlocks(period);92 await waitNewBlocks(period);
85 const bobsBalanceAfterSecond = await getBalanceSingle(api, bob.address);93 const bobsBalanceAfterSecond = await getFreeBalance(bob);
86 expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;94 expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;
87 });95 });
88 });96 });
95 await usingApi(async () => {103 await usingApi(async () => {
96 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);104 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
97105
98 const aliceBalanceBefore = await getFreeBalance(alice);106 const bobBalanceBefore = await getFreeBalance(bob);
107 const waitForBlocks = 4;
99 // no need to wait to check, fees must be deducted on scheduling, immediately108 // no need to wait to check, fees must be deducted on scheduling, immediately
100 await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, 4);109 await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, waitForBlocks, makeScheduledId());
101 const aliceBalanceAfter = await getFreeBalance(alice);110 const bobBalanceAfter = await getFreeBalance(bob);
111 // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;
102 expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;112 expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
113 // wait for sequentiality matters
114 await waitNewBlocks(waitForBlocks - 1);
103 });115 });
104 });116 });
105
106 /*it('Can\'t schedule a transaction with no funds', async () => {
107 await usingApi(async (api) => {
108 // Find an empty, unused account
109 const zeroBalance = await findUnusedAddress(api);
110
111 const collectionId = await createCollectionExpectSuccess();
112 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
113
114 await transferExpectSuccess(collectionId, tokenId, alice, zeroBalance);
115
116 await scheduleTransferAndWaitExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, 4);
117 });
118 });*/
119117
120 it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {118 it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {
121 await usingApi(async (api) => {119 await usingApi(async (api) => {
137135
138 // Schedule transfer of the NFT a few blocks ahead136 // Schedule transfer of the NFT a few blocks ahead
139 const waitForBlocks = 5;137 const waitForBlocks = 5;
140 await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks);138 await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, makeScheduledId());
141139
142 // Get rid of the account's funds before the scheduled transaction takes place140 // Get rid of the account's funds before the scheduled transaction takes place
143 const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?141 const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);
144 const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);
145 const events = await submitTransactionAsync(alice, sudoTx);142 const events = await submitTransactionAsync(zeroBalance, balanceTx2);
146 expect(getGenericResult(events).success).to.be.true;143 expect(getGenericResult(events).success).to.be.true;
144 /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?
145 const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);
146 const events = await submitTransactionAsync(alice, sudoTx);
147 expect(getGenericResult(events).success).to.be.true;*/
147148
148 // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions149 // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions
149 await waitNewBlocks(waitForBlocks - 3);150 await waitNewBlocks(waitForBlocks - 3);
158 await usingApi(async (api) => {159 await usingApi(async (api) => {
159 const zeroBalance = await findUnusedAddress(api);160 const zeroBalance = await findUnusedAddress(api);
160
161 /*await setCollectionLimitsExpectSuccess(alice, nftCollectionId, {
162 sponsoredDataRateLimit: 2,
163 });*/
164 //console.log(await getDetailedCollectionInfo(api, nftCollectionId));
165 const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);161 const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
166 await submitTransactionAsync(alice, balanceTx);162 await submitTransactionAsync(alice, balanceTx);
167163
170166
171 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);167 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
172168
169 const waitForBlocks = 5;
173 await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, 5);170 await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, makeScheduledId());
174171
175 const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);172 const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);
176 const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);173 const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);
177 const events = await submitTransactionAsync(alice, sudoTx);174 const events = await submitTransactionAsync(alice, sudoTx);
178 expect(getGenericResult(events).success).to.be.true;175 expect(getGenericResult(events).success).to.be.true;
179176
180 // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions177 // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions
181 await waitNewBlocks(2);178 await waitNewBlocks(waitForBlocks - 3);
182179
183 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));180 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));
184 });181 });
185 });182 });
183
184 it.skip('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {
185 const collectionId = await createCollectionExpectSuccess();
186 await setCollectionSponsorExpectSuccess(collectionId, bob.address);
187 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
188
189 await usingApi(async (api) => {
190 const zeroBalance = await findUnusedAddress(api);
191
192 await enablePublicMintingExpectSuccess(alice, collectionId);
193 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
194
195 const bobBalanceBefore = await getFreeBalance(bob);
196
197 const createData = {nft: {const_data: [], variable_data: []}};
198 const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);
199
200 /*const badTransaction = async function () {
201 await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
202 };
203 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/
204
205 await scheduleExpectFailure(creationTx, zeroBalance, 3, makeScheduledId(), 1, 3);
206
207 expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);
208 });
209 });
186});210});
187
188describe.skip('Scheduling EVM smart contracts', () => {
189 let alice: IKeyringPair;
190 let bob: IKeyringPair;
191
192 before(async() => {
193 await usingApi(async () => {
194 alice = privateKey('//Alice');
195 bob = privateKey('//Bob');
196 });
197 });
198
199 // todo contract testing
200 it.skip('NFT: Sponsoring of transfers is rate limited', async () => {
201 const collectionId = await createCollectionExpectSuccess();
202 await setCollectionSponsorExpectSuccess(collectionId, bob.address);
203 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
204
205 await usingApi(async (api) => {
206 // Find unused address
207 const zeroBalance = await findUnusedAddress(api);
208
209 // Mint token for alice
210 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
211
212 // Transfer this token from Alice to unused address and back
213 // Alice to Zero gets sponsored
214 const aliceToZero = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);
215 const events1 = await submitTransactionAsync(alice, aliceToZero);
216 const result1 = getGenericResult(events1);
217
218 // Second transfer should fail
219 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
220 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);
221 const badTransaction = async function () {
222 await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
223 };
224 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');
225 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
226
227 // Try again after Zero gets some balance - now it should succeed
228 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
229 await submitTransactionAsync(alice, balancetx);
230 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);
231 const result2 = getGenericResult(events2);
232
233 expect(result1.success).to.be.true;
234 expect(result2.success).to.be.true;
235 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
236 });
237 });
238});
239211
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
845 return balance;845 return balance;
846}846}
847
848export async function
849scheduleExpectSuccess(
850 operationTx: any,
851 sender: IKeyringPair,
852 blockSchedule: number,
853 scheduledId: string,
854 period = 1,
855 repetitions = 1,
856) {
857 await usingApi(async (api: ApiPromise) => {
858 const blockNumber: number | undefined = await getBlockNumber(api);
859 const expectedBlockNumber = blockNumber + blockSchedule;
860
861 expect(blockNumber).to.be.greaterThan(0);
862 const scheduleTx = api.tx.scheduler.scheduleNamed( // schedule
863 scheduledId,
864 expectedBlockNumber,
865 repetitions > 1 ? [period, repetitions] : null,
866 0,
867 {value: operationTx as any},
868 );
869
870 const events = await submitTransactionAsync(sender, scheduleTx);
871 expect(getGenericResult(events).success).to.be.true;
872 });
873}
874
875export async function
876scheduleExpectFailure(
877 operationTx: any,
878 sender: IKeyringPair,
879 blockSchedule: number,
880 scheduledId: string,
881 period = 1,
882 repetitions = 1,
883) {
884 await usingApi(async (api: ApiPromise) => {
885 const blockNumber: number | undefined = await getBlockNumber(api);
886 const expectedBlockNumber = blockNumber + blockSchedule;
887
888 expect(blockNumber).to.be.greaterThan(0);
889 const scheduleTx = api.tx.scheduler.scheduleNamed( // schedule
890 scheduledId,
891 expectedBlockNumber,
892 repetitions <= 1 ? null : [period, repetitions],
893 0,
894 {value: operationTx as any},
895 );
896
897 //const events =
898 await expect(submitTransactionExpectFailAsync(sender, scheduleTx)).to.be.rejected;
899 //expect(getGenericResult(events).success).to.be.false;
900 });
901}
847902
848export async function903export async function
849scheduleTransferAndWaitExpectSuccess(904scheduleTransferAndWaitExpectSuccess(
853 recipient: IKeyringPair,908 recipient: IKeyringPair,
854 value: number | bigint = 1,909 value: number | bigint = 1,
855 blockSchedule: number,910 blockSchedule: number,
911 scheduledId: string,
856) {912) {
857 await usingApi(async (api: ApiPromise) => {913 await usingApi(async (api: ApiPromise) => {
858 await scheduleTransferExpectSuccess(collectionId, tokenId, sender, recipient, value, blockSchedule);914 await scheduleTransferExpectSuccess(collectionId, tokenId, sender, recipient, value, blockSchedule, scheduledId);
859915
860 const recipientBalanceBefore = (await api.query.system.account(recipient.address)).data.free.toBigInt();916 const recipientBalanceBefore = (await api.query.system.account(recipient.address)).data.free.toBigInt();
861 console.log(await getFreeBalance(sender));
862917
863 // sleep for n + 1 blocks918 // sleep for n + 1 blocks
864 await waitNewBlocks(blockSchedule + 1);919 await waitNewBlocks(blockSchedule + 1);
878 recipient: IKeyringPair,933 recipient: IKeyringPair,
879 value: number | bigint = 1,934 value: number | bigint = 1,
880 blockSchedule: number,935 blockSchedule: number,
936 scheduledId: string,
881) {937) {
882 await usingApi(async (api: ApiPromise) => {938 await usingApi(async (api: ApiPromise) => {
883 const blockNumber: number | undefined = await getBlockNumber(api);
884 const expectedBlockNumber = blockNumber + blockSchedule;
885
886 expect(blockNumber).to.be.greaterThan(0);
887 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);939 const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);
888 const scheduleTx = api.tx.scheduler.schedule(expectedBlockNumber, null, 0, transferTx as any);
889940
890 const events = await submitTransactionAsync(sender, scheduleTx);941 await scheduleExpectSuccess(transferTx, sender, blockSchedule, scheduledId);
891 expect(getGenericResult(events).success).to.be.true;
892942
893 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(sender.address));943 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(sender.address));
894 });944 });
900 sender: IKeyringPair,950 sender: IKeyringPair,
901 recipient: IKeyringPair,951 recipient: IKeyringPair,
902 blockSchedule: number,952 blockSchedule: number,
953 scheduledId: string,
903 period: number,954 period: number,
904 repetitions: number,955 repetitions: number,
905) {956) {
906 await usingApi(async (api: ApiPromise) => {957 await usingApi(async (api: ApiPromise) => {
907 const blockNumber: number | undefined = await getBlockNumber(api);
908 const expectedBlockNumber = blockNumber + blockSchedule;
909
910 const balanceBefore = await getFreeBalance(recipient);
911
912 expect(blockNumber).to.be.greaterThan(0);
913 const transferTx = api.tx.balances.transfer(recipient.address, amount);958 const transferTx = api.tx.balances.transfer(recipient.address, amount);
959
914 const scheduleTx = api.tx.scheduler.schedule(expectedBlockNumber, [period, repetitions], 0, transferTx as any);960 const balanceBefore = await getFreeBalance(recipient);
915961
916 const events = await submitTransactionAsync(sender, scheduleTx);962 await scheduleExpectSuccess(transferTx, sender, blockSchedule, scheduledId, period, repetitions);
917 expect(getGenericResult(events).success).to.be.true;
918963
919 expect(await getFreeBalance(recipient)).to.be.equal(balanceBefore);964 expect(await getFreeBalance(recipient)).to.be.equal(balanceBefore);
920 });965 });