Skip to content

Commit 934cff4

Browse files
authored
Migrate 00_matchorders.js file - part 2 (#113)
2 parents dd7627e + fe6f7db commit 934cff4

File tree

4 files changed

+291
-84
lines changed

4 files changed

+291
-84
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
- Migrate unit test files to Typescript & Hardhat:
77
- IexecPoco2
88
- contribute (#108, #109, #110)
9-
- IexecPoco/00_matchorders.js (#107)
9+
- IexecPoco1 (#107, #113)
1010
- Add `.test` suffix to unit test files (#106)
1111
- ENSIntegration (#105)
1212
- IexecOrderManagement (#101, #102, #103, #104)

test/byContract/IexecPoco/IexecPoco1.test.ts

Lines changed: 257 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-FileCopyrightText: 2020-2024 IEXEC BLOCKCHAIN TECH <[email protected]>
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import { AddressZero } from '@ethersproject/constants';
45
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
56
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
67
import { ethers, expect } from 'hardhat';
@@ -9,8 +10,11 @@ import {
910
IexecInterfaceNative,
1011
IexecInterfaceNative__factory,
1112
IexecPocoAccessors__factory,
13+
TestClient,
14+
TestClient__factory,
1215
} from '../../../typechain';
1316
import {
17+
IexecOrders,
1418
OrdersActors,
1519
OrdersAssets,
1620
OrdersPrices,
@@ -21,7 +25,7 @@ import { getDealId, getIexecAccounts, setNextBlockTimestamp } from '../../../uti
2125
import { IexecWrapper } from '../../utils/IexecWrapper';
2226

2327
/*
24-
* TODO add TEE tests
28+
* TODO add Standard tests.
2529
*/
2630

2731
const appPrice = 1000;
@@ -54,6 +58,9 @@ describe('IexecPoco1', () => {
5458
let ordersActors: OrdersActors;
5559
let ordersAssets: OrdersAssets;
5660
let ordersPrices: OrdersPrices;
61+
let orders: IexecOrders;
62+
let randomAddress: string;
63+
let randomContract: TestClient;
5764

5865
beforeEach('Deploy', async () => {
5966
// Deploy all contracts
@@ -94,6 +101,23 @@ describe('IexecPoco1', () => {
94101
dataset: datasetPrice,
95102
workerpool: workerpoolPrice,
96103
};
104+
({ orders } = buildOrders({
105+
assets: ordersAssets,
106+
prices: ordersPrices,
107+
requester: requester.address,
108+
beneficiary: beneficiary.address,
109+
tag: teeDealTag,
110+
volume: volume,
111+
callback: callback,
112+
trust: trust,
113+
category: category,
114+
params: params,
115+
}));
116+
randomAddress = ethers.Wallet.createRandom().address;
117+
randomContract = await new TestClient__factory()
118+
.connect(anyone)
119+
.deploy()
120+
.then((contract) => contract.deployed());
97121
// TODO check why this is done in 00_matchorders.js
98122
// await Workerpool__factory.connect(workerpoolAddress, scheduler)
99123
// .changePolicy(35, 5)
@@ -106,13 +130,15 @@ describe('IexecPoco1', () => {
106130
describe('Verify presignature or signature', () => {});
107131

108132
describe('Match orders', () => {
109-
it('Should match orders with all assets (Standard)', async () => {
133+
it('[TEE] Should match orders with all assets, callback, and BoT', async () => {
134+
// Recreate orders here instead of using the default ones created
135+
// in beforeEach to make this test as explicit as possible.
110136
const { orders } = buildOrders({
111137
assets: ordersAssets,
138+
prices: ordersPrices,
112139
requester: requester.address,
113140
beneficiary: beneficiary.address,
114-
tag: standardDealTag,
115-
prices: ordersPrices,
141+
tag: teeDealTag,
116142
volume: volume,
117143
callback: callback,
118144
trust: trust,
@@ -158,16 +184,6 @@ describe('IexecPoco1', () => {
158184
dealId,
159185
);
160186
const tx = iexecPocoAsRequester.matchOrders(...orders.toArray());
161-
await expect(tx)
162-
.to.emit(iexecPoco, 'OrdersMatched')
163-
.withArgs(
164-
dealId,
165-
appOrderHash,
166-
datasetOrderHash,
167-
workerpoolOrderHash,
168-
requestOrderHash,
169-
volume,
170-
);
171187
// Check balances and frozen after.
172188
await expect(tx).to.changeTokenBalances(
173189
iexecPoco,
@@ -178,6 +194,17 @@ describe('IexecPoco1', () => {
178194
// See https://github.com/NomicFoundation/hardhat/blob/main/packages/hardhat-chai-matchers/src/internal/changeTokenBalance.ts#L42
179195
expect(await iexecPoco.frozenOf(requester.address)).to.equal(dealPrice);
180196
expect(await iexecPoco.frozenOf(scheduler.address)).to.equal(schedulerStake);
197+
// Check events.
198+
await expect(tx)
199+
.to.emit(iexecPoco, 'OrdersMatched')
200+
.withArgs(
201+
dealId,
202+
appOrderHash,
203+
datasetOrderHash,
204+
workerpoolOrderHash,
205+
requestOrderHash,
206+
volume,
207+
);
181208
// Check deal
182209
const deal = await iexecPoco.viewDeal(dealId);
183210
expect(deal.app.pointer).to.equal(appAddress);
@@ -191,7 +218,7 @@ describe('IexecPoco1', () => {
191218
expect(deal.workerpool.price.toNumber()).to.equal(workerpoolPrice);
192219
expect(deal.trust.toNumber()).to.equal(trust);
193220
expect(deal.category.toNumber()).to.equal(category);
194-
expect(deal.tag).to.equal(standardDealTag);
221+
expect(deal.tag).to.equal(teeDealTag);
195222
expect(deal.requester).to.equal(requester.address);
196223
expect(deal.beneficiary).to.equal(beneficiary.address);
197224
expect(deal.callback).to.equal(callback);
@@ -204,10 +231,222 @@ describe('IexecPoco1', () => {
204231
expect(deal.sponsor).to.equal(requester.address);
205232
});
206233

207-
it('[TODO] Should match orders with all assets and callback (Standard)', () => {});
208-
it('[TODO] Should match orders without dataset (Standard)', () => {});
209-
it('[TODO] Should match orders without dataset and with callback (Standard)', () => {});
234+
it('[TEE] Should match orders without callback', async () => {
235+
orders.requester.callback = AddressZero;
236+
await depositForRequesterAndSchedulerWithDefaultPrices();
237+
// Sign and match orders.
238+
await signOrders(iexecWrapper.getDomain(), orders, ordersActors);
239+
const dealId = getDealId(iexecWrapper.getDomain(), orders.requester, taskIndex);
240+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.emit(
241+
iexecPoco,
242+
'OrdersMatched',
243+
);
244+
// Check deal
245+
const deal = await iexecPoco.viewDeal(dealId);
246+
expect(deal.callback).to.equal(AddressZero);
247+
});
248+
249+
it('[TEE] Should match orders without dataset', async () => {
250+
orders.dataset.dataset = AddressZero;
251+
orders.requester.dataset = AddressZero;
252+
// Set dataset volume lower than other assets to make sure
253+
// it does not impact final volume computation.
254+
orders.dataset.volume = volume - 1;
255+
// Compute prices, stakes, rewards, ...
256+
const dealPrice = (appPrice + workerpoolPrice) * volume; // no dataset price
257+
const schedulerStake = await iexecWrapper.computeSchedulerDealStake(
258+
workerpoolPrice,
259+
volume,
260+
);
261+
// Deposit required amounts.
262+
await iexecWrapper.depositInIexecAccount(requester, dealPrice);
263+
await iexecWrapper.depositInIexecAccount(scheduler, schedulerStake);
264+
// Sign and match orders.
265+
await signOrders(iexecWrapper.getDomain(), orders, ordersActors);
266+
const dealId = getDealId(iexecWrapper.getDomain(), orders.requester, taskIndex);
267+
const tx = iexecPocoAsRequester.matchOrders(...orders.toArray());
268+
// Check balances and frozen.
269+
// Dataset price shouldn't be included.
270+
await expect(tx).to.changeTokenBalances(
271+
iexecPoco,
272+
[iexecPoco, requester, scheduler],
273+
[dealPrice + schedulerStake, -dealPrice, -schedulerStake],
274+
);
275+
expect(await iexecPoco.frozenOf(requester.address)).to.equal(dealPrice);
276+
// Check events.
277+
await expect(tx).to.emit(iexecPoco, 'OrdersMatched');
278+
// Check deal
279+
const deal = await iexecPoco.viewDeal(dealId);
280+
expect(deal.dataset.pointer).to.equal(AddressZero);
281+
expect(deal.dataset.owner).to.equal(AddressZero);
282+
expect(deal.dataset.price.toNumber()).to.equal(0);
283+
// BoT size should not be impacted even if the dataset order is the order with the lowest volume
284+
expect(deal.botSize.toNumber()).to.equal(volume);
285+
});
286+
287+
it('[TODO][TEE] Should match orders with restrictions', async () => {});
288+
289+
it('[TEE] Should fail when categories are different', async () => {
290+
orders.requester.category = category + 1; // Valid but different category.
291+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
292+
'iExecV5-matchOrders-0x00',
293+
);
294+
});
295+
296+
it('[TEE] Should fail when category is unknown', async () => {
297+
const lastCategoryIndex = (await iexecPoco.countCategory()).toNumber() - 1;
298+
orders.requester.category = lastCategoryIndex + 1;
299+
orders.workerpool.category = lastCategoryIndex + 1;
300+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
301+
'iExecV5-matchOrders-0x01',
302+
);
303+
});
304+
305+
it('[TEE] Should fail when requested trust is above workerpool trust', async () => {
306+
orders.requester.trust = Number(orders.workerpool.trust) + 1;
307+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
308+
'iExecV5-matchOrders-0x02',
309+
);
310+
});
311+
312+
it('[TEE] Should fail when app max price is less than app price', async () => {
313+
orders.requester.appmaxprice = Number(orders.app.appprice) - 1;
314+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
315+
'iExecV5-matchOrders-0x03',
316+
);
317+
});
318+
319+
it('[TEE] Should fail when dataset max price is less than dataset price', async () => {
320+
orders.requester.datasetmaxprice = Number(orders.dataset.datasetprice) - 1;
321+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
322+
'iExecV5-matchOrders-0x04',
323+
);
324+
});
325+
326+
it('[TEE] Should fail when workerpool max price is less than workerpool price', async () => {
327+
orders.requester.workerpoolmaxprice = Number(orders.workerpool.workerpoolprice) - 1;
328+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
329+
'iExecV5-matchOrders-0x05',
330+
);
331+
});
332+
333+
it('[TEE] Should fail when workerpool tag does not satisfy app, dataset and request requirements', async () => {
334+
orders.app.tag = '0x0000000000000000000000000000000000000000000000000000000000000001'; // 0b0001
335+
orders.dataset.tag =
336+
'0x0000000000000000000000000000000000000000000000000000000000000002'; // 0b0010
337+
orders.requester.tag =
338+
'0x0000000000000000000000000000000000000000000000000000000000000003'; // 0b0011
339+
// Workerpool order is supposed to satisfy conditions of all actors.
340+
// Bad tag, correct tag should be 0b0011.
341+
orders.workerpool.tag =
342+
'0x0000000000000000000000000000000000000000000000000000000000000004'; // 0b0100
343+
// Match orders.
344+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
345+
'iExecV5-matchOrders-0x06',
346+
);
347+
});
348+
349+
it('[TEE] Should fail when the last bit of app tag does not satisfy dataset or request requirements', async () => {
350+
// The last bit of dataset and request tag is 1, but app tag does not set it
351+
orders.app.tag = '0x0000000000000000000000000000000000000000000000000000000000000002'; // 0b0010
352+
orders.dataset.tag =
353+
'0x0000000000000000000000000000000000000000000000000000000000000003'; // 0b0011
354+
orders.requester.tag =
355+
'0x0000000000000000000000000000000000000000000000000000000000000003'; // 0b0011
356+
// Set the workerpool tag in a way to pass first tag check.
357+
orders.workerpool.tag =
358+
'0x0000000000000000000000000000000000000000000000000000000000000003'; // 0b0011
359+
// Match orders.
360+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
361+
'iExecV5-matchOrders-0x07',
362+
);
363+
});
364+
365+
it('[TEE] Should fail when apps are different', async () => {
366+
orders.requester.app = randomAddress;
367+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
368+
'iExecV5-matchOrders-0x10',
369+
);
370+
});
371+
372+
it('[TEE] Should fail when datasets are different', async () => {
373+
orders.requester.dataset = randomAddress;
374+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
375+
'iExecV5-matchOrders-0x11',
376+
);
377+
});
378+
379+
it('[TEE] Should fail when request order workerpool mismatches workerpool order workerpool (EOA, SC)', async () => {
380+
orders.requester.workerpool = randomAddress;
381+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
382+
'iExecV5-matchOrders-0x12',
383+
);
384+
orders.requester.workerpool = randomContract.address;
385+
await expect(iexecPocoAsRequester.matchOrders(...orders.toArray())).to.be.revertedWith(
386+
'iExecV5-matchOrders-0x12',
387+
);
388+
});
389+
390+
/**
391+
* Dynamically generated tests for all different restrictions in orders
392+
* (requesterrestrict, apprestrict, workerpoolrestrict, datasetrestrict).
393+
*/
394+
const revertMessages: { [key: string]: { [key: string]: string } } = {
395+
app: {
396+
dataset: 'iExecV5-matchOrders-0x13',
397+
workerpool: 'iExecV5-matchOrders-0x14',
398+
requester: 'iExecV5-matchOrders-0x15',
399+
},
400+
dataset: {
401+
app: 'iExecV5-matchOrders-0x16',
402+
workerpool: 'iExecV5-matchOrders-0x17',
403+
requester: 'iExecV5-matchOrders-0x18',
404+
},
405+
workerpool: {
406+
app: 'iExecV5-matchOrders-0x19',
407+
dataset: 'iExecV5-matchOrders-0x1a',
408+
requester: 'iExecV5-matchOrders-0x1b',
409+
},
410+
};
411+
['app', 'workerpool', 'dataset'].forEach((orderName) => {
412+
// No request order
413+
['requester', 'app', 'workerpool', 'dataset'].forEach((assetName) => {
414+
// Filter irrelevant cases. E.g. no need to change the app address in the app order.
415+
if (orderName.includes(assetName)) {
416+
return;
417+
}
418+
it(`[TEE] Should fail when ${orderName} order mismatch ${assetName} restriction (EOA, SC)`, async function () {
419+
const message = revertMessages[orderName][assetName];
420+
// EOA
421+
orders[orderName][assetName + 'restrict'] = randomAddress; // e.g. orders['app']['apprestrict'] = 0xEOA
422+
await expect(iexecPoco.matchOrders(...orders.toArray())).to.be.revertedWith(
423+
message,
424+
);
425+
// SC
426+
orders[orderName][assetName + 'restrict'] = randomContract.address; // e.g. orders['app']['apprestrict'] = 0xSC
427+
await expect(iexecPoco.matchOrders(...orders.toArray())).to.be.revertedWith(
428+
message,
429+
);
430+
});
431+
});
432+
});
433+
210434
it('[TODO] Should match orders with replication', () => {});
211435
});
212436
describe('[TODO] Sponsor match orders', () => {});
437+
438+
/**
439+
* Helper function to deposit requester and scheduler stakes with
440+
* default prices for tests that do not rely on price changes.
441+
*/
442+
async function depositForRequesterAndSchedulerWithDefaultPrices() {
443+
const dealPrice = (appPrice + datasetPrice + workerpoolPrice) * volume;
444+
const schedulerStake = await iexecWrapper.computeSchedulerDealStake(
445+
workerpoolPrice,
446+
volume,
447+
);
448+
// Deposit required amounts.
449+
await iexecWrapper.depositInIexecAccount(requester, dealPrice);
450+
await iexecWrapper.depositInIexecAccount(scheduler, schedulerStake);
451+
}
213452
});

0 commit comments

Comments
 (0)